• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Products.CMFCore / 4524439281

pending completion
4524439281

push

github

GitHub
SkinsTool: fix Find form with Zope4 ZMI (#127)

2451 of 3655 branches covered (67.06%)

Branch coverage included in aggregate %.

17256 of 19222 relevant lines covered (89.77%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.33
/src/Products/CMFCore/SkinsTool.py
1
##############################################################################
2
#
3
# Copyright (c) 2001 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
"""Portal skins tool.
1✔
14
"""
15

16
from difflib import unified_diff
1✔
17

18
from AccessControl.class_init import InitializeClass
1✔
19
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
20
from Acquisition import aq_base
1✔
21
from App.special_dtml import DTMLFile
1✔
22
from DateTime import DateTime
1✔
23
from OFS.DTMLMethod import DTMLMethod
1✔
24
from OFS.Folder import Folder
1✔
25
from OFS.Image import Image
1✔
26
from OFS.ObjectManager import REPLACEABLE
1✔
27
from Persistence import PersistentMapping
1✔
28
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
1✔
29
from zope.component import getUtility
1✔
30
from zope.globalrequest import getRequest
1✔
31
from zope.interface import implementer
1✔
32

33
from Products.PythonScripts.PythonScript import PythonScript
1✔
34

35
from .ActionProviderBase import ActionProviderBase
1✔
36
from .DirectoryView import base_ignore
1✔
37
from .DirectoryView import ignore
1✔
38
from .DirectoryView import ignore_re
1✔
39
from .interfaces import IMembershipTool
1✔
40
from .interfaces import ISkinsTool
1✔
41
from .interfaces import IURLTool
1✔
42
from .permissions import AccessContentsInformation
1✔
43
from .permissions import ManagePortal
1✔
44
from .permissions import View
1✔
45
from .SkinsContainer import SkinsContainer
1✔
46
from .utils import UniqueObject
1✔
47
from .utils import _dtmldir
1✔
48
from .utils import registerToolInterface
1✔
49

50

51
def modifiedOptions():
1✔
52
    # Remove the existing "Properties" option and add our own.
53
    rval = []
1✔
54
    for o in Folder.manage_options:
1✔
55
        label = o.get('label', None)
1✔
56
        if label != 'Properties':
1✔
57
            rval.append(o)
1✔
58
    rval[1:1] = [{'label': 'Properties', 'action': 'manage_propertiesForm'}]
1✔
59
    return tuple(rval)
1✔
60

61

62
@implementer(ISkinsTool)
1✔
63
class SkinsTool(UniqueObject, SkinsContainer, Folder, ActionProviderBase):
1✔
64

65
    """ This tool is used to supply skins to a portal.
66
    """
67

68
    id = 'portal_skins'
1✔
69
    meta_type = 'CMF Skins Tool'
1✔
70
    allow_any = 0
1✔
71
    cookie_persistence = 0
1✔
72
    default_skin = ''
1✔
73
    request_varname = 'portal_skin'
1✔
74
    selections = None
1✔
75

76
    security = ClassSecurityInfo()
1✔
77

78
    manage_options = (modifiedOptions() +
1✔
79
                      ({'label': 'Overview', 'action': 'manage_overview'},) +
80
                      ActionProviderBase.manage_options)
81

82
    def __init__(self):
1✔
83
        self.selections = PersistentMapping()
1✔
84

85
    def _getSelections(self):
1✔
86
        sels = self.selections
1✔
87
        if sels is None:
1!
88
            # Backward compatibility.
89
            self.selections = sels = PersistentMapping()
×
90
        return sels
1✔
91

92
    #
93
    #   ZMI methods
94
    #
95
    security.declareProtected(ManagePortal,  # NOQA: flake8: D001
1✔
96
                              'manage_overview')
97
    manage_overview = DTMLFile('explainSkinsTool', _dtmldir)
1✔
98

99
    security.declareProtected(ManagePortal,  # NOQA: flake8: D001
1✔
100
                              'manage_propertiesForm')
101
    manage_propertiesForm = DTMLFile('dtml/skinProps', globals())
1✔
102

103
    # the following method override the one in FindSupport, to
104
    # support marking of objects used in specific skins
105
    security.declareProtected(ManagePortal,  # NOQA: flake8: D001
1✔
106
                              'manage_findForm')
107
    manage_findForm = DTMLFile('findForm', _dtmldir,
1✔
108
                               management_view='Find')
109

110
    security.declareProtected(ManagePortal,  # NOQA: flake8: D001
1✔
111
                              'manage_compareResults')
112
    manage_compareResults = DTMLFile('compareResults', _dtmldir,
1✔
113
                                     management_view='Compare')
114

115
    @security.protected(ManagePortal)
1✔
116
    def manage_skinLayers(self, chosen=(), add_skin=0, del_skin=0,
1✔
117
                          skinname='', skinpath='', REQUEST=None):
118
        """ Change the skinLayers.
119
        """
120
        sels = self._getSelections()
×
121
        if del_skin:
×
122
            for name in chosen:
×
123
                del sels[name]
×
124

125
        if REQUEST is not None:
×
126
            for key in sels:
×
127
                fname = 'skinpath_%s' % key
×
128
                val = REQUEST[fname]
×
129

130
                # if val is a list from the new lines field
131
                # then munge it back into a comma delimited list
132
                # for hysterical reasons
133
                if isinstance(val, list):
×
134
                    val = ','.join([layer.strip() for layer in val])
×
135

136
                if sels[key] != val:
×
137
                    self.testSkinPath(val)
×
138
                    sels[key] = val
×
139

140
        if add_skin:
×
141
            skinpath = ','.join([layer.strip() for layer in skinpath])
×
142
            self.testSkinPath(skinpath)
×
143
            sels[str(skinname)] = skinpath
×
144

145
        if REQUEST is not None:
×
146
            msg = 'Skins changed.'
×
147
            return self.manage_propertiesForm(self, REQUEST,
×
148
                                              management_view='Properties',
149
                                              manage_tabs_message=msg)
150

151
    @security.protected(ManagePortal)
1✔
152
    def isFirstInSkin(self, template_path, skin=None):
1✔
153
        """
154
        Is the specified template the one that would get returned
155
        from the current skin?
156
        """
157
        if skin is None or skin == 'None':
×
158
            skin = self.getDefaultSkin()
×
159
        template = self.restrictedTraverse(template_path)
×
160
        name = template.getId()
×
161
        skin_path = self.getSkinPath(skin)
×
162
        if not skin_path:
×
163
            return 0
×
164
        parts = list(skin_path.split(','))
×
165
        found = ''
×
166
        for part in parts:
×
167
            part = part.strip()
×
168
            if part[0] == '_':
×
169
                continue
×
170
            partob = getattr(self, part, None)
×
171
            if partob:
×
172
                skin_template = getattr(partob.aq_base, name, None)
×
173
                if skin_template:
×
174
                    found = skin_template
×
175
                    break
×
176
        if found == template:
×
177
            return 1
×
178
        else:
179
            return 0
×
180

181
    @security.protected(ManagePortal)
1✔
182
    def manage_properties(self, default_skin='', request_varname='',
1✔
183
                          allow_any=0, chosen=(), add_skin=0,
184
                          del_skin=0, skinname='', skinpath='',
185
                          cookie_persistence=0, REQUEST=None):
186
        """ Changes portal_skin properties. """
187
        self.default_skin = str(default_skin)
1✔
188
        self.request_varname = str(request_varname)
1✔
189
        self.allow_any = allow_any and 1 or 0
1✔
190
        self.cookie_persistence = cookie_persistence and 1 or 0
1✔
191
        if REQUEST is not None:
1!
192
            msg = 'Properties changed.'
×
193
            return self.manage_propertiesForm(self, REQUEST,
×
194
                                              management_view='Properties',
195
                                              manage_tabs_message=msg)
196

197
    @security.private
1✔
198
    def PUT_factory(self, name, typ, body):
1✔
199
        """
200
            Dispatcher for PUT requests to non-existent IDs.  Returns
201
            an object of the appropriate type (or None, if we don't
202
            know what to do).
203
        """
204
        major, minor = typ.split('/', 1)
×
205

206
        if major == 'image':
×
207
            return Image(id=name, title='', file='', content_type=typ)
×
208

209
        if major == 'text':
×
210

211
            if minor == 'x-python':
×
212
                return PythonScript(id=name)
×
213

214
            if minor in ('html', 'xml'):
×
215
                return ZopePageTemplate(name)
×
216

217
            return DTMLMethod(__name__=name)
×
218

219
        return None
×
220

221
    # Make the PUT_factory replaceable
222
    PUT_factory__replaceable__ = REPLACEABLE
1✔
223

224
    @security.private
1✔
225
    def testSkinPath(self, p):
1✔
226
        """ Calls SkinsContainer.getSkinByPath().
227
        """
228
        self.getSkinByPath(p, raise_exc=1)
×
229

230
    #
231
    #   'SkinsContainer' interface methods
232
    #
233
    @security.protected(AccessContentsInformation)
1✔
234
    def getSkinPath(self, name):
1✔
235
        """ Convert a skin name to a skin path.
236
        """
237
        sels = self._getSelections()
1✔
238
        p = sels.get(name, None)
1✔
239
        if p is None:
1!
240
            if self.allow_any:
×
241
                return name
×
242
        return p  # Can be None
1✔
243

244
    @security.protected(AccessContentsInformation)
1✔
245
    def getDefaultSkin(self):
1✔
246
        """ Get the default skin name.
247
        """
248
        return self.default_skin
1✔
249

250
    @security.protected(AccessContentsInformation)
1✔
251
    def getRequestVarname(self):
1✔
252
        """ Get the variable name to look for in the REQUEST.
253
        """
254
        return self.request_varname
1✔
255

256
    #
257
    #   UI methods
258
    #
259
    @security.protected(AccessContentsInformation)
1✔
260
    def getAllowAny(self):
1✔
261
        """
262
        Used by the management UI.  Returns a flag indicating whether
263
        users are allowed to use arbitrary skin paths.
264
        """
265
        return self.allow_any
×
266

267
    @security.protected(AccessContentsInformation)
1✔
268
    def getCookiePersistence(self):
1✔
269
        """
270
        Used by the management UI.  Returns a flag indicating whether
271
        the skins cookie is persistent or not.
272
        """
273
        return self.cookie_persistence
×
274

275
    @security.protected(AccessContentsInformation)
1✔
276
    def getSkinPaths(self):
1✔
277
        """
278
        Used by the management UI.  Returns the list of skin name to
279
        skin path mappings as a sorted list of tuples.
280
        """
281
        sels = self._getSelections()
1✔
282
        rval = []
1✔
283
        for key, value in sorted(sels.items()):
1✔
284
            rval.append((key, value))
1✔
285
        return rval
1✔
286

287
    #
288
    #   'portal_skins' interface methods
289
    #
290
    @security.public
1✔
291
    def getSkinSelections(self):
1✔
292
        """ Get the sorted list of available skin names.
293
        """
294
        sels = self._getSelections()
1✔
295
        rval = sorted(sels)
1✔
296
        return rval
1✔
297

298
    @security.protected(View)
1✔
299
    def updateSkinCookie(self):
1✔
300
        """ If needed, updates the skin cookie based on the member preference.
301
        """
302
        mtool = getUtility(IMembershipTool)
×
303
        member = mtool.getAuthenticatedMember()
×
304
        if hasattr(aq_base(member), 'getProperty'):
×
305
            mskin = member.getProperty('portal_skin')
×
306
            if mskin:
×
307
                req = getRequest()
×
308
                cookie = req.cookies.get(self.request_varname, None)
×
309
                if cookie != mskin:
×
310
                    resp = req.RESPONSE
×
311
                    utool = getUtility(IURLTool)
×
312
                    portal_path = req['BASEPATH1'] + '/' + utool(1)
×
313

314
                    if not self.cookie_persistence:
×
315
                        # *Don't* make the cookie persistent!
316
                        resp.setCookie(self.request_varname, mskin,
×
317
                                       path=portal_path)
318
                    else:
319
                        expires = (DateTime('GMT') + 365).rfc822()
×
320
                        resp.setCookie(self.request_varname, mskin,
×
321
                                       path=portal_path, expires=expires)
322
                    # Ensure updateSkinCookie() doesn't try again
323
                    # within this request.
324
                    req.cookies[self.request_varname] = mskin
×
325
                    req[self.request_varname] = mskin
×
326
                    return 1
×
327
        return 0
×
328

329
    @security.protected(View)
1✔
330
    def clearSkinCookie(self):
1✔
331
        """ Expire the skin cookie.
332
        """
333
        req = getRequest()
×
334
        resp = req.RESPONSE
×
335
        utool = getUtility(IURLTool)
×
336
        portal_path = req['BASEPATH1'] + '/' + utool(1)
×
337
        resp.expireCookie(self.request_varname, path=portal_path)
×
338

339
    @security.protected(ManagePortal)
1✔
340
    def addSkinSelection(self, skinname, skinpath, test=0, make_default=0):
1✔
341
        """
342
        Adds a skin selection.
343
        """
344
        sels = self._getSelections()
1✔
345
        skinpath = str(skinpath)
1✔
346

347
        # Basic precaution to make sure the stuff we want to ignore in
348
        # DirectoryViews gets prevented from ending up in a skin path
349
        path_elems = [x.strip() for x in skinpath.split(',')]
1✔
350
        ignored = base_ignore + ignore
1✔
351

352
        for elem in path_elems[:]:
1✔
353
            if elem in ignored or ignore_re.match(elem):
1✔
354
                path_elems.remove(elem)
1✔
355

356
        skinpath = ','.join(path_elems)
1✔
357

358
        if test:
1!
359
            self.testSkinPath(skinpath)
×
360
        sels[str(skinname)] = skinpath
1✔
361
        if make_default:
1!
362
            self.default_skin = skinname
×
363

364
    @security.protected(AccessContentsInformation)
1✔
365
    def getDiff(self, item_one_path, item_two_path, reverse=0):
1✔
366
        """ Return a diff between one and two.
367
        """
368
        if not reverse:
×
369
            item_one = self.unrestrictedTraverse(item_one_path)
×
370
            item_two = self.unrestrictedTraverse(item_two_path)
×
371
        else:
372
            item_one = self.unrestrictedTraverse(item_two_path)
×
373
            item_two = self.unrestrictedTraverse(item_one_path)
×
374

375
        res = unified_diff(item_one.read().splitlines(),
×
376
                           item_two.read().splitlines(),
377
                           item_one_path, item_two_path, '', '', lineterm='')
378
        return res
×
379

380

381
InitializeClass(SkinsTool)
1✔
382
registerToolInterface('portal_skins', ISkinsTool)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc