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

zopefoundation / Products.CMFCore / 6246931310

20 Sep 2023 09:54AM UTC coverage: 86.008% (-0.3%) from 86.266%
6246931310

Pull #131

github

mauritsvanrees
gha: don't need setup-python on 27 as we use the 27 container.
Pull Request #131: Make decodeFolderFilter and encodeFolderFilter non-public.

2466 of 3689 branches covered (0.0%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

17297 of 19289 relevant lines covered (89.67%)

0.9 hits per line

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

48.57
/src/Products/CMFCore/Skinnable.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
""" Base class for object managers which can be "skinned".
1✔
14

15
Skinnable object managers inherit attributes from a skin specified in
16
the browser request.  Skins are stored in a fixed-name subobject.
17
"""
18

19
import logging
1✔
20

21
from six.moves._thread import get_ident
1✔
22

23
from AccessControl.class_init import InitializeClass
1✔
24
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
25
from Acquisition import aq_base
1✔
26
from OFS.ObjectManager import ObjectManager
1✔
27
from ZODB.POSException import ConflictError
1✔
28
from zope.component import queryUtility
1✔
29

30
from .interfaces import ISkinsTool
1✔
31

32

33
logger = logging.getLogger('CMFCore.Skinnable')
1✔
34

35
_MARKER = object()  # Create a new marker object.
1✔
36

37
SKINDATA = {}  # mapping thread-id -> (skinobj, skinname, ignore, resolve)
1✔
38

39

40
class SkinDataCleanup:
1✔
41
    """Cleanup at the end of the request."""
42
    def __init__(self, tid):
1✔
43
        self.tid = tid
×
44

45
    def __del__(self):
1✔
46
        tid = self.tid
×
47
        # Be extra careful in __del__
48
        if SKINDATA is not None:
×
49
            if tid in SKINDATA:
×
50
                del SKINDATA[tid]
×
51

52

53
class SkinnableObjectManager(ObjectManager):
1✔
54

55
    security = ClassSecurityInfo()
1✔
56

57
    def __getattr__(self, name):
1✔
58
        """
59
        Looks for the name in an object with wrappers that only reach
60
        up to the root skins folder.
61

62
        This should be fast, flexible, and predictable.
63
        """
64
        if not name:
1!
65
            raise AttributeError(name)
×
66
        if name[0] not in ('_', '@', '+') and not name.startswith('aq_'):
1!
67
            sd = SKINDATA.get(get_ident())
1✔
68
            if sd is not None:
1!
69
                ob, _skinname, ignore, resolve = sd
×
70
                if name not in ignore:
×
71
                    if name in resolve:
×
72
                        return resolve[name]
×
73
                    subob = getattr(ob, name, _MARKER)
×
74
                    if subob is not _MARKER:
×
75
                        # Return it in context of self, forgetting
76
                        # its location and acting as if it were located
77
                        # in self.
78
                        retval = aq_base(subob)
×
79
                        resolve[name] = retval
×
80
                        return retval
×
81
                    else:
82
                        ignore[name] = 1
×
83
        raise AttributeError(name)
1✔
84

85
    @security.private
1✔
86
    def getSkin(self, name=None):
1✔
87
        """Returns the requested skin.
88
        """
89
        skinob = None
1✔
90
        stool = queryUtility(ISkinsTool)
1✔
91
        if stool is not None:
1!
92
            if name is not None:
1!
93
                skinob = stool.getSkinByName(name)
1✔
94
            if skinob is None:
1!
95
                skinob = stool.getSkinByName(stool.getDefaultSkin())
1✔
96
                if skinob is None:
1!
97
                    skinob = stool.getSkinByPath('')
1✔
98
        return skinob
1✔
99

100
    @security.public
1✔
101
    def getSkinNameFromRequest(self, REQUEST=None):
1✔
102
        """Returns the skin name from the Request."""
103
        if REQUEST is None:
1!
104
            return None
×
105
        stool = queryUtility(ISkinsTool)
1✔
106
        if stool is not None:
1!
107
            name = REQUEST.get(stool.getRequestVarname(), None)
1✔
108
            if not name or name not in stool.getSkinSelections():
1✔
109
                return None
1✔
110
            return name
1✔
111

112
    @security.public
1✔
113
    def changeSkin(self, skinname, REQUEST=None):
1✔
114
        """Change the current skin.
115

116
        Can be called manually, allowing the user to change
117
        skins in the middle of a request.
118
        """
119
        skinobj = self.getSkin(skinname)
1✔
120
        if skinobj is not None:
1!
121
            tid = get_ident()
1✔
122
            SKINDATA[tid] = (skinobj, skinname, {}, {})
1✔
123
            if REQUEST is not None:
1!
124
                REQUEST._hold(SkinDataCleanup(tid))
×
125

126
    @security.public
1✔
127
    def getCurrentSkinName(self):
1✔
128
        """Return the current skin name.
129
        """
130
        sd = SKINDATA.get(get_ident())
1✔
131
        if sd is not None:
1✔
132
            _ob, skinname, _ignore, _resolve = sd
1✔
133
            if skinname is not None:
1!
134
                return skinname
1✔
135
        # nothing here, so assume the default skin
136
        stool = queryUtility(ISkinsTool)
1✔
137
        if stool is not None:
1!
138
            return stool.getDefaultSkin()
1✔
139
        # and if that fails...
140
        return None
×
141

142
    @security.public
1✔
143
    def clearCurrentSkin(self):
1✔
144
        """Clear the current skin."""
145
        tid = get_ident()
×
146
        if tid in SKINDATA:
×
147
            del SKINDATA[tid]
×
148

149
    @security.public
1✔
150
    def setupCurrentSkin(self, REQUEST=None):
1✔
151
        """
152
        Sets up skindata so that __getattr__ can find it.
153

154
        Can NOT be called manually to change skins in the middle of a
155
        request! Use changeSkin for that.
156
        """
157
        if REQUEST is None:
×
158
            return
×
159
        if get_ident() in SKINDATA:
×
160
            # Already set up for this request.
161
            return
×
162
        skinname = self.getSkinNameFromRequest(REQUEST)
×
163
        try:
×
164
            self.changeSkin(skinname, REQUEST)
×
165
        except ConflictError:
×
166
            raise
×
167
        except Exception:
×
168
            # This shouldn't happen, even if the requested skin
169
            # does not exist.
170
            logger.exception('Unable to setupCurrentSkin()')
×
171

172
    def _checkId(self, id, allow_dup=0):
1✔
173
        """
174
        Override of ObjectManager._checkId().
175

176
        Allows the user to create objects with IDs that match the ID of
177
        a skin object.
178
        """
179
        superCheckId = SkinnableObjectManager.inheritedAttribute('_checkId')
×
180
        if not allow_dup:
×
181
            # Temporarily disable skindata.
182
            # Note that this depends heavily on Zope's current thread
183
            # behavior.
184
            tid = get_ident()
×
185
            sd = SKINDATA.get(tid)
×
186
            if sd is not None:
×
187
                del SKINDATA[tid]
×
188
            try:
×
189
                base = getattr(self, 'aq_base', self)
×
190
                if not hasattr(base, id):
×
191
                    # Cause _checkId to not check for duplication.
192
                    return superCheckId(self, id, allow_dup=1)
×
193
            finally:
194
                if sd is not None:
×
195
                    SKINDATA[tid] = sd
×
196
        return superCheckId(self, id, allow_dup)
×
197

198

199
InitializeClass(SkinnableObjectManager)
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