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

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

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

85.02
/src/OFS/SimpleItem.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 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
"""This module implements a simple item mix-in for objects that have a
1✔
14
very simple (e.g. one-screen) management interface, like documents,
15
Aqueduct database adapters, etc.
16

17
This module can also be used as a simple template for implementing new
18
item types.
19
"""
20

21
import logging
1✔
22
import re
1✔
23
import sys
1✔
24

25
from AccessControl.class_init import InitializeClass
1✔
26
from AccessControl.Permissions import access_contents_information
1✔
27
from AccessControl.Permissions import view as View
1✔
28
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
29
from Acquisition import Acquired
1✔
30
from Acquisition import Implicit
1✔
31
from Acquisition import aq_acquire
1✔
32
from Acquisition import aq_base
1✔
33
from Acquisition import aq_inner
1✔
34
from Acquisition import aq_parent
1✔
35
from App.Management import Navigation
1✔
36
from App.Management import Tabs
1✔
37
from App.special_dtml import HTML
1✔
38
from App.special_dtml import DTMLFile
1✔
39
from ComputedAttribute import ComputedAttribute
1✔
40
from DocumentTemplate.html_quote import html_quote
1✔
41
from DocumentTemplate.ustr import ustr
1✔
42
from ExtensionClass import Base
1✔
43
from OFS.CopySupport import CopySource
1✔
44
from OFS.interfaces import IItem
1✔
45
from OFS.interfaces import IItemWithName
1✔
46
from OFS.interfaces import ISimpleItem
1✔
47
from OFS.Lockable import LockableItem
1✔
48
from OFS.owner import Owned
1✔
49
from OFS.role import RoleManager
1✔
50
from OFS.Traversable import Traversable
1✔
51
from Persistence import Persistent
1✔
52
from webdav.Resource import Resource
1✔
53
from zExceptions import Redirect
1✔
54
from zExceptions.ExceptionFormatter import format_exception
1✔
55
from zope.interface import implementer
1✔
56
from ZPublisher.HTTPRequest import default_encoding
1✔
57

58

59
logger = logging.getLogger()
1✔
60

61

62
class PathReprProvider(Base):
1✔
63
    """Provides a representation that includes the physical path.
64

65
    Should be in the MRO before persistent.Persistent as this provides an own
66
    implementation of `__repr__` that includes information about connection and
67
    oid.
68
    """
69

70
    def __repr__(self):
1✔
71
        """Show the physical path of the object and context if available."""
72
        try:
1✔
73
            path = '/'.join(self.getPhysicalPath())
1✔
74
        except Exception:
×
75
            return super().__repr__()
×
76
        context_path = None
1✔
77
        context = aq_parent(self)
1✔
78
        container = aq_parent(aq_inner(self))
1✔
79
        if aq_base(context) is not aq_base(container):
1✔
80
            try:
1✔
81
                context_path = '/'.join(context.getPhysicalPath())
1✔
82
            except Exception:
×
83
                context_path = None
×
84
        res = '<%s' % self.__class__.__name__
1✔
85
        res += ' at %s' % path
1✔
86
        if context_path:
1✔
87
            res += ' used for %s' % context_path
1✔
88
        res += '>'
1✔
89
        return res
1✔
90

91

92
@implementer(IItem)
1✔
93
class Item(
1✔
94
    PathReprProvider,
95
    Base,
96
    Navigation,
97
    Resource,
98
    LockableItem,
99
    CopySource,
100
    Tabs,
101
    Traversable,
102
    Owned
103
):
104
    """A common base class for simple, non-container objects."""
105

106
    zmi_icon = 'far fa-file'
1✔
107
    zmi_show_add_dialog = True
1✔
108

109
    security = ClassSecurityInfo()
1✔
110

111
    isPrincipiaFolderish = 0
1✔
112
    isTopLevelPrincipiaApplicationObject = 0
1✔
113

114
    manage_options = ({'label': 'Interfaces', 'action': 'manage_interfaces'},)
1✔
115

116
    def manage_afterAdd(self, item, container):
1✔
117
        pass
1✔
118
    manage_afterAdd.__five_method__ = True
1✔
119

120
    def manage_beforeDelete(self, item, container):
1✔
121
        pass
1✔
122
    manage_beforeDelete.__five_method__ = True
1✔
123

124
    def manage_afterClone(self, item):
1✔
125
        pass
1✔
126
    manage_afterClone.__five_method__ = True
1✔
127

128
    # Direct use of the 'id' attribute is deprecated - use getId()
129
    id = ''
1✔
130

131
    @security.public
1✔
132
    def getId(self):
1✔
133
        """Return the id of the object as a string.
134

135
        This method should be used in preference to accessing an id attribute
136
        of an object directly. The getId method is public.
137
        """
138
        name = self.id
1✔
139
        if name is not None:
1!
140
            return name
1✔
141
        return self.__name__
×
142

143
    # Alias id to __name__, which will make tracebacks a good bit nicer:
144
    __name__ = ComputedAttribute(lambda self: self.id)
1✔
145

146
    # Meta type used for selecting all objects of a given type.
147
    meta_type = 'simple item'
1✔
148

149
    # Default title.
150
    title = ''
1✔
151

152
    # Default propertysheet info:
153
    __propsets__ = ()
1✔
154

155
    # Attributes that must be acquired
156
    REQUEST = Acquired
1✔
157

158
    # Allow (reluctantly) access to unprotected attributes
159
    __allow_access_to_unprotected_subobjects__ = 1
1✔
160

161
    def title_or_id(self):
1✔
162
        """Return the title if it is not blank and the id otherwise.
163
        """
164
        title = self.title
1✔
165
        if callable(title):
1!
166
            title = title()
×
167
        if title:
1✔
168
            return title
1✔
169
        return self.getId()
1✔
170

171
    def title_and_id(self):
1✔
172
        """Return the title if it is not blank and the id otherwise.
173

174
        If the title is not blank, then the id is included in parens.
175
        """
176
        title = self.title
1✔
177
        if callable(title):
1!
178
            title = title()
×
179
        id = self.getId()
1✔
180
        # Make sure we don't blindly concatenate encoded and unencoded data
181
        if title and type(id) is not type(title):
1✔
182
            if isinstance(id, bytes):
1!
183
                id = id.decode(default_encoding)
1✔
184
            if isinstance(title, bytes):
1!
185
                title = title.decode(default_encoding)
×
186
        return title and f"{title} ({id})" or id
1✔
187

188
    def this(self):
1✔
189
        # Handy way to talk to ourselves in document templates.
190
        return self
1✔
191

192
    def tpURL(self):
1✔
193
        # My URL as used by tree tag
194
        return self.getId()
1✔
195

196
    def tpValues(self):
1✔
197
        # My sub-objects as used by the tree tag
198
        return ()
1✔
199

200
    _manage_editedDialog = DTMLFile('dtml/editedDialog', globals())
1✔
201

202
    def manage_editedDialog(self, REQUEST, **args):
1✔
203
        return self._manage_editedDialog(self, REQUEST, **args)
×
204

205
    def raise_standardErrorMessage(
1✔
206
        self,
207
        client=None,
208
        REQUEST={},
209
        error_type=None,
210
        error_value=None,
211
        tb=None,
212
        error_tb=None,
213
        error_message='',
214
        tagSearch=re.compile(r'[a-zA-Z]>').search,
215
        error_log_url=''
216
    ):
217

218
        try:
1✔
219
            if error_type is None:
1✔
220
                error_type = sys.exc_info()[0]
1✔
221
            if error_value is None:
1✔
222
                error_value = sys.exc_info()[1]
1✔
223

224
            # allow for a few different traceback options
225
            if tb is None and error_tb is None:
1!
226
                tb = sys.exc_info()[2]
1✔
227
            if not isinstance(tb, str) and (error_tb is None):
1!
228
                error_tb = pretty_tb(error_type, error_value, tb)
1✔
229
            elif isinstance(tb, str) and not error_tb:
×
230
                error_tb = tb
×
231

232
            if hasattr(self, '_v_eek'):
1!
233
                # Stop if there is recursion.
234
                raise error_value.with_traceback(tb)
×
235
            self._v_eek = 1
1✔
236

237
            if hasattr(error_type, '__name__'):
1✔
238
                error_name = error_type.__name__
1✔
239
            else:
240
                error_name = 'Unknown'
1✔
241

242
            if not error_message:
1!
243
                try:
1✔
244
                    s = ustr(error_value)
1✔
245
                except Exception:
×
246
                    s = error_value
×
247
                try:
1✔
248
                    match = tagSearch(s)
1✔
249
                except TypeError:
×
250
                    match = None
×
251
                if match is not None:
1✔
252
                    error_message = error_value
1✔
253

254
            if client is None:
1✔
255
                client = self
1✔
256

257
            if not REQUEST:
1!
258
                REQUEST = aq_acquire(self, 'REQUEST')
×
259

260
            try:
1✔
261
                s = aq_acquire(client, 'standard_error_message')
1✔
262

263
                # For backward compatibility, we pass 'error_name' as
264
                # 'error_type' here as historically this has always
265
                # been a string.
266
                kwargs = {
1✔
267
                    'error_type': error_name,
268
                    'error_value': error_value,
269
                    'error_tb': error_tb,
270
                    'error_traceback': error_tb,
271
                    'error_message': error_message,
272
                    'error_log_url': error_log_url,
273
                }
274

275
                if getattr(aq_base(s), 'isDocTemp', 0):
1✔
276
                    v = s(client, REQUEST, **kwargs)
1✔
277
                elif callable(s):
1!
278
                    v = s(**kwargs)
1✔
279
                else:
280
                    v = HTML.__call__(s, client, REQUEST, **kwargs)
×
281
            except Exception:
1✔
282
                logger.error(
1✔
283
                    'Exception while rendering an error message',
284
                    exc_info=True
285
                )
286
                try:
1✔
287
                    strv = repr(error_value)  # quotes tainted strings
1✔
288
                except Exception:
×
289
                    strv = ('<unprintable %s object>' %
×
290
                            str(type(error_value).__name__))
291
                v = strv + (
1✔
292
                    (" (Also, the following error occurred while attempting "
293
                     "to render the standard error message, please see the "
294
                     "event log for full details: %s)") % (
295
                        html_quote(sys.exc_info()[1]),
296
                    ))
297

298
            # If we've been asked to handle errors, just return the rendered
299
            # exception and let the ZPublisher Exception Hook deal with it.
300
            return error_type, v, tb
1✔
301
        finally:
302
            if hasattr(self, '_v_eek'):
1!
303
                del self._v_eek
1✔
304
            tb = None
1!
305

306
    def manage(self, URL1):
1✔
307
        """
308
        """
309
        raise Redirect("%s/manage_main" % URL1)
×
310

311
    # This keeps simple items from acquiring their parents
312
    # objectValues, etc., when used in simple tree tags.
313
    def objectValues(self, spec=None):
1✔
314
        return ()
1✔
315
    objectIds = objectItems = objectValues
1✔
316

317
    def __len__(self):
1✔
318
        return 1
1✔
319

320
    @security.protected(access_contents_information)
1✔
321
    def getParentNode(self):
1✔
322
        """The parent of this node.  All nodes except Document
323
        DocumentFragment and Attr may have a parent"""
324
        return getattr(self, '__parent__', None)
×
325

326

327
InitializeClass(Item)
1✔
328

329

330
@implementer(IItemWithName)
1✔
331
class Item_w__name__(Item):
1✔
332
    """Mixin class to support common name/id functions"""
333

334
    def getId(self):
1✔
335
        """Return the id of the object as a string.
336
        """
337
        return self.__name__
1✔
338

339
    # Alias (deprecated) `id` to `getId()` (but avoid recursion)
340
    id = ComputedAttribute(
1✔
341
        lambda self: self.getId() if "__name__" in self.__dict__ else "")
342

343
    def title_or_id(self):
1✔
344
        """Return the title if it is not blank and the id otherwise.
345
        """
346
        return self.title or self.__name__
1✔
347

348
    def title_and_id(self):
1✔
349
        """Return the title if it is not blank and the id otherwise.
350

351
        If the title is not blank, then the id is included in parens.
352
        """
353
        t = self.title
×
354
        return t and f"{t} ({self.__name__})" or self.__name__
×
355

356
    def _setId(self, id):
1✔
357
        self.__name__ = id
1✔
358

359
    def getPhysicalPath(self):
1✔
360
        # Get the physical path of the object.
361
        #
362
        # Returns a path (an immutable sequence of strings) that can be used
363
        # to access this object again later, for example in a copy/paste
364
        # operation.  getPhysicalRoot() and getPhysicalPath() are designed to
365
        # operate together.
366

367
        path = (self.__name__, )
1✔
368
        p = aq_parent(aq_inner(self))
1✔
369
        if p is not None:
1!
370
            path = p.getPhysicalPath() + path
1✔
371
        return path
1✔
372

373

374
def pretty_tb(t, v, tb, as_html=1):
1✔
375
    tb = format_exception(t, v, tb, as_html=as_html)
1✔
376
    tb = '\n'.join(tb)
1✔
377
    return tb
1✔
378

379

380
@implementer(ISimpleItem)
1✔
381
class SimpleItem(
1✔
382
    Item,
383
    Persistent,
384
    Implicit,
385
    RoleManager,
386
):
387
    """Mix-in class combining the most common set of basic mix-ins
388
    """
389

390
    security = ClassSecurityInfo()
1✔
391
    security.setPermissionDefault(View, ('Manager',))
1✔
392

393
    manage_options = Item.manage_options + (
1✔
394
        {
395
            'label': 'Security',
396
            'action': 'manage_access',
397
        },
398
    )
399

400

401
InitializeClass(SimpleItem)
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