File: commands.py

package info (click to toggle)
plone3 3.1.3-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 73,972 kB
  • ctags: 39,149
  • sloc: python: 209,481; xml: 25,065; sh: 1,395; makefile: 585; php: 129; lisp: 54
file content (214 lines) | stat: -rw-r--r-- 7,527 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# Copyright (c) 2005-2007
# Authors: KSS Project Contributors (see docs/CREDITS.txt)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

'''\
Marshal objects

These build up the response and get marshalled to the client
in the defined format
'''

from xml.sax.saxutils import escape as xml_escape
from zope.interface import implements
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from interfaces import IKSSCommands, IKSSCommand, IKSSParam, IKSSCommandView
from unicode_quirks import force_unicode
import zope.component
from parsers import XmlParser, HtmlParser
from pluginregistry import checkRegisteredCommand_old
from pluginregistry import checkRegisteredCommand, checkRegisteredSelector, \
        KSSPluginError

class KSSCommands(list):
    implements(IKSSCommands)
 
    def addCommand(self, command_name, selector=None, **kw):
        command = KSSCommand(command_name, selector=selector, **kw)
        self.append(command)
        return command
   
    def render(self, request):
        '''All methods must use this to return their command set
        '''
        adapter = zope.component.getMultiAdapter((self, request), IKSSCommandView)
        return adapter.render()

class KSSParam:
    implements(IKSSParam)
    
    def __init__(self, name, content=''):
        self.name = name
        self.content = content

    def force_content_unicode(self):
        # Content must be str with ascii encoding, or unicode!
        self.content = force_unicode(self.content)

    def getName(self):
        return self.name

    def getContent(self):
        return self.content
        
class KSSCommand:
    implements(IKSSCommand)
    
    def __init__(self, command_name, selector=None, **kw):
        try:
            checkRegisteredCommand_old(command_name)
        except KSSPluginError:
            # we expect this is not registered as command, anyway
            # so check it as an action.
            checkRegisteredCommand(command_name)
        else:
            # ok. XXX this will be deprecated
            # All registerCommand commands are obsolete, by default
            import warnings, textwrap
            warnings.warn(textwrap.dedent('''\
            The usage of the kss command "%s" is deprecated''' 
                % (command_name, )), DeprecationWarning, 2)
        if selector is not None:
            if isinstance(selector, basestring): 
                # the default selector - given just as a string
                self.selector = selector
                self.selectorType = ''
            else:
                checkRegisteredSelector(selector.type)
                self.selector = selector.value
                self.selectorType = selector.type
        else:
            self.selector = None
            self.selectorType = None
        self.name = command_name
        self.params = []
        # Add parameters passed in **kw
        for key, value in kw.iteritems():
            self.addParam(key, value)

    # --
    # Different parameter conversions
    # --

    # REMARK: with the jsonserver product present, you can
    # just send complex data types directly with AddParam

    def addParam(self, name, content=''):
        # Check for the size of the content. Larger than 4K will give
        # problems with Firefox (which splits text nodes). Therefore
        # we give this special treatment.
        if len(content) > 4096:
            return self.addCdataParam(name, content)
        else:
            # Escape all XML characters
            return self._addParam(name, content=xml_escape(content))

    def _addParam(self, name, content=''):
        'Add the param as is'
        param = KSSParam(name, content)
        self.params.append(param)
        return param

    #
    # Some helpers
    #

    def addUnicodeParam(self, name, content=u''):
        'Add the param as unicode'
        self.addParam(name, content)

    def addStringParam(self, name, content='', encoding='utf8'):
        'Add the param as an encoded string, by default UTF-8'
        content = unicode(content, encoding)
        self.addUnicodeParam(name, content=content)

    def addHtmlParam(self, name, content=''):
        'Add the param as an HTML content.'
        content = HtmlParser(content)().encode('ascii', 'xmlcharrefreplace')
        ##self.addParam(name, content=content)
        # add html as cdata!
        self.addCdataParam(name, content=content)

    def addXmlParam(self, name, content=''):
        'Add the param as XML content'
        content = XmlParser(content)().encode('ascii', 'xmlcharrefreplace')
        self._addParam(name, content=content)

    def addCdataParam(self, name, content=''):
        'Add the param as a CDATA node'
        # Replace `>` part of `]]>` with the entity ref so it won't
        # accidentally close the CDATA (required by the XML spec)
        content = '<![CDATA[%s]]>' % content.replace(']]>', ']]&gt;')
        self._addParam(name, content=content)


    # --
    # Accessors, not sure if we need them
    # --

    def getName(self):
        return self.name

    def getSelector(self):
        return self.selector

    def getSelectorType(self):
        return self.selectorType

    def getParams(self):
        return self.params

class CommandView(object):
    '''View of a command.
    
    The render method does actual marshalling
    of the commands to be sent to the client.
    '''
    implements(IKSSCommandView)

    def __init__(self, context, request):
        self.context = context
        self.request = request
        # XXX From Zope2.9 we need this.
        # Note: We don't use proper views for Five. As our context object
        # is not even a proper Zope content. There would be a way to:
        #
        # - use ZopeTwoPageTemplateFile
        # - and, make the object to be proper zope content.
        #
        # This would be two much ado for nothing, so we just use barefoot
        # rendering but as a consequence no path expression, only python:
        # is available from the page template.
        if not hasattr(self.request, 'debug'):
            self.request.debug = None

        # Force parameters content to be unicode
        for command in context:
            for param in command.getParams():
                param.force_content_unicode()
    
    # XML output gets rendered via a page template
    # XXX note: barefoot rendering, use python: only after zope2.9
    # XXX we must have the content type set both here and below
    _render = ViewPageTemplateFile('browser/kukitresponse.pt', content_type='text/xml;charset=utf-8')

    def render(self):
        result = self._render()
        # Always output text/xml to make sure browsers but the data in the
        # responseXML instead of responseText attribute of the
        # XMLHttpRequestobject.
        self.request.response.setHeader('Content-type', 'text/xml;charset=utf-8')
        return result