Back to index

plone3  3.1.7
JSRegistry.py
Go to the documentation of this file.
00001 from Globals import InitializeClass
00002 from AccessControl import ClassSecurityInfo
00003 
00004 from zope.interface import implements
00005 
00006 from Products.CMFCore.utils import getToolByName
00007 
00008 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00009 
00010 from Products.ResourceRegistries import config
00011 from Products.ResourceRegistries import permissions
00012 from Products.ResourceRegistries.interfaces import IJSRegistry
00013 from Products.ResourceRegistries.tools.BaseRegistry import BaseRegistryTool
00014 from Products.ResourceRegistries.tools.BaseRegistry import Resource
00015 
00016 import re
00017 from packer import JavascriptPacker, JavascriptKeywordMapper
00018 
00019 
00020 class JavaScript(Resource):
00021     security = ClassSecurityInfo()
00022 
00023     def __init__(self, id, **kwargs):
00024         Resource.__init__(self, id, **kwargs)
00025         self._data['inline'] = kwargs.get('inline', False)
00026         self._data['compression'] = kwargs.get('compression', 'safe')
00027 
00028     security.declarePublic('getInline')
00029     def getInline(self):
00030         return self._data['inline']
00031 
00032     security.declareProtected(permissions.ManagePortal, 'setInline')
00033     def setInline(self, inline):
00034         self._data['inline'] = inline
00035 
00036     security.declarePublic('getCompression')
00037     def getCompression(self):
00038         # as this is a new property, old instance might not have that value, so
00039         # return 'safe' as default
00040         compression = self._data.get('compression', 'safe')
00041         if compression in config.JS_COMPRESSION_METHODS:
00042             return compression
00043         return 'none'
00044 
00045     security.declareProtected(permissions.ManagePortal, 'setCompression')
00046     def setCompression(self, compression):
00047         self._data['compression'] = compression
00048 
00049 InitializeClass(JavaScript)
00050 
00051 
00052 class JSRegistryTool(BaseRegistryTool):
00053     """A Plone registry for managing the linking to Javascript files."""
00054 
00055     id = config.JSTOOLNAME
00056     meta_type = config.JSTOOLTYPE
00057     title = 'JavaScript Registry'
00058 
00059     security = ClassSecurityInfo()
00060 
00061     implements(IJSRegistry)
00062     __implements__ = BaseRegistryTool.__implements__
00063 
00064     #
00065     # ZMI stuff
00066     #
00067 
00068     manage_jsForm = PageTemplateFile('www/jsconfig', config.GLOBALS)
00069     manage_jsComposition = PageTemplateFile('www/jscomposition', config.GLOBALS)
00070 
00071     manage_options = (
00072         {
00073             'label': 'Javascript Registry',
00074             'action': 'manage_jsForm',
00075         },
00076         {
00077             'label': 'Merged JS Composition',
00078             'action': 'manage_jsComposition',
00079         },
00080     ) + BaseRegistryTool.manage_options
00081 
00082     attributes_to_compare = ('getExpression', 'getCookable',
00083                              'getCacheable', 'getInline')
00084     filename_base = 'ploneScripts'
00085     filename_appendix = '.js'
00086     cache_duration = config.JS_CACHE_DURATION
00087     merged_output_prefix = u"""
00088 /* Merged Plone Javascript file
00089  * This file is dynamically assembled from separate parts.
00090  * Some of these parts have 3rd party licenses or copyright information attached
00091  * Such information is valid for that section,
00092  * not for the entire composite file
00093  * originating files are separated by - filename.js -
00094  */
00095 """
00096     resource_class = JavaScript
00097 
00098     #
00099     # Private Methods
00100     #
00101 
00102     security.declarePrivate('clearScripts')
00103     def clearScripts(self):
00104         self.clearResources()
00105 
00106     def _compressJS(self, content, level='safe'):
00107         encode_marker = "/* sTART eNCODE */\n%s\n/* eND eNCODE */"
00108         if level == 'full-encode':
00109             return encode_marker % JavascriptPacker('full').pack(content)
00110         elif level == 'safe-encode':
00111             return encode_marker % JavascriptPacker('safe').pack(content)
00112         elif level == 'full':
00113             return JavascriptPacker('full').pack(content)
00114         elif level == 'safe':
00115             return JavascriptPacker('safe').pack(content)
00116         else:
00117             return content
00118 
00119     security.declarePrivate('finalizeContent')
00120     def finalizeContent(self, resource, content):
00121         """Finalize the resource content."""
00122         compression = resource.getCompression()
00123         if compression != 'none' and not self.getDebugMode():
00124             orig_url = "%s/%s?original=1" % (self.absolute_url(), resource.getId())
00125             content = "// %s\n%s" % (orig_url,
00126                                      self._compressJS(content, compression))
00127 
00128         return content
00129 
00130     #
00131     # ZMI Methods
00132     #
00133 
00134     security.declareProtected(permissions.ManagePortal, 'manage_addScript')
00135     def manage_addScript(self, id, expression='', inline=False,
00136                          enabled=False, cookable=True, compression='safe',
00137                          cacheable=True, REQUEST=None):
00138         """Register a script from a TTW request."""
00139         self.registerScript(id, expression, inline, enabled, cookable, compression, cacheable)
00140         if REQUEST:
00141             REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])
00142 
00143     security.declareProtected(permissions.ManagePortal, 'manage_saveScripts')
00144     def manage_saveScripts(self, REQUEST=None):
00145         """Save scripts from the ZMI.
00146 
00147         Updates the whole sequence. For editing and reordering.
00148         """
00149         debugmode = REQUEST.get('debugmode', False)
00150         self.setDebugMode(debugmode)
00151         autogroupingmode = REQUEST.get('autogroupingmode', False)
00152         self.setAutoGroupingMode(autogroupingmode)
00153         records = REQUEST.form.get('scripts')
00154         records.sort(lambda a, b: a.sort-b.sort)
00155         self.resources = ()
00156         scripts = []
00157         for r in records:
00158             script = JavaScript(r.get('id'),
00159                                 expression=r.get('expression', ''),
00160                                 inline=r.get('inline'),
00161                                 enabled=r.get('enabled'),
00162                                 cookable=r.get('cookable'),
00163                                 cacheable=r.get('cacheable'),
00164                                 compression=r.get('compression', 'safe'))
00165             scripts.append(script)
00166         self.resources = tuple(scripts)
00167         self.cookResources()
00168         if REQUEST:
00169             REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])
00170 
00171     security.declareProtected(permissions.ManagePortal, 'manage_removeScript')
00172     def manage_removeScript(self, id, REQUEST=None):
00173         """Remove script with ZMI button."""
00174         self.unregisterResource(id)
00175         if REQUEST:
00176             REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])
00177 
00178     #
00179     # Protected Methods
00180     #
00181 
00182     security.declareProtected(permissions.ManagePortal, 'registerScript')
00183     def registerScript(self, id, expression='', inline=False, enabled=True,
00184                        cookable=True, compression='safe', cacheable=True,
00185                        skipCooking=False):
00186         """Register a script."""
00187         script = JavaScript(id,
00188                             expression=expression,
00189                             inline=inline,
00190                             enabled=enabled,
00191                             cookable=cookable,
00192                             compression=compression,
00193                             cacheable=cacheable)
00194         self.storeResource(script, skipCooking=skipCooking)
00195 
00196     security.declareProtected(permissions.ManagePortal, 'updateScript')
00197     def updateScript(self, id, **data):
00198         script = self.getResourcesDict().get(id, None)
00199         if script is None:
00200             raise ValueError, 'Invalid resource id %s' % (id)
00201         if data.get('expression', None) is not None:
00202             script.setExpression(data['expression'])
00203         if data.get('inline', None) is not None:
00204             script.setInline(data['inline'])
00205         if data.get('enabled', None) is not None:
00206             script.setEnabled(data['enabled'])
00207         if data.get('cookable', None) is not None:
00208             script.setCookable(data['cookable'])
00209         if data.get('compression', None) is not None:
00210             script.setCompression(data['compression'])
00211         if data.get('cacheable', None) is not None:
00212             script.setCacheable(data['cacheable'])
00213 
00214     security.declareProtected(permissions.ManagePortal, 'getCompressionOptions')
00215     def getCompressionOptions(self):
00216         """Compression methods for use in ZMI forms."""
00217         return config.JS_COMPRESSION_METHODS
00218 
00219     security.declareProtected(permissions.View, 'getContentType')
00220     def getContentType(self):
00221         """Return the registry content type."""
00222         plone_utils = getToolByName(self, 'plone_utils')
00223         try:
00224             encoding = plone_utils.getSiteEncoding()
00225         except AttributeError:
00226             # For Plone < 2.1
00227             pprop = getToolByName(self, 'portal_properties')
00228             default = 'utf-8'
00229             try:
00230                 encoding = pprop.site_properties.getProperty('default_charset', default)
00231             except AttributeError:
00232                 encoding = default
00233         return 'application/x-javascript;charset=%s' % encoding
00234 
00235     security.declarePrivate('getResourceContent')
00236     def getResourceContent(self, item, context, original=False):
00237         output = BaseRegistryTool.getResourceContent(self, item, context, original)
00238         if not original:
00239             mapper = JavascriptKeywordMapper()
00240             regexp = re.compile(r"/\* sTART eNCODE \*/\s*(.*?)\s*/\* eND eNCODE \*/", re.DOTALL)
00241             matches = regexp.findall(output)
00242             if len(matches) > 0:
00243                 mapper.analyse("\n".join(matches))
00244                 decoder = mapper.getDecodeFunction(name='__dEcOdE')
00245                 def repl(m):
00246                     return mapper.getDecoder(mapper.sub(m.group(1)),
00247                                              keyword_var="''",
00248                                              decode_func='__dEcOdE')
00249                 #output = "\n__sTaRtTiMe = new Date()\n%s\n%s\nalert(new Date() - __sTaRtTiMe);" % (decoder,
00250                 output = "\n%s\n%s\n" % (decoder,
00251                                          regexp.sub(repl, output))
00252         return output
00253 
00254 InitializeClass(JSRegistryTool)