CheckoutableTemplates/0000755000175000017500000000000011030137651015161 5ustar peterbepeterbeCheckoutableTemplates/Permission.py0000644000175000017500000000042511030137651017664 0ustar peterbepeterbe# CheckoutableTemplates Permission settings # # Peter Bengtsson, Fry-IT Ltd, 2003-2004 # License: ZPL # # Credits to Stefan H. Holek and his DocFinderEverywhere # __doc__="""See README.txt""" ViewCTPermission = 'View CheckoutableTemplates' ViewCTDefaultRoles = ('Manager',) CheckoutableTemplates/version.txt0000644000175000017500000000000711030137651017404 0ustar peterbepeterbe0.2.10 CheckoutableTemplates/showCheckoutableTemplates.dtml0000644000175000017500000003204111030137651023214 0ustar peterbepeterbe Show Checkoutable Templates
Exit

Checkoutable Templates

Difference between source and checked out template



Cancel, /manage_main">Edit
 

Error
Idenfifier expired. Please reload the page

Objects written back to file

None

Objects retracted


 

Error
Idenfifier expired. Please reload the page

Error
Template could not be checked out again. Please reload the page

Objects created!


 

Source of

Cancel, " >Check out this template

Cancel, " >Check out this template

 

Error
Identifier has expired. Please reload the page

Templates checked out

Filetype Object (Click to Edit) Owner  
"> ';return true;"> 40"> /manage_main" > [...]  /manage_main" > [No difference from source] [&identifier=">diff]
no one " />
No templates checked out

Templates not checked out

Filetype Object (Click to View Source)  
"> ';return true;"> 40"> " > [...]  " >
" /> " class="small">Check out  
There are no templates that can be checked out

How it works

Checkoutable Templates are Python product templates that have been created by the CTDTMLFile(DTMLFile) and
CTPageTemplateFile(PageTemplateFile) classes.

These templates belong to the Python products as templates run off the file system. Checkoutable Templates makes a
copy of these as Zope objects.
When it does so, it creates a Zope object (DTML Method or Page Template) based on the filesystem filename and
the default extension. So if your product uses a file called manage_edi.tdtml, then the object that is created
becomes manage_edit.dtml.

Checkoutable Templates can not know what the name of the method is that the filesystem based template used for.
For example, if your Python product defines something like this:

class MyProduct:
    ...
    viewStatistics = CTDTMLFile('dtml/statistics', globals())
                                 d='Setting a description is optional')
    ...
    updateStats = CTPageTemplateFile('zpt/updatestats', globals(),
                                     d='Description of this template')
    ...
    # Not necessary to set this attribute but
    # with it set, the showCheckoutableTemplates shows
    # only relevant files which is helpful if you
    # have several products that use CheckoutableTemplates.
    this_package_home = package_home(globals())
    ...

Then, that the method name becomes viewStatistics is impossible to know.

When these Checkoutable Templates are run, theres a quick check if the template exists as a Zope object based on
its name. If so, run that instead. If not, run the filesystem based template as normal.

CheckoutableTemplates/__init__.py0000644000175000017500000000336511030137651017301 0ustar peterbepeterbe## ## CheckoutableTemplates, ## By Peter Bengtsson, mail@peterbe.com, www.peterbe.com ## Copyright 2003-2005 ## License ZPL ## __doc__="""CheckoutableTemplates by Peter Bengtsson, Fry-IT Ltd, 2003-2005. CheckoutableTemplates allows you to make exceptions with DTML and PageTemplate attributes of a Python product. This is highly usable if you have an instance of a Python product class, and you want to change some little thing in one of its templates. """ __refresh_module__ = 0 # python import os # Zope from OFS.Application import Application from Globals import HTMLFile from AccessControl.Permission import registerPermissions from OFS.SimpleItem import Item from AccessControl.PermissionRole import PermissionRole # Product from Permission import ViewCTPermission, ViewCTDefaultRoles from CTFiles import CTDTMLFile, CTPageTemplateFile from Constants import * from findCTs import CheckoutableTemplatesBase # Delete the old config file when Zope starts #if os.path.isfile(CONFIGFILEPATH): # Delete it! #os.remove(CONFIGFILEPATH) def initialize(context): registerPermissions(((ViewCTPermission, (), ViewCTDefaultRoles),)) if DISABLE_CHECKOUTABLETEMPLATES: from zLOG import LOG, WARNING LOG('CheckoutableTemplates', WARNING, 'CheckoutableTemplates disabled') else: Application.CheckoutableTemplates = CheckoutableTemplatesBase() showCT = HTMLFile('showCheckoutableTemplates', globals()) ViewCTRoles = PermissionRole(ViewCTPermission, ViewCTDefaultRoles) Item.showCheckoutableTemplates = showCT Item.showCheckoutableTemplates__roles__ = ViewCTRoles if DEBUG: from zLOG import LOG, INFO LOG('CheckoutableTemplates', INFO, 'installed') CheckoutableTemplates/CTFiles.py0000644000175000017500000002566611030137651017043 0ustar peterbepeterbe## ## CheckoutableTemplates, ## By Peter Bengtsson, mail@peterbe.com, www.peterbe.com ## Copyright 2003-2005 ## License ZPL ## import os, cPickle, stat, sys, re from types import DictType, ListType, StringType from time import time import marshal from pprint import pprint from random import shuffle from Globals import DTMLFile from zLOG import LOG, INFO, WARNING from AccessControl import ClassSecurityInfo from Acquisition import aq_parent, aq_inner, aq_base from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Constants import * import logging logger = logging.getLogger('CheckoutableTemplates') if DISABLE_CHECKOUTABLETEMPLATES: logger.info("CheckoutableTemplates is disabled") try: from slimmer import slimmer, acceptableSyntax except ImportError: def acceptableSyntax(*a, **k): return None def slimmer(inputtext, *a, **k): return inputtext if DEBUG: m = "slimmer not installed. Whitespace optimization disbled." m += " (see www.issuetrackerproduct.com)" LOG("CheckoutableTemplates", WARNING, m) try: __test_dict={'a':'A'} __test_dict.pop('a') Python21 = 0 except: Python21 = 1 def dict_popper(dict, key): """ simulate what {}.pop() does in Python 2.3 """ if not dict: raise KeyError, 'dict_popper(): dictionary is empty' if not dict.has_key(key): raise KeyError, repr(key) new_dict = {} for k, v in dict.items(): if k == key: value = v else: new_dict[k] = v return value, new_dict def debug(debug_output): if DEBUG: print "CT|", if type(debug_output)==StringType: print debug_output else: pprint(debug_output) open('output.log','a').write(debug_output.strip()+'\n') _id_junk_regex = re.compile('[\\/\s\.:]') def _generateIdentifier(basepath, relpath, mtime): id = basepath + relpath id = _id_junk_regex.sub('', id) id = list(id[:3]+id[-3:]+str(mtime)[-5:]) shuffle(id) id = "".join(id) return id def _getAllconfigs(): raise "NotUsed" def _readAllConfigs(): if os.path.isfile(CONFIGFILEPATH): try: #items, finder = cPickle.load(open(CONFIGFILEPATH)) items, finder = cPickle.load(open(CONFIGFILEPATH,'rb')) #items, finder = marshal.load(open(CONFIGFILEPATH)) except ValueError, e: if str(e) == "unpack list of wrong size": # Wohw! the config file is corrupted which could very # much be because it's from an ancient version of # CheckoutableTemplates return [], {} try: assert type(items)==ListType, "items is not a list" assert type(finder)==DictType, "finder is not a dictionary" except AssertionError: return [], {} else: items, finder = [], {} # this is how it starts return items, finder #def _findConfig(key): # """ key can be an identifier or basepath,relpath,filetype """ # items, finder = _readAllConfigs() # if finder.has_key(key): # return items[finder.get(key)] # else: # return None def _writeAllConfigs(items, finder): combined = items, finder # save new file cPickle.dump(combined, open(CONFIGFILEPATH, 'wb'), -1) def _clean_itemlist(itemslist, basepath, relpath, filetype): """ return a _new list_ which doesn't have duplicate combinations of basepath, relpath and filetype and nothing of the params """ copy = [] newfinder = {} _template = "%s/%s/%s" skip_combo = _template%(basepath, relpath, filetype) for each in itemslist: combo = _template%(each['basepath'], each['relpath'], each['filetype']) if not newfinder.has_key(combo): if combo != skip_combo: copy.append(each) newfinder[combo] = copy.index(each) else: debug("Duplicate combo: %s"%combo) return copy, newfinder def _write2config(basepath, relpath, description, mtime, filetype='DTML'): """ save an item serialized file """ items, finder = _readAllConfigs() key = "%s/%s/%s" % (basepath, relpath, filetype) addthis = 1 item_no = finder.get(key) if item_no is None: # ah! Nothing in the finder addthis = 1 else: try: item = items[item_no] # we have it already addthis = 0 except IndexError: # Ghaa! we're out-of-sync with the finder addthis = 1 # force a new addition # test if we can change our mind based on the mtime if not addthis and mtime > item['mtime']: # has changed # remove it items.pop(item_no) if Python21: _value, finder = dict_popper(finder, key) else: finder.pop(key) addthis = 1 if addthis: if CLEAN_CHECK: # read Constants.py items, finder = _clean_itemlist(items, basepath, relpath, filetype) identifier = _generateIdentifier(basepath, relpath, mtime) d = {'description':description, 'filetype':filetype, 'relpath':relpath, 'basepath':basepath, 'mtime':mtime, 'identifier':identifier } items.append(d) item_no = items.index(d) finder[key] = item_no finder[identifier] = item_no debug("Write new identifier %s"%identifier) _writeAllConfigs(items, finder) return 1 else: return 0 def write2config(basepath, relpath, description, mtime, filetype='DTML'): """ save a config item """ return _write2config(basepath, relpath, description, mtime, filetype=filetype) class _CTDTMLFile(DTMLFile): def __init__(self, name, _prefix=None, **kw): " doc string " description = kw.get('description',kw.get('d','')) # 'optimize' argument optimize = kw.get('optimize', kw.get('opt', None)) self.optimize = acceptableSyntax(optimize) if not kw.has_key('uncheckoutable'): sep = name[max(name.find('\\'), name.find('/'))] self.ctnamesplit = name.split(sep) prodpath = _prefix['__name__'].split('.')[:-1] prodpath = ".".join(prodpath).replace('.',os.sep) basepath = os.path.join(CT_INSTANCE_HOME, prodpath) if not os.path.isdir(basepath): basepath = os.path.join(CT_SOFTWARE_HOME, prodpath) # first, open file to see how old it is. name = name.replace('/', os.sep).replace('\\',os.sep) t = os.stat(os.path.join(basepath, name+ '.dtml')) mtime = t[stat.ST_MTIME] # if already there there's no point to # add it again added = write2config(basepath, name, description, mtime, 'DTML') if DEBUG and added: LOG(self.__class__.__name__, INFO, name) apply(DTMLFile.__init__, (self, name, _prefix), kw) def _exec(self, bound_data, args, kw): """ Execute the template but first look for one instanciated in the ZODB based on the some filename conversion. """ ctnamesplit = bound_data['self'].ctnamesplit if len(self.ctnamesplit) == 1: possibletemplate = ctnamesplit[0] + '.dtml' else: possibletemplate = '.'.join(ctnamesplit[1:]) + '.dtml' base = bound_data['context'] if hasattr(base, possibletemplate): request = bound_data['context'].REQUEST params = (bound_data['context'], request) result = apply(getattr(base, possibletemplate), params, kw) else: result = apply(DTMLFile._exec, (self, bound_data, args, kw)) if self.optimize and OPTIMIZE: result = slimmer(result, self.optimize) return result class _CTPageTemplateFile(PageTemplateFile): """ CTPageTemplateFile subclasses PageTemplateFile and intercepts the __init__ and the _exec methods """ def __init__(self, name, _prefix=None, **kw): """ doc string """ description = kw.get('description',kw.get('d','')) # 'optimize' argument optimize = kw.get('optimize', kw.get('opt', None)) self.optimize = acceptableSyntax(optimize) if not kw.has_key('uncheckoutable'): sep = name[max(name.find('\\'), name.find('/'))] self.ctnamesplit = name.split(sep) prodpath = _prefix['__name__'].split('.')[:-1] prodpath = ".".join(prodpath).replace('.',os.sep) basepath = os.path.join(CT_INSTANCE_HOME, prodpath) if not os.path.isdir(basepath): basepath = os.path.join(CT_SOFTWARE_HOME, prodpath) # first, open file to see how long it is. name = name.replace('/', os.sep).replace('\\',os.sep) template_path = os.path.join(basepath, name) if not os.path.splitext(template_path)[1]: template_path += '.zpt' t = os.stat(template_path) mtime = t[stat.ST_MTIME] added = write2config(basepath, name, description, mtime, 'ZPT') if DEBUG and added: LOG(self.__class__.__name__, INFO, name) apply(PageTemplateFile.__init__, (self, name, _prefix), kw) def _exec(self, bound_data, args, kw): """ doc string """ if len(self.ctnamesplit) == 1: possibletemplate = self.ctnamesplit[0] else: possibletemplate = '.'.join(self.ctnamesplit[1:]) if not possibletemplate.endswith('.zpt'): possibletemplate += '.zpt' base = self if hasattr(base, possibletemplate): request = self.REQUEST result = apply(getattr(base, possibletemplate), (self, request), kw) else: result = apply(PageTemplateFile._exec, (self, bound_data, args, kw)) if self.optimize and OPTIMIZE: try: result = slimmer(result, self.optimize) except: try: err_log = self.error_log err_log.raising(sys.exc_info()) except: pass return result if DISABLE_CHECKOUTABLETEMPLATES: CTPageTemplateFile = PageTemplateFile CTDTMLFile = DTMLFile else: CTPageTemplateFile = _CTPageTemplateFile CTDTMLFile = _CTDTMLFileCheckoutableTemplates/Constants.py0000644000175000017500000000360111030137651017507 0ustar peterbepeterbe# Suppose you have done: # $ ln -s zope261b1 Zope-2.6.2b2-linux2-x86 # or some similar setup, then CLIENT_HOME becomes the latter but # globals() will have the former. # This hack makes sure that CT_HOME becomes like # globals() # # To set this variable, edit you ./start script to have the following: # # CT_HOME=/home/zope/zope123/var # export CT_HOME # # The same goes for the CT_SOFTWARE_HOME and you set it like this: # # CT_SOFTWARE_HOME=/home/zope/zope123/lib/python/Products # export CT_SOFTWARE_HOME # import os CT_HOME = os.environ.get('CT_HOME', CLIENT_HOME) CT_SOFTWARE_HOME = os.environ.get('CT_SOFTWARE_HOME', SOFTWARE_HOME) CT_INSTANCE_HOME = os.environ.get('CT_INSTANCE_HOME', INSTANCE_HOME) def _getVariable(key, default): value = os.environ.get(key, default) try: value = not not int(value) except: if str(value).lower().strip() in ['yes','on']: value = 1 elif str(value).lower().strip() in ['no','off']: value = 0 else: value = default return value DISABLE_CHECKOUTABLETEMPLATES = _getVariable('DISABLE_CHECKOUTABLETEMPLATES', 0) # Allows to write back to file from showCheckoutableTemplate CAN_WRITEBACK = _getVariable('CT_CAN_WRITEBACK', 0) # Writes to Log() every time we store a template DEBUG = _getVariable('CT_DEBUG',0) # Attempts to optimize the output OPTIMIZE = _getVariable('CT_OPTIMIZE',1) CONFIGFILEPATH = os.path.join(CT_HOME, "CTConfig") CONFIGFILEPATH = CONFIGFILEPATH + '.dump' # It's good to have CLEAN_CHECK on if you have upgraded from # an old CheckoutableTemplates and you might have old information # in your pickled file in 'var/CTConfig.dump' # If you switch CLEAN_CHECK off you might gain some speed but # this should only be done once you know you've at least once # deleted the CTConfig.dump file sometime before Zope started. CLEAN_CHECK = _getVariable('CT_CLEAN_CHECK', 1)CheckoutableTemplates/README.txt0000644000175000017500000001611511030137651016663 0ustar peterbepeterbeCheckoutableTemplates by Peter Bengtsson, Fry-IT Ltd, 2003-2004. This software is released under the ZPL license. Credits to Dieter Maurer and his code that inspired me to enable the 'showCheckoutableTemplates' page. Credits to the MoinMoin project (http://sourceforge.net/projects/moin/) for the inspiration and code used in diff.py CheckoutableTemplates allows you to make exceptions to DTMLFile and PageTemplateFile attributes of a Python product. This is highly usable if you have an instance of a Python product class, and you want to change some little thing in one of its templates. In Zope, it is NOT possible to subclass an template attribute from withing the ZMI. Having templates as attributes in a Python product class is useful because when you roll out a new version, it's easy to include your changes. It's also useful in that you can make the Zope object instance very simple. The advantage about only having templates instanciated inside the class instance in Zope is that you can make changes to the look and feel of one and only one instance. Suppose your Python product defines a template like this:: class MyProduct(ObjectManager): meta_type = 'My Meta Type' def __init__(self, id, title=''): ... show_page = DTMLFile('dtml/show_page', globals()) edit_page = PageTemplateFile('zpt/edit_page', globals()) Then you can use these templates by visiting http://localhost:8080/myinstance/show_page Suppose you're **not** the author of the Python product but have found a spelling misstake or an invalid HTML tag in show_page.dtml, or suppose you have two instance of the product; one called 'myinstance' and one called 'herinstance'. For the 'herinstance' instance you want the show_page to look slightly different. How do you fix that without editing the sourcecode of the Python product? You simply can't! Suppose instead that you have CheckoutableTemplates installed and define your Python product like this:: from Products.CheckoutableTemplates \ import CTPageTemplateFile, CTDTMLFile class MyProduct(ObjectManager): meta_type = 'My Meta Type' def __init__(self, id, title=''): ... show_page = CTDTMLFile('dtml/show_page', globals()) edit_page = CTPageTemplateFile('zpt/edit_page', globals()) Then, visit http://localhost:8080/myinstance/showCheckoutableTemplates and you'll see a list of templates that you can make exceptions on. When you use that interface to check out a template, an object is created called show_page.dtml and/or edit_page.zpt. Their Id "protected", so you can't call it whatever you want. The only thing that links these template objects with your python product is the Id. If you have SilverCity (http://silvercity.sourceforge.net/) installed code syntax will be shown more nicely. It is not a necessity. Again, you visit http://localhost:8080/myinstance/show_page, CheckoutableTemplates makes a check if there is an object called 'show_page.dtml'. If so, use that instead; if not, use the file system template as normal. If you have installed several Python products that use CheckoutableTemplates, then the showCheckoutableTemplates page will list the templates of all Python products. To help you with this, the Python product can define an attributes called 'this_package_home' like this:: from Globals import package_home from Products.CheckoutableTemplates \ import CTPageTemplateFile, CTDTMLFile class MyProduct(ObjectManager): meta_type = 'My Meta Type' def __init__(self, id, title=''): ... show_page = CTDTMLFile('dtml/show_page', globals()) edit_page = CTPageTemplateFile('zpt/edit_page', globals()) this_package_home = package_home(globals()) Generally you don't have to worry about this option. Refreshing a Python product is something you don't have to be worried about. When you refresh the Python product CheckoutableTemplates rechecks all template attributes but only changes those which have changed. Just because you have defined your template attributes with CheckoutableTemplates doesn't mean that you have to use it. It's just extremely useful IF you will need it at a later point. It also makes it possible to edit your templates via the web if you for some reason can't do it on the command line in emacs. When you start Zope, CheckoutableTemplates keeps a record of all templates your Python product uses. It stores this in '/var/CTConfig.dump' which is a pickle dump. If you have your zoperoot linked as a symbolic link, you'll have to set this as an environment variable in your start script:: CT_HOME=/home/peterbe/zope261b2/var CT_SOFTWARE_HOME=/home/peterbe/zope261b2/lib/python export CT_HOME export CT_SOFTWARE_HOME Set this appropriatly to how you have your Zope set up. I guess that if you do, you know enough about Zope and sys admin to be able to see what needs to be done. The best way to test your settings is that you use the showCheckoutableTemplates page. It is not recommended for live environments, but useful when debugging your python product, you can enable 'CT_CAN_WRITEBACK' which allows you to save a Zope object template back onto the file system. Set this in your start script like this:: export CT_CAN_WRITEBACK=1 To do this on Windows, in start.bat you write:: SET CT_CAN_WRITEBACK=1 This product was developed to be used in a live environment, but comes with absolutely no warrenty. I advice that you familiarize yourself with CheckoutableTemplates before you decide to enroll it in your best Python products. All bug reports and suggestions are welcome to peter@fry-it.com. The CTPageTemplateFile and CTDTMLFile also accepts a parameter called 'optimize' which can (at the time of writing this) accept values like 'CSS', 'HTML' or 'XHTML'. If any of these are set, the output CSS/HTML/XHTML will be optimized but stripping out everything excessive such as excess whitespace and comments. The 'XHTML' optimizer is less rough than the 'HTML' optimizer which strips unnecessary quotes. NOTE! This only works if you have the slimmer.py module installed. As an example, some CSS can be changed from:: /* Header 1 */ h1 { font-family: Verdana, Arial; color: #123456; } To this:: h1{font-family:Verdana,Arial;color:#123456;} Of course it depends on how your HTML output looks like but preliminary calculations have shown that you get on average a 10-20% time gain average. I.e. the time to download the document plus the time to optimize the output. Note: Always test your pages after you have switched on the optimization. Note2: Don't optimize twice. Use sparingly.:: from Products.CheckoutableTemplates \ import CTPageTemplateFile, CTDTMLFile class MyProduct(ObjectManager): meta_type = 'My Meta Type' def __init__(self, id, title=''): ... show_page = CTDTMLFile('dtml/show_page', globals(), optimize='XHTML') edit_page = CTPageTemplateFile('zpt/edit_page', globals(), optimize='HTML') styles_css = CTDTMLFile('dtml/show_page', globals(), optimize='CSS') CheckoutableTemplates/CHANGES.txt0000644000175000017500000000755011030137651017001 0ustar peterbepeterbe0.2.10 - Fixed a bug which made it impossible to include templates that already had an extension like 'screen.css.dtml' 0.2.9 - Templates specified without an extension are assumed to be .zpt in the end for ZPT. 0.2.8 - Important bugfix, keyword arguments passed to checked out templates did not reach the template code. 0.2.7 - showCheckoutableTemplates is not XHTML Transitional compliant. - No "diff" link on checked out templates that aren't different. - Excessive LOG() messages removed (unless in CT_DEBUG mode) - Corrected sourcecode to use 4 spaces for tabs ONLY. 0.2.6 - Exit link in showCheckoutableTemplates. - Made it Python2.1 compatible again after a Python2.>1 feature was introduced. See CTFiles.py/dict_popper() - Differ feature greatly improved thanks to MoinMoin project. 0.2.5 - _write2config() now prevents duplicates in the pickle file useful to have when you upgrade to this latest version. - Draft of differ for checked out templates in showCheckoutableTemplates 0.2.4 - marshal module used in CTFiles.py caused a memory leak in one of my Gentoo Linux servers so to be on the safe side I resided back to using cPickle. 0.2.3 - write2config() greatly optimized. (see http://www.peterbe.com/plog/python-optimization-anecdote) - PUT_REQUEST_NOTE constant for 'DebugFilterCTFilesPH' in REQUEST - _generateIdentifier() optimized to generate more unique identifiers faster. 0.2.2 - slimmer.py thrown out. It was too different and slimmer.py is now going to become a standalone Open Source package. 0.2.1 - Executing CTDTML instances did not use Zope acquisition. CTPageTemplates did. (Thanks Ria Marinussen) 0.2.0 - Fixed bug the way CheckoutableTemplates finds the Products directory. Now no extra configuration settings necessary for Zope2.7.x on win32. - Fixed a bug recently introduced in html_slimmer() that was related to unquoted attributes. - Improved (x)html_slimmer() to not strip excess whitespace in tags like this: - Major speed improvement in (x)html_slimmer(). Sometimes three times faster. 0.1.9 - Fixed bug in (x)html_slimmer() that tried to slimmer inline JavaScript. - Rewrote some of the tests. - Fixed bug in css_slimmer() with line breaks and selectors starting with '#'. - Improved css_slimmer() to replace '#FFCCFF' to '#FCF' - Fixed bug in css_slimmer() when using IE hacks. - Tweak slimmer.py css_slimmer() to replace ': 10px' to ':10px'. 0.1.8 - Prepared for public release to the Zope community by deleting unnecessary files, silly comments and improved README.txt 0.1.7 - Fixed a bug that added duplicates when a product using CT refreshes a template. 0.1.6 - Added some basic unittesting cases for slimmer module. 0.1.5 - Added basic support for HTML and CSS optimization. To use, add a keyword argument to the constructor or CTPageTemplateFile and CTDTMLFile like this optimize='CSS' or optimize='HTML'. 0.1.4 - Fixed security bug that allowed Anonymous access to showCheckoutableTemplates - Improved identifier checks in showCheckoutableTemplates - Beautified showCheckoutableTemplates. If SilverCity is importable, showing source is done with nice XML stylesheets. 0.1.3 - Changed use of os.stat to be backcompatible with python 2.1 0.1.2 - Removed all XML storage facilities. Nice to look at but too slow - Changed the "checker" from length-of-document to modification time os.stat() 0.1.1 First release to Zope.org 0.1.0 First release zipped up. CheckoutableTemplates/diff.py0000644000175000017500000001161011030137651016442 0ustar peterbepeterbe# -*- coding: iso-8859-1 -*- """ MoinMoin - Side by side diffs @copyright: 2005 by Peter Bengtsson @copyright: 2002 by Jürgen Hermann @copyright: 2002 by Scott Moonen @license: GNU GPL, see COPYING for details. """ import difflib #from MoinMoin.wikiutil import escape def escape(s, quote=0): """ Escape possible html tags Replace special characters '&', '<' and '>' by SGML entities. (taken from cgi.escape so we don't have to include that, even if we don't use cgi at all) FIXME: should return string or unicode? @param s: (unicode) string to escape @param quote: bool, should transform '\"' to '"' @rtype: (unicode) string @return: escaped version of s """ if not isinstance(s, (str, unicode)): s = str(s) # Must first replace & s = s.replace("&", "&") # Then other... s = s.replace("<", "<") s = s.replace(">", ">") if quote: s = s.replace('"', """) return s def indent(line): eol = '' while line and line[0] == '\n': eol += '\n' line = line[1:] stripped = line.lstrip() if len(line) - len(stripped): line = " " * (len(line) - len(stripped)) + stripped #return "%d / %d / %s" % (len(line), len(stripped), line) return eol + line # This code originally by Scott Moonen, used with permission. # The copied from MoinMoin 1.3.3 and modified by Peter Bengtsson def diff(old, new): """ Find changes between old and new and return HTML markup visualising them. """ #_ = request.getText #t_line = _("Line") + " " t_line = "Line " seq1 = old.splitlines() seq2 = new.splitlines() seqobj = difflib.SequenceMatcher(None, seq1, seq2) linematch = seqobj.get_matching_blocks() if len(seq1) == len(seq2) and linematch[0] == (0, 0, len(seq1)): # No differences. return "No differences found!" lastmatch = (0, 0) result = """ """ % ('Deletions are marked like this.', 'Additions are marked like this.') # Print all differences for match in linematch: # Starts of pages identical? if lastmatch == match[0:2]: lastmatch = (match[0] + match[2], match[1] + match[2]) continue result += """ """ % ( t_line, str(lastmatch[0] + 1), t_line, str(lastmatch[1] + 1),) leftpane = '' rightpane = '' linecount = max(match[0] - lastmatch[0], match[1] - lastmatch[1]) for line in range(linecount): if line < match[0] - lastmatch[0]: if line > 0: leftpane += '\n' leftpane += seq1[lastmatch[0] + line] if line < match[1] - lastmatch[1]: if line > 0: rightpane += '\n' rightpane += seq2[lastmatch[1] + line] charobj = difflib.SequenceMatcher(None, leftpane, rightpane) charmatch = charobj.get_matching_blocks() if charobj.ratio() < 0.5: # Insufficient similarity. if leftpane: leftresult = """%s""" % indent(escape(leftpane)) else: leftresult = '' if rightpane: rightresult = """%s""" % indent(escape(rightpane)) else: rightresult = '' else: # Some similarities; markup changes. charlast = (0, 0) leftresult = '' rightresult = '' for thismatch in charmatch: if thismatch[0] - charlast[0] != 0: leftresult += """%s""" % indent( escape(leftpane[charlast[0]:thismatch[0]])) if thismatch[1] - charlast[1] != 0: rightresult += """%s""" % indent( escape(rightpane[charlast[1]:thismatch[1]])) leftresult += escape(leftpane[thismatch[0]:thismatch[0] + thismatch[2]]) rightresult += escape(rightpane[thismatch[1]:thismatch[1] + thismatch[2]]) charlast = (thismatch[0] + thismatch[2], thismatch[1] + thismatch[2]) leftpane = '
\n'.join(map(indent, leftresult.splitlines())) rightpane = '
\n'.join(map(indent, rightresult.splitlines())) # removed width="50%%" result += """ """ % (leftpane, rightpane) lastmatch = (match[0] + match[2], match[1] + match[2]) result += '
Checked out template (modified) Original template (source)
%s %s
%s %s: %s %s:
%s %s
\n' return result CheckoutableTemplates/findCTs.py0000644000175000017500000003442311030137651017073 0ustar peterbepeterbe## ## CheckoutableTemplates, ## By Peter Bengtsson, mail@peterbe.com, www.peterbe.com ## Copyright 2003-2008 ## License ZPL ## __doc__="""extract which templates can be checked out""" import os, StringIO, re from Products.PythonScripts.standard import html_quote, newline_to_br from ExtensionClass import Base from AccessControl.SecurityInfo import ClassSecurityInfo # Attempt to import fancy SilverCity formatting try: from SilverCity import XML as SC_XML try: import SilverCity _SC_stylesheet_location = SilverCity.get_default_stylesheet_location() SC_stylesheet = open(_SC_stylesheet_location).read() except: SC_XML = None except ImportError: SC_XML = None from Constants import * import CTFiles from diff import diff #----------------------------------------------------------------------------- def sgmlDiff(a, b): out = [] isjunk = lambda x: not not re.search("\s", x) d = difflib.SequenceMatcher(isjunk, a, b) for e in d.get_opcodes(): if e[0] == "replace": out.append(''+''.join(a[e[1]:e[2]]) + ''+''.join(b[e[3]:e[4]])+"") elif e[0] == "delete": out.append(''+ ''.join(a[e[1]:e[2]]) + "") elif e[0] == "insert": out.append(''+''.join(b[e[3]:e[4]]) + "") elif e[0] == "equal": out.append(''.join(b[e[3]:e[4]])) else: raise "OpcodesError", "Unrecognized %r"%e[0] return "".join(out) #----------------------------------------------------------------------------- class CheckoutableTemplatesBase(Base): """ the purpose of this class is to use the XML config file to extract which templates that can be checked out in this instance. """ _secInfo= ClassSecurityInfo() _secInfo.declarePublic('__getitem__','__len__','tpValues', 'tpId', 'getCTFiles') _secInfo.declarePrivate('View management screens','doCheckout', 'doWriteback','canWriteback', 'doRetract' 'showCheckoutableTemplates', 'hasCheckedout', 'hasFileitemFromIdentifier', 'getFileitemFromIdentifier', ) def __init__(self): """ no doc string """ pass def getCTFiles(self, zope, filter=None): """ returns which templates are checkoutable. The 'filter' parameter can be used to: - None: no filtering - 'Deployed': those that have been deployed - 'Deployed here': like 'Deployed' but only 'here'. - 'Undeployed': those not yet deployed. - 'Undeployed here': like 'Undeployed' but only 'here'. """ fileitems = [] # read config file fileitems, finder = CTFiles._readAllConfigs() fileitems = self._appendMoreInfo2Items(fileitems) if zope is None: return fileitems if filter is not None: filter = filter.lower().replace(' ','').strip() checked = [] if filter == 'deployed': # only those where objectid exists as zope object for fileitem in fileitems: if hasattr(zope, fileitem['objectid']): checked.append(fileitem) elif filter == 'deployedhere': # only those where objectid exists here without # acquisition. base = getattr(zope, 'aq_base', zope) for fileitem in fileitems: if hasattr(base, fileitem['objectid']): checked.append(fileitem) elif filter == 'undeployed': # only those where objectid does not exists # as zope object. for fileitem in fileitems: if not hasattr(zope, fileitem['objectid']): checked.append(fileitem) elif filter == 'undeployedhere': # only those where objectid does not exists # here without acquisition. base = getattr(zope, 'aq_base', zope) for fileitem in fileitems: if not hasattr(base, fileitem['objectid']): checked.append(fileitem) else: checked = fileitems base = getattr(zope, 'aq_base', zope) # Inspect if 'base' has a this_package_home attribute, # and if so, filter 'checked' based on that. if hasattr(base, 'this_package_home'): req_basepath = base.this_package_home # Make note that we do this: zope.REQUEST.set('DebugFilterCTFilesPH', req_basepath) doublechecked = [] for item in checked: if item['basepath'].find(req_basepath) > -1: doublechecked.append(item) checked = doublechecked return checked def _appendMoreInfo2Items(self, fileitems): " using the data we have create few more interesting things " newfileitems = [] for fileitem in fileitems: newd = fileitem ikey = 'filetypeicon' if fileitem['filetype'].lower()=='dtml': newd[ikey] = '''DTML Method''' extension = '.dtml' elif fileitem['filetype'].lower()=='zpt': newd[ikey] = '''Page Template''' extension = '.zpt' else: # Unrecognized!! continue basepath = fileitem['basepath'] basepath = basepath.replace('\\',os.sep).replace('/',os.sep) relpath = fileitem['relpath'] relpath = relpath.replace('\\',os.sep).replace('/',os.sep) if not os.path.splitext(relpath)[1]: relpath = relpath + extension fullpath = os.path.join(basepath, relpath) newd['fullpath'] = fullpath relpath = fileitem['relpath'] sep = relpath[max(relpath.rfind('\\'), relpath.rfind('/'))] objectidlist = [str(x) for x in relpath.split(sep)] if len(objectidlist) == 1: objectid = objectidlist[0] else: objectid = '.'.join(objectidlist[1:]) if os.path.splitext(objectid)[1] not in ('.dtml','.zpt'): objectid += extension newd['objectid'] = objectid # keep it newfileitems.append(newd) return newfileitems def hasCheckedout(self, zope, identifiers): """ return true if any identifier is already checked out """ if type(identifiers)==type('s'): identifiers = [identifiers] base = getattr(zope, 'aq_base', zope) for identifier in identifiers: fileitem = self.getFileitemFromIdentifier(identifier) if hasattr(base, fileitem['objectid']): return 1 return 0 def doCheckout(self, zope, identifiers): """ create template objects """ objects_created=[] if type(identifiers)==type('s'): identifiers = [identifiers] for identifier in identifiers: fileitem = self.getFileitemFromIdentifier(identifier) id = fileitem['objectid'] fr = open(fileitem['fullpath'], 'r') code = fr.read() fr.close() title = '' if fileitem['filetype'].lower()=='dtml': obj=self._createDTMLMethod(zope, id, code, title) objects_created.append(obj) elif fileitem['filetype'].lower()=='zpt': obj=self._createPageTemplate(zope, id, code, title) objects_created.append(obj) else: raise "UnrecognizedFiletype", fileitem['filetype'] return objects_created def _createDTMLMethod(self, zope, id, code, title=''): """ create a DTML object in zope """ with = zope.manage_addProduct['OFSP'] with.addDTMLMethod(id, title) dtmlmethod = getattr(zope, id) dtmlmethod.manage_edit(code, title) return dtmlmethod def _createPageTemplate(self, zope, id, code, title=''): """ create a PageTemplate object in zope """ with = zope.manage_addProduct['PageTemplates'] with.manage_addPageTemplate(id, title, code) pagetemplate = getattr(zope, id) return pagetemplate def doRetract(self, zope, identifiers): """ delete some zope objects """ if type(identifiers)==type('s'): identifiers = [identifiers] objectids= [] for fileitem in self.getCTFiles(zope, 'deployed'): if fileitem['identifier'] in identifiers: objectids.append(fileitem['objectid']) objectids_copy = objectids[:] zope.manage_delObjects(objectids) return objectids_copy def getSourcecode(self, identifier): " read file and return source code " fullpath = self.getFullpathFromIdentifier(identifier) if fullpath is None: return "NONE FOUND!!" fr = open(fullpath, 'r') code = fr.read() fr.close() return code def showSourcecode(self, identifier): """ return source code htmlified """ code = self.getSourcecode(identifier) return self._niceSourceFormat(code) def _niceSourceFormat(self, code): if SC_XML is not None: generator = SC_XML.XMLHTMLGenerator() file = StringIO.StringIO() generator.generate_html(file, code) code = file.getvalue() file.close() del generator css = '\n\n'%SC_stylesheet code = '
%s
'%code return css + code else: code = html_quote(code) code = newline_to_br(code) code = code.replace('\t',' '*4) return "%s"%code def showDifference(self, zope, identifier, objectid): """ return a nice formatted difference string """ code_source = self.getSourcecode(identifier) object = getattr(zope, objectid) assert hasattr(object, 'absolute_url'), "%r not a ZODB object" % object code_object = object.document_src() #difference = diff(html_quote(code_source), html_quote(code_object)) difference = diff(code_object, code_source) return difference # this is a HTML def _niceDifference(self, difference): """ return a nice explaination of the difference """ #difference = newline_to_br(difference) lines = difference.splitlines(1) lineitemer = lambda x, c: "%s  %s"%(c, x) newlines = [] for i in range(len(lines)): newlines.append(lines[i]) difference = "
".join(newlines) difference = difference.replace("\t", " "*4) return '
%s
'%difference def hasFileitemFromIdentifier(self, identifiers): """ search to see if this identifier exists """ if type(identifiers)==type('s'): identifiers = [identifiers] for fileitem in self.getCTFiles(None): if fileitem['identifier'] in identifiers: return 1 return 0 def getFileitemFromIdentifier(self, identifier): """ search through all files and match identifier, then return fileitem (dict) of the found one. """ for fileitem in self.getCTFiles(None): if fileitem['identifier'] == identifier: return fileitem else: return None def getFullpathFromIdentifier(self, identifier): """ get the full path from an identifier """ found = self.getFileitemFromIdentifier(identifier) if found is not None: fullpath = found['fullpath'] if found['filetype'] == 'DTML' and os.path.splitext(fullpath)[1] != '.dtml': fullpath += '.dtml' elif found['filetype'] == 'ZPT' and os.path.splitext(fullpath)[1] not in ('.zpt','.pt'): fullpath += '.zpt' return fullpath else: return None def doWriteback(self, zope, identifiers, makebackupcopy=1): """ From the identifier, find the equivalent Zope object and use it's document_src to write to file """ objects =[] # list of all object we manage to write back for identifier in identifiers: fileitem = self.getFileitemFromIdentifier(identifier) if fileitem is None: raise "InvalidIdentifier", "No file in config found with"\ "this identifier %s"%identifier else: fullpath = fileitem['fullpath'] objectid = fileitem['objectid'] # get it as Zope object object = getattr(zope, objectid) document_src = object.document_src() if makebackupcopy: incr = 1 while os.path.isfile(fullpath + '.bak%s'%incr): incr += 1 fullpath_backup = fullpath + '.bak%s'%incr fbr = open(fullpath, 'r') fbw = open(fullpath_backup, 'w') fbw.write(fbr.read()) fbw.close() fbr.close() # nice and simple write fw = open(fullpath, 'w') fw.write(document_src) fw.close() # remember that we wrote this back objects.append([object, identifier]) return objects def canWriteback(self): """ true if CAN_WRITEBACK """ return CAN_WRITEBACK CheckoutableTemplatesBase._secInfo.apply(CheckoutableTemplatesBase) CheckoutableTemplates/styles.css0000644000175000017500000004267011030137651017227 0ustar peterbepeterbe div.callink { font-family:verdana, arial, helvetica; text-align:left; color:#3366B4; font-size:14px; font-style:normal; font-weight:bold; text-decoration:none; line-height:16px; } .bluetitle{ font-family:verdana, arial, helvetica; text-align:left; color:#3366B4; font-size:14px; font-style:normal; font-weight:bold; text-decoration:none; line-height:16px; } .calendardropdown{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a:link{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:underline; } .link{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:underline; } a:visited { font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:underline; } a:hover{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#C31111; text-decoration:underline; } a:active{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:underline; } body{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; } p{ font-family:verdana, arial, helvetica; text-align:justify; font-size:11px; font-style:normal; font-weight:normal; color:#000000; line-height:14px; } b{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#000000; line-height:14px; } .bblue{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; line-height:14px; } .bred{ font-family:verdana, arial, helvetica; font-size:12px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; line-height:14px; } s{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; line-height:14px; } .sblue{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#3366B4; line-height:11px; } td{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; line-height:14px; } ul{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; line-height:14px; } li{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; line-height:14px; } .title{ font-family:verdana, arial, helvetica; text-align:left; color:#444444; font-size:12px; font-style:normal; font-weight:bold; text-decoration:none; line-height:16px; } i{ font-style:italic; } em{ font-style:italic; } .just{ text-align:justify; } .center{ text-align:center; } .left{ text-align:left; } .right{ text-align:right; } .progress{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#000000; text-decoration:none; line-height:13px; } .progress_selected{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#B5B5B5; text-decoration:none; line-height:13px; } h1{ font-family:Arial Black, verdana, arial, helvetica; color:#8a8a8a; font-size:13pt; font-style:normal; font-weight:bold; text-decoration:none; line-height:19px; } h2{ font-family:Arial Black, verdana, arial, helvetica; color:#8a8a8a; font-size:10pt; font-style:normal; font-weight:bold; text-decoration:none; line-height:19px; } .form{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .calendardropdown{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } .formbold{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#000000; text-decoration:none; } .dark{ background-color:#dddddd; font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .light{ background-color:#f2f2f2; font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .tableheader{ font-family:verdana, arial, helvetica; font-size:12px; font-style:normal; font-weight:bold; color:#ffffff; text-decoration:none; background-color:#3366B4; } LI.img { list-style-image : url(http://www.quietdays.com/images/orange_bullet.gif); font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; line-height:17px } LI.img_treatments { list-style-image : url(http://www.quietdays.com/images/ailments_bullet.gif); font-family:verdana, arial, helvetica; font-size:12px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .CellLightOff { background-color: #FFFFFF; } .CellLightOn { background-color: #FFF5E8; } .siteframe{ background-color:#9DB8DD; } .maintbl{ background-color:#FFFFFF; } .lefttbl{ background: url(http://www.quietdays.com/images/left-bg.gif) #6898D0; } .blue{ background-color:#6898D0; } .lightblue{ background-color:#9DB8DD; } .stats{ font-family:verdana, arial, helvetica; background-color:#FFFFFF; vertical-align:bottom; font-size:9px; font-style:normal; font-weight:normal; color:#7F7F7F; text-decoration:none; } .systemmsg { font-family:Verdana, Arial, Helvetica; background-color:#FFFFFF; vertical-align:bottom; font-size:10px; font-style:normal; font-weight:bold; color:#2A63A6; text-decoration:none; } .bluebutton { font-family: Arial, Helvetica, sans-serif; color: #FFFFFF; background-color: #336699; text-transform: uppercase; font-size: 9px; font-weight: bold; border-top-color: #CCCCCC; border-right-color: #000033; border-bottom-color: #000033; border-left-color: #CCCCCC; } .searchnotused{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .searchbig{ font-family:verdana, arial, helvetica; line-height:20px; font-size:14px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .searcht{ font-family:verdana, arial, helvetica; font-size:13px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .searchb{ font-family:verdana, arial, helvetica; line-height:20px; font-size:11px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .searcherror{ font-family:verdana, arial, helvetica; line-height:20px; font-size:10px; font-style:normal; font-weight:bold; color:#D90000; text-decoration:none; } .search{ font-family:verdana, arial, helvetica; line-height:20px; font-size:10px; font-style:normal; font-weight:normal; color:#FFFFFF; text-decoration:none; } .searchform{ font-family:verdana, arial, helvetica; width:102px; font-size:10px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .searchform_unfixed { font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .searchform2{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .loginnotused{ font-family:verdana, arial, helvetica; background-color:#336AB4; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .logint{ font-family:verdana, arial, helvetica; background-color:#336AB4; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .loginb{ font-family:verdana, arial, helvetica; line-height:20px; font-size:10px; font-style:normal; font-weight:bold; color:#000000; text-decoration:none; } .login{ font-family:verdana, arial, helvetica; line-height:20px; font-size:9px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } .loginform{ font-family:verdana, arial, helvetica; width:75px; font-size:10px; font-style:normal; font-weight:normal; color:#000000; text-decoration:none; } a.loginlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } .loginlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } a.loginlink:visited{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } a.loginlink:hover{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#5395DB; text-decoration:none; } a.loginlink:active{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#FFFFFF; text-decoration:none; } a.navlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } .navlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a.navlink:visited{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a.navlink:hover{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } a.navlink:active{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } .nav{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#5395DB; text-decoration:none; } a.navlinkon{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:underline; } .navlinkon{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } a.navlinkon:visited{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } a.navlinkon:hover{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } a.navlinkon:active{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } .nav{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#5395DB; text-decoration:none; } a.slink{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#5395DB; text-decoration:none; } .slink{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#5395DB; text-decoration:none; } a.slink:visited{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#5395DB; text-decoration:none; } a.slink:hover{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#C31111; text-decoration:underline; } a.slink:active{ font-family:verdana, arial, helvetica; font-size:9px; font-style:normal; font-weight:normal; color:#5395DB; text-decoration:none; } a.boldlink{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:underline; } .boldlink{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:underline; } a.boldlink:visited{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:underline; } a.boldlink:hover{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:underline; } a.boldlink:active{ font-family:verdana, arial, helvetica; font-size:11px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:underline; } a.smalllink{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } .smalllink{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.smalllink:visited{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.smalllink:hover{ font-family:verdana, arial, helvetica; color:#C31111; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.smalllink:active{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.golink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } .golink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a.golink:visited{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a.golink:hover{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#C31111; text-decoration:none; } a.golink:active{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:bold; color:#3366B4; text-decoration:none; } a.breadcrumblink{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:12px; font-style:normal; font-weight:bold; text-decoration:underline; text-align:left; } .breadcrumblink{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:12px; font-style:normal; font-weight:bold; text-decoration:underline; text-align:left; } .breadcrumb{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:12px; font-style:normal; font-weight:bold; text-decoration:none; text-align:left; } a.breadcrumblink:visited{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:12px; font-style:normal; font-weight:bold; text-decoration:underline; text-align:left; } a.breadcrumblink:hover{ font-family:verdana, arial, helvetica; color:#C31111; font-size:12px; font-style:normal; font-weight:bold; text-decoration:underline; text-align:left; } a.breadcrumblink:active{ font-family:verdana, arial, helvetica; color:#3366B4; font-size:12px; font-style:normal; font-weight:bold; text-decoration:underline; text-align:left; } a.passwordlink{ font-family:verdana, arial, helvetica; color:#000000; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } .passwordlink{ font-family:verdana, arial, helvetica; color:#000000; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.passwordlink:visited{ font-family:verdana, arial, helvetica; color:#000000; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.passwordlink:hover{ font-family:verdana, arial, helvetica; color:#C31111; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.passwordlink:active{ font-family:verdana, arial, helvetica; color:#000000; font-size:9px; font-style:normal; font-weight:normal; text-decoration:underline; } a.tldlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#7B7B7B; text-decoration:none; } .tldlink{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#7B7B7B; text-decoration:none; } a.tldlink:visited{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#7B7B7B; text-decoration:none; } a.tldlink:hover{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#A3CA00; text-decoration:underline; } a.tldlink:active{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#7B7B7B; text-decoration:none; } .tld{ font-family:verdana, arial, helvetica; font-size:10px; font-style:normal; font-weight:normal; color:#7B7B7B; text-decoration:none; line-height:15px; background-color:#FFFFFF; } td.weekdays{ font-family:verdana, arial, helvetica; font-weight:bold; } td.weekcounter{ font-family:verdana, arial, helvetica; font-size:70%; font-weight:bold; } td.selected_directory { background-color:#cccccc; } td.unselected_directory { } td.calendar_month_header{ color:blue; font-size:120%; font-weight:bold; }