Back to index

plone3  3.1.7
commands.py
Go to the documentation of this file.
00001 # Copyright (c) 2005-2007
00002 # Authors: KSS Project Contributors (see docs/CREDITS.txt)
00003 #
00004 # This program is free software; you can redistribute it and/or modify
00005 # it under the terms of the GNU General Public License version 2 as published
00006 # by the Free Software Foundation.
00007 #
00008 # This program is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 # GNU General Public License for more details.
00012 #
00013 # You should have received a copy of the GNU General Public License
00014 # along with this program; if not, write to the Free Software
00015 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00016 # 02111-1307, USA.
00017 
00018 '''\
00019 Marshal objects
00020 
00021 These build up the response and get marshalled to the client
00022 in the defined format
00023 '''
00024 
00025 from xml.sax.saxutils import escape as xml_escape
00026 from zope.interface import implements
00027 from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
00028 from interfaces import IKSSCommands, IKSSCommand, IKSSParam, IKSSCommandView
00029 from unicode_quirks import force_unicode
00030 import zope.component
00031 from parsers import XmlParser, HtmlParser
00032 from pluginregistry import checkRegisteredCommand_old
00033 from pluginregistry import checkRegisteredCommand, checkRegisteredSelector, \
00034         KSSPluginError
00035 
00036 class KSSCommands(list):
00037     implements(IKSSCommands)
00038  
00039     def addCommand(self, command_name, selector=None, **kw):
00040         command = KSSCommand(command_name, selector=selector, **kw)
00041         self.append(command)
00042         return command
00043    
00044     def render(self, request):
00045         '''All methods must use this to return their command set
00046         '''
00047         adapter = zope.component.getMultiAdapter((self, request), IKSSCommandView)
00048         return adapter.render()
00049 
00050 class KSSParam:
00051     implements(IKSSParam)
00052     
00053     def __init__(self, name, content=''):
00054         self.name = name
00055         self.content = content
00056 
00057     def force_content_unicode(self):
00058         # Content must be str with ascii encoding, or unicode!
00059         self.content = force_unicode(self.content)
00060 
00061     def getName(self):
00062         return self.name
00063 
00064     def getContent(self):
00065         return self.content
00066         
00067 class KSSCommand:
00068     implements(IKSSCommand)
00069     
00070     def __init__(self, command_name, selector=None, **kw):
00071         try:
00072             checkRegisteredCommand_old(command_name)
00073         except KSSPluginError:
00074             # we expect this is not registered as command, anyway
00075             # so check it as an action.
00076             checkRegisteredCommand(command_name)
00077         else:
00078             # ok. XXX this will be deprecated
00079             # All registerCommand commands are obsolete, by default
00080             import warnings, textwrap
00081             warnings.warn(textwrap.dedent('''\
00082             The usage of the kss command "%s" is deprecated''' 
00083                 % (command_name, )), DeprecationWarning, 2)
00084         if selector is not None:
00085             if isinstance(selector, basestring): 
00086                 # the default selector - given just as a string
00087                 self.selector = selector
00088                 self.selectorType = ''
00089             else:
00090                 checkRegisteredSelector(selector.type)
00091                 self.selector = selector.value
00092                 self.selectorType = selector.type
00093         else:
00094             self.selector = None
00095             self.selectorType = None
00096         self.name = command_name
00097         self.params = []
00098         # Add parameters passed in **kw
00099         for key, value in kw.iteritems():
00100             self.addParam(key, value)
00101 
00102     # --
00103     # Different parameter conversions
00104     # --
00105 
00106     # REMARK: with the jsonserver product present, you can
00107     # just send complex data types directly with AddParam
00108 
00109     def addParam(self, name, content=''):
00110         # Check for the size of the content. Larger than 4K will give
00111         # problems with Firefox (which splits text nodes). Therefore
00112         # we give this special treatment.
00113         if len(content) > 4096:
00114             return self.addCdataParam(name, content)
00115         else:
00116             # Escape all XML characters
00117             return self._addParam(name, content=xml_escape(content))
00118 
00119     def _addParam(self, name, content=''):
00120         'Add the param as is'
00121         param = KSSParam(name, content)
00122         self.params.append(param)
00123         return param
00124 
00125     #
00126     # Some helpers
00127     #
00128 
00129     def addUnicodeParam(self, name, content=u''):
00130         'Add the param as unicode'
00131         self.addParam(name, content)
00132 
00133     def addStringParam(self, name, content='', encoding='utf8'):
00134         'Add the param as an encoded string, by default UTF-8'
00135         content = unicode(content, encoding)
00136         self.addUnicodeParam(name, content=content)
00137 
00138     def addHtmlParam(self, name, content=''):
00139         'Add the param as an HTML content.'
00140         content = HtmlParser(content)().encode('ascii', 'xmlcharrefreplace')
00141         ##self.addParam(name, content=content)
00142         # add html as cdata!
00143         self.addCdataParam(name, content=content)
00144 
00145     def addXmlParam(self, name, content=''):
00146         'Add the param as XML content'
00147         content = XmlParser(content)().encode('ascii', 'xmlcharrefreplace')
00148         self._addParam(name, content=content)
00149 
00150     def addCdataParam(self, name, content=''):
00151         'Add the param as a CDATA node'
00152         # Replace `>` part of `]]>` with the entity ref so it won't
00153         # accidentally close the CDATA (required by the XML spec)
00154         content = '<![CDATA[%s]]>' % content.replace(']]>', ']]&gt;')
00155         self._addParam(name, content=content)
00156 
00157 
00158     # --
00159     # Accessors, not sure if we need them
00160     # --
00161 
00162     def getName(self):
00163         return self.name
00164 
00165     def getSelector(self):
00166         return self.selector
00167 
00168     def getSelectorType(self):
00169         return self.selectorType
00170 
00171     def getParams(self):
00172         return self.params
00173 
00174 class CommandView(object):
00175     '''View of a command.
00176     
00177     The render method does actual marshalling
00178     of the commands to be sent to the client.
00179     '''
00180     implements(IKSSCommandView)
00181 
00182     def __init__(self, context, request):
00183         self.context = context
00184         self.request = request
00185         # XXX From Zope2.9 we need this.
00186         # Note: We don't use proper views for Five. As our context object
00187         # is not even a proper Zope content. There would be a way to:
00188         #
00189         # - use ZopeTwoPageTemplateFile
00190         # - and, make the object to be proper zope content.
00191         #
00192         # This would be two much ado for nothing, so we just use barefoot
00193         # rendering but as a consequence no path expression, only python:
00194         # is available from the page template.
00195         if not hasattr(self.request, 'debug'):
00196             self.request.debug = None
00197 
00198         # Force parameters content to be unicode
00199         for command in context:
00200             for param in command.getParams():
00201                 param.force_content_unicode()
00202     
00203     # XML output gets rendered via a page template
00204     # XXX note: barefoot rendering, use python: only after zope2.9
00205     # XXX we must have the content type set both here and below
00206     _render = ViewPageTemplateFile('browser/kukitresponse.pt', content_type='text/xml;charset=utf-8')
00207 
00208     def render(self):
00209         result = self._render()
00210         # Always output text/xml to make sure browsers but the data in the
00211         # responseXML instead of responseText attribute of the
00212         # XMLHttpRequestobject.
00213         self.request.response.setHeader('Content-type', 'text/xml;charset=utf-8')
00214         return result