Beispiel #1
0
class PLexicon(Lexicon, Implicit, SimpleItem):
    """Lexicon for ZCTextIndex.
    """

    implements(IZCLexicon)

    meta_type = 'ZCTextIndex Lexicon'

    manage_options = (
        {
            'label': 'Overview',
            'action': 'manage_main'
        },
        {
            'label': 'Query',
            'action': 'queryLexicon'
        },
    ) + SimpleItem.manage_options

    security = ClassSecurityInfo()
    security.declareObjectProtected(LexiconQueryPerm)

    def __init__(self, id, title='', *pipeline):
        self.id = str(id)
        self.title = str(title)
        PLexicon.inheritedAttribute('__init__')(self, *pipeline)

    ## User Interface Methods ##

    def getPipelineNames(self):
        """Return list of names of pipeline element classes"""
        return [element.__class__.__name__ for element in self._pipeline]

    _queryLexicon = DTMLFile('dtml/queryLexicon', globals())

    security.declareProtected(LexiconQueryPerm, 'queryLexicon')

    def queryLexicon(self, REQUEST, words=None, page=0, rows=20, cols=4):
        """Lexicon browser/query user interface
        """
        if words:
            wids = []
            for word in self.parseTerms(words):
                wids.extend(self.globToWordIds(word))
            words = [self.get_word(wid) for wid in wids]
        else:
            words = self.words()

        word_count = len(words)
        rows = max(min(rows, 500), 1)
        cols = max(min(cols, 12), 1)
        page_count = word_count / (rows * cols) + \
                     (word_count % (rows * cols) > 0)
        page = max(min(page, page_count - 1), 0)
        start = rows * cols * page
        end = min(rows * cols * (page + 1), word_count)

        if word_count:
            words = list(words[start:end])
        else:
            words = []

        columns = []
        i = 0
        while i < len(words):
            columns.append(words[i:i + rows])
            i += rows

        info = dict(page=page,
                    rows=rows,
                    cols=cols,
                    start_word=start + 1,
                    end_word=end,
                    word_count=word_count,
                    page_count=page_count,
                    page_range=xrange(page_count),
                    page_columns=columns)

        if REQUEST is not None:
            return self._queryLexicon(self, REQUEST, **info)

        return info

    security.declareProtected(LexiconMgmtPerm, 'manage_main')
    manage_main = DTMLFile('dtml/manageLexicon', globals())
class DateRangeIndex(UnIndex):
    """Index for date ranges, such as the "effective-expiration" range in CMF.

    Any object may return None for either the start or the end date: for the
    start date, this should be the logical equivalent of "since the beginning
    of time"; for the end date, "until the end of time".

    Therefore, divide the space of indexed objects into four containers:

    - Objects which always match (i.e., they returned None for both);

    - Objects which match after a given time (i.e., they returned None for the
      end date);

    - Objects which match until a given time (i.e., they returned None for the
      start date);

    - Objects which match only during a specific interval.
    """

    security = ClassSecurityInfo()

    meta_type = 'DateRangeIndex'
    query_options = ('query', )

    manage_options = ({
        'label': 'Properties',
        'action': 'manage_indexProperties'
    }, )

    since_field = until_field = None

    # int(DateTime('1000/1/1 0:00 GMT-12').millis() / 1000 / 60)
    floor_value = -510162480
    # int(DateTime('2499/12/31 0:00 GMT+12').millis() / 1000 / 60)
    ceiling_value = 278751600
    # precision of indexed time interval in minutes
    precision_value = 1

    def __init__(self,
                 id,
                 since_field=None,
                 until_field=None,
                 caller=None,
                 extra=None,
                 floor_value=None,
                 ceiling_value=None,
                 precision_value=None):

        if extra:
            since_field = extra.since_field
            until_field = extra.until_field
            floor_value = getattr(extra, 'floor_value', None)
            ceiling_value = getattr(extra, 'ceiling_value', None)
            precision_value = getattr(extra, 'precision_value', None)

        self._setId(id)
        self._edit(since_field, until_field, floor_value, ceiling_value,
                   precision_value)
        self.clear()

    @security.protected(view)
    def getSinceField(self):
        """Get the name of the attribute indexed as start date.
        """
        return self._since_field

    @security.protected(view)
    def getUntilField(self):
        """Get the name of the attribute indexed as end date.
        """
        return self._until_field

    @security.protected(view)
    def getFloorValue(self):
        """ """
        return self.floor_value

    @security.protected(view)
    def getCeilingValue(self):
        """ """
        return self.ceiling_value

    @security.protected(view)
    def getPrecisionValue(self):
        """ """
        return self.precision_value

    manage_indexProperties = DTMLFile('manageDateRangeIndex', _dtmldir)

    @security.protected(manage_zcatalog_indexes)
    def manage_edit(self, since_field, until_field, floor_value, ceiling_value,
                    precision_value, REQUEST):
        """ """
        self._edit(since_field, until_field, floor_value, ceiling_value,
                   precision_value)
        REQUEST['RESPONSE'].redirect('{0}/manage_main'
                                     '?manage_tabs_message=Updated'.format(
                                         REQUEST.get('URL2')))

    @security.private
    def _edit(self,
              since_field,
              until_field,
              floor_value=None,
              ceiling_value=None,
              precision_value=None):
        """Update the fields used to compute the range.
        """
        self._since_field = since_field
        self._until_field = until_field
        if floor_value not in (None, ''):
            self.floor_value = int(floor_value)
        if ceiling_value not in (None, ''):
            self.ceiling_value = int(ceiling_value)
        if precision_value not in (None, ''):
            self.precision_value = int(precision_value)

    @security.protected(manage_zcatalog_indexes)
    def clear(self):
        """Start over fresh."""
        self._always = IITreeSet()
        self._since_only = IOBTree()
        self._until_only = IOBTree()
        self._since = IOBTree()
        self._until = IOBTree()
        self._unindex = IOBTree()  # 'datum' will be a tuple of date ints
        self._length = Length()
        if self._counter is None:
            self._counter = Length()
        else:
            self._increment_counter()

    def getEntryForObject(self, documentId, default=None):
        """Get all information contained for the specific object
        identified by 'documentId'.  Return 'default' if not found.
        """
        return self._unindex.get(documentId, default)

    def index_object(self, documentId, obj, threshold=None):
        """Index an object:
        - 'documentId' is the integer ID of the document
        - 'obj' is the object to be indexed
        - ignore threshold
        """
        if self._since_field is None:
            return 0

        since = getattr(obj, self._since_field, None)
        if safe_callable(since):
            since = since()
        since = self._convertDateTime(since)

        until = getattr(obj, self._until_field, None)
        if safe_callable(until):
            until = until()
        until = self._convertDateTime(until)

        datum = (since, until)

        old_datum = self._unindex.get(documentId, None)
        if datum == old_datum:  # No change?  bail out!
            return 0

        self._increment_counter()

        if old_datum is not None:
            old_since, old_until = old_datum
            self._removeForwardIndexEntry(old_since, old_until, documentId)

        self._insertForwardIndexEntry(since, until, documentId)
        self._unindex[documentId] = datum

        return 1

    def unindex_object(self, documentId):
        """Remove the object corresponding to 'documentId' from the index.
        """

        datum = self._unindex.get(documentId, None)
        if datum is None:
            return

        self._increment_counter()

        since, until = datum
        self._removeForwardIndexEntry(since, until, documentId)
        del self._unindex[documentId]

    def uniqueValues(self, name=None, withLengths=0):
        """Return a sequence of unique values for 'name'.

        If 'withLengths' is true, return a sequence of tuples, in
        the form '(value, length)'.
        """
        if name not in (self._since_field, self._until_field):
            return

        if name == self._since_field:
            sets = (self._since, self._since_only)
        else:
            sets = (self._until, self._until_only)

        if not withLengths:
            for s in sets:
                for key in s.keys():
                    yield key
        else:
            for s in sets:
                for key, value in s.items():
                    if isinstance(value, int):
                        yield (key, 1)
                    else:
                        yield (key, len(value))

    def getRequestCacheKey(self, record, resultset=None):
        term = self._convertDateTime(record.keys[0])
        tid = str(term)

        # unique index identifier
        iid = '_{0}_{1}_{2}'.format(self.__class__.__name__, self.id,
                                    self.getCounter())
        # record identifier
        if resultset is None:
            rid = '_{0}'.format(tid)
        else:
            rid = '_inverse_{0}'.format(tid)

        return (iid, rid)

    def _apply_index(self, request, resultset=None):
        record = IndexQuery(request, self.id, self.query_options,
                            self.operators, self.useOperator)
        if record.keys is None:
            return None
        return (self.query_index(record, resultset=resultset),
                (self._since_field, self._until_field))

    def query_index(self, record, resultset=None):
        cache = self.getRequestCache()
        if cache is not None:
            cachekey = self.getRequestCacheKey(record, resultset)
            cached = cache.get(cachekey, None)
            if cached is not None:
                if resultset is None:
                    return cached
                else:
                    return difference(resultset, cached)

        term = self._convertDateTime(record.keys[0])
        if resultset is None:
            # Aggregate sets for each bucket separately, to avoid
            # large-small union penalties.
            until_only = multiunion(self._until_only.values(term))
            since_only = multiunion(self._since_only.values(None, term))
            until = multiunion(self._until.values(term))
            since = multiunion(self._since.values(None, term))
            bounded = intersection(until, since)

            # Merge from smallest to largest.
            result = multiunion(
                [bounded, until_only, since_only, self._always])
            if cache is not None:
                cache[cachekey] = result

            return result
        else:
            # Compute the inverse and subtract from res
            until_only = multiunion(self._until_only.values(None, term - 1))
            since_only = multiunion(self._since_only.values(term + 1))
            until = multiunion(self._until.values(None, term - 1))
            since = multiunion(self._since.values(term + 1))

            result = multiunion([since, since_only, until_only, until])
            if cache is not None:
                cache[cachekey] = result

            return difference(resultset, result)

    def _insert_migrate(self, tree, key, value):
        treeset = tree.get(key, None)
        if treeset is None:
            tree[key] = IITreeSet((value, ))
        else:
            if isinstance(treeset, IITreeSet):
                treeset.insert(value)
            elif isinstance(treeset, int):
                tree[key] = IITreeSet((treeset, value))
            else:
                tree[key] = IITreeSet(treeset)
                tree[key].insert(value)

    def _insertForwardIndexEntry(self, since, until, documentId):
        """Insert 'documentId' into the appropriate set based on 'datum'.
        """
        if since is None and until is None:
            self._always.insert(documentId)
        elif since is None:
            self._insert_migrate(self._until_only, until, documentId)
        elif until is None:
            self._insert_migrate(self._since_only, since, documentId)
        else:
            self._insert_migrate(self._since, since, documentId)
            self._insert_migrate(self._until, until, documentId)

    def _remove_delete(self, tree, key, value):
        treeset = tree.get(key, None)
        if treeset is not None:
            if isinstance(treeset, int):
                del tree[key]
            else:
                treeset.remove(value)
                if not treeset:
                    del tree[key]

    def _removeForwardIndexEntry(self, since, until, documentId):
        """Remove 'documentId' from the appropriate set based on 'datum'.
        """
        if since is None and until is None:
            self._always.remove(documentId)
        elif since is None:
            self._remove_delete(self._until_only, until, documentId)
        elif until is None:
            self._remove_delete(self._since_only, since, documentId)
        else:
            self._remove_delete(self._since, since, documentId)
            self._remove_delete(self._until, until, documentId)

    def _convertDateTime(self, value):
        value = datetime_to_minutes(value, self.precision_value)

        if value is None:
            return None

        if (value > self.ceiling_value or value < self.floor_value):
            # handle values outside our specified range
            return None

        return value
Beispiel #3
0
class DTMLMethod(PathReprProvider, RestrictedDTML, HTML, Implicit, RoleManager,
                 Item_w__name__, Historical, Cacheable):
    """ DocumentTemplate.HTML objects that act as methods of their containers.
    """
    meta_type = 'DTML Method'
    zmi_icon = 'far fa-file-alt'
    _proxy_roles = ()
    index_html = None  # Prevent accidental acquisition
    _cache_namespace_keys = ()
    _locked_error_text = 'This DTML Method is locked.'

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    __code__ = Code()
    __code__.co_varnames = 'self', 'REQUEST', 'RESPONSE'
    __code__.co_argcount = 3
    __defaults__ = None

    manage_options = ((
        {
            'label': 'Edit',
            'action': 'manage_main',
        },
        {
            'label': 'View',
            'action': '',
        },
        {
            'label': 'Proxy',
            'action': 'manage_proxyForm',
        },
    ) + Historical.manage_options + RoleManager.manage_options +
                      Item_w__name__.manage_options + Cacheable.manage_options)

    # Careful in permission changes--used by DTMLDocument!
    security.declareProtected(
        change_dtml_methods,  # NOQA: D001
        'manage_historyCopy')
    security.declareProtected(
        change_dtml_methods,  # NOQA: D001
        'manage_beforeHistoryCopy')
    security.declareProtected(
        change_dtml_methods,  # NOQA: D001
        'manage_afterHistoryCopy')

    # More reasonable default for content-type for http HEAD requests.
    default_content_type = 'text/html'

    def errQuote(self, s):
        # Quoting is done when rendering the error in the template.
        return s

    @security.protected(View)
    def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        """Render using the given client object

        o If client is not passed, we are being called as a sub-template:
          don't do any error propagation.

        o If supplied, use the REQUEST mapping, Response, and key word
        arguments.
        """
        if not self._cache_namespace_keys:
            data = self.ZCacheable_get(default=_marker)
            if data is not _marker:
                if IStreamIterator.isImplementedBy(data) and \
                   RESPONSE is not None:
                    # This is a stream iterator and we need to set some
                    # headers now before giving it to medusa
                    headers_get = RESPONSE.headers.get

                    if headers_get('content-length', None) is None:
                        RESPONSE.setHeader('content-length', len(data))

                    if headers_get('content-type', None) is None and \
                       headers_get('Content-type', None) is None:
                        ct = (self.__dict__.get('content_type')
                              or self.default_content_type)
                        RESPONSE.setHeader('content-type', ct)

                # Return cached results.
                return data

        __traceback_supplement__ = (PathTracebackSupplement, self)
        kw['document_id'] = self.getId()
        kw['document_title'] = self.title

        security = getSecurityManager()
        security.addContext(self)
        if 'validate' in self.__dict__:
            first_time_through = 0
        else:
            self.__dict__['validate'] = security.DTMLValidate
            first_time_through = 1
        try:

            if client is None:
                # Called as subtemplate, so don't need error propagation!
                r = HTML.__call__(self, client, REQUEST, **kw)
                if RESPONSE is None:
                    result = r
                else:
                    result = decapitate(r, RESPONSE)
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(result)
                return result

            r = HTML.__call__(self, client, REQUEST, **kw)

            if RESPONSE is None or not isinstance(r, str):
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(r)
                return r

        finally:
            security.removeContext(self)
            if first_time_through:
                del self.__dict__['validate']

        have_key = RESPONSE.headers.__contains__
        if not (have_key('content-type') or have_key('Content-Type')):
            if 'content_type' in self.__dict__:
                c = self.content_type
            else:
                encoding = getattr(self, 'encoding', default_encoding)
                if PY2 and not isinstance(r, text_type):
                    # Prevent double-encoding edge cases under Python 2
                    r = r.decode(encoding)
                c, e = guess_content_type(self.getId(), r.encode(encoding))
            RESPONSE.setHeader('Content-Type', c)
        result = decapitate(r, RESPONSE)
        if not self._cache_namespace_keys:
            self.ZCacheable_set(result)
        return result

    def validate(self, inst, parent, name, value, md=None):
        return getSecurityManager().validate(inst, parent, name, value)

    def ZDocumentTemplate_beforeRender(self, md, default):
        # Tries to get a cached value.
        if self._cache_namespace_keys:
            # Use the specified keys from the namespace to identify a
            # cache entry.
            kw = {}
            for key in self._cache_namespace_keys:
                try:
                    val = md[key]
                except Exception:
                    val = None
                kw[key] = val
            return self.ZCacheable_get(keywords=kw, default=default)
        return default

    def ZDocumentTemplate_afterRender(self, md, result):
        # Tries to set a cache value.
        if self._cache_namespace_keys:
            kw = {}
            for key in self._cache_namespace_keys:
                try:
                    val = md[key]
                except Exception:
                    val = None
                kw[key] = val
            self.ZCacheable_set(result, keywords=kw)

    security.declareProtected(change_dtml_methods,
                              'ZCacheable_configHTML')  # NOQA: D001,E501
    ZCacheable_configHTML = DTMLFile('dtml/cacheNamespaceKeys', globals())

    @security.protected(change_dtml_methods)
    def getCacheNamespaceKeys(self):
        # Return the cacheNamespaceKeys.
        return self._cache_namespace_keys

    @security.protected(change_dtml_methods)
    def setCacheNamespaceKeys(self, keys, REQUEST=None):
        # Set the list of names looked up to provide a cache key.
        ks = []
        for key in keys:
            key = str(key).strip()
            if key:
                ks.append(key)
        self._cache_namespace_keys = tuple(ks)

        if REQUEST is not None:
            return self.ZCacheable_manage(self, REQUEST)

    @security.protected(View)
    def get_size(self):
        return len(self.raw)

    # deprecated; use get_size!
    getSize = get_size

    security.declareProtected(change_dtml_methods, 'manage')  # NOQA: D001

    security.declareProtected(change_dtml_methods,
                              'manage_editForm')  # NOQA: D001,E501
    manage_editForm = DTMLFile('dtml/documentEdit', globals())
    manage_editForm._setName('manage_editForm')

    # deprecated!
    manage_uploadForm = manage_editForm

    security.declareProtected(change_dtml_methods, 'manage_main')  # NOQA: D001
    manage = manage_main = manage_editDocument = manage_editForm

    security.declareProtected(change_proxy_roles,
                              'manage_proxyForm')  # NOQA: D001,E501
    manage_proxyForm = DTMLFile('dtml/documentProxy', globals())

    @security.protected(change_dtml_methods)
    def manage_edit(self, data, title, SUBMIT='Change', REQUEST=None):
        """ Replace contents with 'data', title with 'title'.
        """
        self._validateProxy(REQUEST)
        if self.wl_isLocked():
            raise ResourceLockedError(self._locked_error_text)

        self.title = str(title)
        if isinstance(data, TaintedString):
            data = data.quoted()

        if hasattr(data, 'read'):
            data = data.read()
        try:
            self.munge(data)
        except ParseError as e:
            if REQUEST:
                return self.manage_main(self,
                                        REQUEST,
                                        manage_tabs_message=e,
                                        manage_tabs_type='warning')
            else:
                raise
        self.ZCacheable_invalidate()
        if REQUEST:
            message = "Saved changes."
            return self.manage_main(self, REQUEST, manage_tabs_message=message)

    @security.protected(change_dtml_methods)
    def manage_upload(self, file='', REQUEST=None):
        """ Replace the contents of the document with the text in 'file'.

        Store `file` as a native `str`.
        """
        self._validateProxy(REQUEST)
        if self.wl_isLocked():
            if REQUEST is not None:
                return self.manage_main(
                    self,
                    REQUEST,
                    manage_tabs_message=self._locked_error_text,
                    manage_tabs_type='warning')
            raise ResourceLockedError(self._locked_error_text)

        if REQUEST is not None and not file:
            return self.manage_main(self,
                                    REQUEST,
                                    manage_tabs_message='No file specified',
                                    manage_tabs_type='warning')

        self.munge(safe_file_data(file))
        self.ZCacheable_invalidate()
        if REQUEST is not None:
            message = "Content uploaded."
            return self.manage_main(self, REQUEST, manage_tabs_message=message)

    def manage_haveProxy(self, r):
        return r in self._proxy_roles

    def _validateProxy(self, request, roles=None):
        if roles is None:
            roles = self._proxy_roles
        if not roles:
            return
        user = u = getSecurityManager().getUser()
        user = user.allowed
        for r in roles:
            if r and not user(self, (r, )):
                user = None
                break

        if user is not None:
            return

        raise Forbidden(
            'You are not authorized to change <em>%s</em> because you '
            'do not have proxy roles.\n<!--%s, %s-->' %
            (self.__name__, u, roles))

    @security.protected(change_proxy_roles)
    @requestmethod('POST')
    def manage_proxy(self, roles=(), REQUEST=None):
        "Change Proxy Roles"
        self._validateProxy(REQUEST, roles)
        self._validateProxy(REQUEST)
        self._proxy_roles = tuple(roles)
        if REQUEST:
            message = "Saved changes."
            return self.manage_proxyForm(self,
                                         REQUEST,
                                         manage_tabs_message=message)

    @security.protected(view_management_screens)
    def PrincipiaSearchSource(self):
        # Support for searching - the document's contents are searched.
        return self.read()

    @security.protected(view_management_screens)
    def document_src(self, REQUEST=None, RESPONSE=None):
        # Return unprocessed document source.
        if RESPONSE is not None:
            RESPONSE.setHeader('Content-Type', 'text/plain')
        return self.read()

    def manage_historyCompare(self,
                              rev1,
                              rev2,
                              REQUEST,
                              historyComparisonResults=''):
        return DTMLMethod.inheritedAttribute('manage_historyCompare')(
            self,
            rev1,
            rev2,
            REQUEST,
            historyComparisonResults=html_diff(rev1.read(), rev2.read()))

    if bbb.HAS_ZSERVER:

        @security.protected(change_dtml_methods)
        def PUT(self, REQUEST, RESPONSE):
            """ Handle FTP / HTTP PUT requests.
            """
            self.dav__init(REQUEST, RESPONSE)
            self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
            body = REQUEST.get('BODY', '')
            self._validateProxy(REQUEST)
            self.munge(body)
            self.ZCacheable_invalidate()
            RESPONSE.setStatus(204)
            return RESPONSE

        @security.protected(ftp_access)
        def manage_FTPget(self):
            """ Get source for FTP download.
            """
            return self.read()
Beispiel #4
0
class FSDTMLMethod(RestrictedDTML, RoleManager, FSObject, HTML):

    """FSDTMLMethods act like DTML methods but are not directly
    modifiable from the management interface.
    """

    meta_type = 'Filesystem DTML Method'
    _owner = None
    _proxy_roles = ()
    _cache_namespace_keys = ()
    _reading = 0

    manage_options = (
        {'label': 'Customize', 'action': 'manage_main'},
        {'label': 'View', 'action': ''},
        {'label': 'Proxy', 'action': 'manage_proxyForm'})

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    security.declareProtected(ViewManagementScreens, 'manage_main')
    manage_main = DTMLFile('custdtml', _dtmldir)

    def __init__(self, id, filepath, fullname=None, properties=None):
        FSObject.__init__(self, id, filepath, fullname, properties)
        # Normally called via HTML.__init__ but we don't need the rest that
        # happens there.
        self.initvars(None, {})

    def _createZODBClone(self):
        """Create a ZODB (editable) equivalent of this object."""
        return DTMLMethod(self.read(), __name__=self.getId())

    def _readFile(self, reparse):
        """Read the data from the filesystem.
        """
        file = open(self._filepath, 'r') # not 'rb', as this is a text file!
        try:
            data = file.read()
        finally:
            file.close()
        self.raw = data

        if reparse:
            self._reading = 1  # Avoid infinite recursion
            try:
                self.cook()
            finally:
                self._reading = 0

    # Hook up chances to reload in debug mode
    @security.private
    def read_raw(self):
        if not self._reading:
            self._updateFromFS()
        return HTML.read_raw(self)

    #### The following is mainly taken from OFS/DTMLMethod.py ###

    index_html = None # Prevent accidental acquisition

    # Documents masquerade as functions:
    __code__ = DTMLMethod.__code__

    default_content_type = 'text/html'

    def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        """Render the document given a client object, REQUEST mapping,
        Response, and key word arguments."""

        self._updateFromFS()

        kw['document_id'] = self.getId()
        kw['document_title'] = self.title

        if client is not None:
            if _checkConditionalGET(self, kw):
                return ''

        if not self._cache_namespace_keys:
            data = self.ZCacheable_get(default=_marker)
            if data is not _marker:
                # Return cached results.
                return data

        __traceback_info__ = self._filepath
        security = getSecurityManager()
        security.addContext(self)
        try:
            r = HTML.__call__(self, client, REQUEST, **kw)

            if client is None:
                # Called as subtemplate, so don't need error propagation!
                if RESPONSE is None:
                    result = r
                else:
                    result = decapitate(r, RESPONSE)
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(result)
                return result

            if not isinstance(r, six.string_types) or RESPONSE is None:
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(r)
                return r

        finally:
            security.removeContext(self)

        headers = RESPONSE.headers
        if not ('content-type' in headers or 'Content-Type' in headers):
            if 'content_type' in self.__dict__:
                c = self.content_type
            else:
                if six.PY2:
                    c, _e = guess_content_type(self.getId(), r)
                else:
                    c, _e = guess_content_type(self.getId(), r.encode())

            RESPONSE.setHeader('Content-Type', c)
        if RESPONSE is not None:
            # caching policy manager hook
            _setCacheHeaders(self, {})
        result = decapitate(r, RESPONSE)
        if not self._cache_namespace_keys:
            self.ZCacheable_set(result)
        return result

    def getCacheNamespaceKeys(self):
        '''
        Returns the cacheNamespaceKeys.
        '''
        return self._cache_namespace_keys

    def setCacheNamespaceKeys(self, keys, REQUEST=None):
        '''
        Sets the list of names that should be looked up in the
        namespace to provide a cache key.
        '''
        ks = []
        for key in keys:
            key = str(key).strip()
            if key:
                ks.append(key)
        self._cache_namespace_keys = tuple(ks)
        if REQUEST is not None:
            return self.ZCacheable_manage(self, REQUEST)

    # Zope 2.3.x way:
    def validate(self, inst, parent, name, value, md=None):
        return getSecurityManager().validate(inst, parent, name, value)

    if bbb.HAS_ZSERVER:
        security.declareProtected(FTPAccess, 'manage_FTPget')
        manage_FTPget = get_unbound_function(DTMLMethod.manage_FTPget)

    security.declareProtected(ViewManagementScreens, 'PrincipiaSearchSource')
    PrincipiaSearchSource = get_unbound_function(DTMLMethod.PrincipiaSearchSource)

    security.declareProtected(ViewManagementScreens, 'document_src')
    document_src = get_unbound_function(DTMLMethod.document_src)

    security.declareProtected(ViewManagementScreens, 'manage_haveProxy')
    manage_haveProxy = get_unbound_function(DTMLMethod.manage_haveProxy)
Beispiel #5
0
class MembershipTool(UniqueObject, Folder):
    """ This tool accesses member data through an acl_users object.

    It can be replaced with something that accesses member data in a
    different way.
    """

    implements(IMembershipTool)

    id = 'portal_membership'
    meta_type = 'CMF Membership Tool'
    memberareaCreationFlag = 1

    security = ClassSecurityInfo()

    manage_options = (({
        'label': 'Configuration',
        'action': 'manage_mapRoles'
    }, ) + ({
        'label': 'Overview',
        'action': 'manage_overview'
    }, ) + Folder.manage_options)

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainMembershipTool', _dtmldir)

    #
    #   'portal_membership' interface methods
    #
    security.declareProtected(ManagePortal, 'manage_mapRoles')
    manage_mapRoles = DTMLFile('membershipRolemapping', _dtmldir)

    security.declareProtected(SetOwnPassword, 'setPassword')

    @postonly
    def setPassword(self, password, domains=None, REQUEST=None):
        '''Allows the authenticated member to set his/her own password.
        '''
        # XXX: this method violates the rules for tools/utilities:
        # it depends on a non-utility tool
        registration = getToolByName(self, 'portal_registration', None)
        if not self.isAnonymousUser():
            member = self.getAuthenticatedMember()
            if registration:
                failMessage = registration.testPasswordValidity(password)
                if failMessage is not None:
                    raise BadRequest(failMessage)
            member.setSecurityProfile(password=password, domains=domains)
        else:
            raise BadRequest('Not logged in.')

    security.declarePublic('getAuthenticatedMember')

    def getAuthenticatedMember(self):
        '''
        Returns the currently authenticated member object
        or the Anonymous User.  Never returns None.
        '''
        u = _getAuthenticatedUser(self)
        if u is None:
            u = nobody
        return self.wrapUser(u)

    security.declarePrivate('wrapUser')

    def wrapUser(self, u, wrap_anon=0):
        """ Set up the correct acquisition wrappers for a user object.

        Provides an opportunity for a portal_memberdata tool to retrieve and
        store member data independently of the user object.
        """
        # XXX: this method violates the rules for tools/utilities:
        # it depends on a non-utility tool
        b = getattr(u, 'aq_base', None)
        if b is None:
            # u isn't wrapped at all.  Wrap it in self.acl_users.
            b = u
            u = u.__of__(self.acl_users)
        if (b is nobody and not wrap_anon) or hasattr(b, 'getMemberId'):
            # This user is either not recognized by acl_users or it is
            # already registered with something that implements the
            # member data tool at least partially.
            return u

        # Apply any role mapping if we have it
        if hasattr(self, 'role_map'):
            for portal_role in self.role_map.keys():
                if (self.role_map.get(portal_role) in u.roles
                        and portal_role not in u.roles):
                    u.roles.append(portal_role)

        mdtool = getToolByName(self, 'portal_memberdata', None)
        if mdtool is not None:
            try:
                u = mdtool.wrapUser(u)
            except ConflictError:
                raise
            except:
                logger.exception("Error during wrapUser")
        return u

    security.declareProtected(ManagePortal, 'getPortalRoles')

    def getPortalRoles(self):
        """
        Return all local roles defined by the portal itself,
        which means roles that are useful and understood
        by the portal object
        """
        parent = self.aq_inner.aq_parent
        roles = list(parent.userdefined_roles())

        # This is *not* a local role in the portal but used by it
        roles.append('Manager')
        roles.append('Owner')

        return roles

    security.declareProtected(ManagePortal, 'setRoleMapping')

    @postonly
    def setRoleMapping(self, portal_role, userfolder_role, REQUEST=None):
        """
        set the mapping of roles between roles understood by
        the portal and roles coming from outside user sources
        """
        if not hasattr(self, 'role_map'): self.role_map = PersistentMapping()

        if len(userfolder_role) < 1:
            del self.role_map[portal_role]
        else:
            self.role_map[portal_role] = userfolder_role

        return MessageDialog(title='Mapping updated',
                             message='The Role mappings have been updated',
                             action='manage_mapRoles')

    security.declareProtected(ManagePortal, 'getMappedRole')

    def getMappedRole(self, portal_role):
        """
        returns a role name if the portal role is mapped to
        something else or an empty string if it is not
        """
        if hasattr(self, 'role_map'):
            return self.role_map.get(portal_role, '')
        else:
            return ''

    security.declarePublic('getMembersFolder')

    def getMembersFolder(self):
        """ Get the members folder object.
        """
        parent = aq_parent(aq_inner(self))
        members = getattr(parent, 'Members', None)
        return members

    security.declareProtected(ManagePortal, 'getMemberareaCreationFlag')

    def getMemberareaCreationFlag(self):
        """
        Returns the flag indicating whether the membership tool
        will create a member area if an authenticated user from
        an underlying user folder logs in first without going
        through the join process
        """
        return self.memberareaCreationFlag

    security.declareProtected(ManagePortal, 'setMemberareaCreationFlag')

    def setMemberareaCreationFlag(self):
        """
        sets the flag indicating whether the membership tool
        will create a member area if an authenticated user from
        an underlying user folder logs in first without going
        through the join process
        """
        if not hasattr(self, 'memberareaCreationFlag'):
            self.memberareaCreationFlag = 0

        if self.memberareaCreationFlag == 0:
            self.memberareaCreationFlag = 1
        else:
            self.memberareaCreationFlag = 0

        return MessageDialog(
            title='Member area creation flag changed',
            message='Member area creation flag has been updated',
            action='manage_mapRoles')

    security.declarePublic('createMemberArea')

    def createMemberArea(self, member_id=''):
        """ Create a member area for 'member_id' or authenticated user.
        """
        if not self.getMemberareaCreationFlag():
            return None
        members = self.getMembersFolder()
        if members is None:
            return None
        if self.isAnonymousUser():
            return None
        # Note: We can't use getAuthenticatedMember() and getMemberById()
        # because they might be wrapped by MemberDataTool.
        user = _getAuthenticatedUser(self)
        user_id = user.getId()
        if member_id in ('', user_id):
            member = user
            member_id = user_id
        else:
            if _checkPermission(ManageUsers, self):
                uf = self._huntUserFolder(member_id, self)
                if uf:
                    member = uf.getUserById(member_id).__of__(uf)
                else:
                    raise ValueError('Member %s does not exist' % member_id)
            else:
                return None
        if hasattr(aq_base(members), member_id):
            return None
        else:
            f_title = "%s's Home" % member_id
            members.manage_addPortalFolder(id=member_id, title=f_title)
            f = getattr(members, member_id)

            f.manage_permission(View, ['Owner', 'Manager', 'Reviewer'], 0)
            f.manage_permission(AccessContentsInformation,
                                ['Owner', 'Manager', 'Reviewer'], 0)

            # Grant Ownership and Owner role to Member
            f.changeOwnership(member)
            f.__ac_local_roles__ = None
            f.manage_setLocalRoles(member_id, ['Owner'])
        return f

    security.declarePublic('createMemberarea')
    createMemberarea = createMemberArea

    security.declareProtected(ManageUsers, 'deleteMemberArea')

    @postonly
    def deleteMemberArea(self, member_id, REQUEST=None):
        """ Delete member area of member specified by member_id.
        """
        members = self.getMembersFolder()
        if not members:
            return 0
        if hasattr(aq_base(members), member_id):
            members.manage_delObjects(member_id)
            return 1
        else:
            return 0

    security.declarePublic('isAnonymousUser')

    def isAnonymousUser(self):
        '''
        Returns 1 if the user is not logged in.
        '''
        u = _getAuthenticatedUser(self)
        if u is None or u.getUserName() == 'Anonymous User':
            return 1
        return 0

    security.declarePublic('checkPermission')

    def checkPermission(self, permissionName, object, subobjectName=None):
        '''
        Checks whether the current user has the given permission on
        the given object or subobject.
        '''
        if subobjectName is not None:
            object = getattr(object, subobjectName)
        return _checkPermission(permissionName, object)

    security.declarePublic('credentialsChanged')

    def credentialsChanged(self, password, REQUEST=None):
        '''
        Notifies the authentication mechanism that this user has changed
        passwords.  This can be used to update the authentication cookie.
        Note that this call should *not* cause any change at all to user
        databases.
        '''
        # XXX: this method violates the rules for tools/utilities:
        # it depends on self.REQUEST
        if REQUEST is None:
            REQUEST = self.REQUEST
            warn(
                "credentialsChanged should be called with 'REQUEST' as "
                "second argument. The BBB code will be removed in CMF 2.3.",
                DeprecationWarning,
                stacklevel=2)

        if not self.isAnonymousUser():
            acl_users = self.acl_users
            user = _getAuthenticatedUser(self)
            name = user.getUserName()
            # this really does need to be the user name, and not the user id,
            # because we're dealing with authentication credentials
            p = getattr(REQUEST, '_credentials_changed_path', None)
            if p is not None:
                # Use an interface provided by CookieCrumbler.
                change = self.restrictedTraverse(p)
                change(user, name, password)

    security.declareProtected(ManageUsers, 'getMemberById')

    def getMemberById(self, id):
        '''
        Returns the given member.
        '''
        user = self._huntUser(id, self)
        if user is not None:
            user = self.wrapUser(user)
        return user

    def _huntUserFolder(self, username, context):
        """Find userfolder containing user in the hierarchy
           starting from context
        """
        uf = context.acl_users
        while uf is not None:
            user = uf.getUserById(username)
            if user is not None:
                return uf
            container = aq_parent(aq_inner(uf))
            parent = aq_parent(aq_inner(container))
            uf = getattr(parent, 'acl_users', None)
        return None

    def _huntUser(self, username, context):
        """Find user in the hierarchy of userfolders
           starting from context
        """
        uf = self._huntUserFolder(username, context)
        if uf is not None:
            return uf.getUserById(username)

    def __getPUS(self):
        """ Retrieve the nearest user folder
        """
        warn(
            '__getPUS is deprecated and will be removed in CMF 2.4, '
            'please acquire "acl_users" instead.',
            DeprecationWarning,
            stacklevel=2)
        return self.acl_users

    security.declareProtected(ManageUsers, 'listMemberIds')

    def listMemberIds(self):
        '''Lists the ids of all members.  This may eventually be
        replaced with a set of methods for querying pieces of the
        list rather than the entire list at once.
        '''
        user_folder = self.acl_users
        return [x.getId() for x in user_folder.getUsers()]

    security.declareProtected(ManageUsers, 'listMembers')

    def listMembers(self):
        '''Gets the list of all members.
        '''
        return map(self.wrapUser, self.acl_users.getUsers())

    security.declareProtected(ListPortalMembers, 'searchMembers')

    def searchMembers(self, search_param, search_term):
        """ Search the membership """
        # XXX: this method violates the rules for tools/utilities:
        # it depends on a non-utility tool
        md = getToolByName(self, 'portal_memberdata')

        return md.searchMemberData(search_param, search_term)

    security.declareProtected(View, 'getCandidateLocalRoles')

    def getCandidateLocalRoles(self, obj):
        """ What local roles can I assign?
        """
        member = self.getAuthenticatedMember()
        member_roles = member.getRolesInContext(obj)
        if _checkPermission(ManageUsers, obj):
            local_roles = self.getPortalRoles()
            if 'Manager' not in member_roles:
                local_roles.remove('Manager')
        else:
            local_roles = [
                role for role in member_roles
                if role not in ('Member', 'Authenticated')
            ]
        local_roles.sort()
        return tuple(local_roles)

    security.declareProtected(View, 'setLocalRoles')

    @postonly
    def setLocalRoles(self,
                      obj,
                      member_ids,
                      member_role,
                      reindex=1,
                      REQUEST=None):
        """ Add local roles on an item.
        """
        if (_checkPermission(ChangeLocalRoles, obj)
                and member_role in self.getCandidateLocalRoles(obj)):
            for member_id in member_ids:
                roles = list(obj.get_local_roles_for_userid(userid=member_id))

                if member_role not in roles:
                    roles.append(member_role)
                    obj.manage_setLocalRoles(member_id, roles)

        if reindex:
            # It is assumed that all objects have the method
            # reindexObjectSecurity, which is in CMFCatalogAware and
            # thus PortalContent and PortalFolder.
            obj.reindexObjectSecurity()

    security.declareProtected(View, 'deleteLocalRoles')

    @postonly
    def deleteLocalRoles(self,
                         obj,
                         member_ids,
                         reindex=1,
                         recursive=0,
                         REQUEST=None):
        """ Delete local roles of specified members.
        """
        if _checkPermission(ChangeLocalRoles, obj):
            for member_id in member_ids:
                if obj.get_local_roles_for_userid(userid=member_id):
                    obj.manage_delLocalRoles(userids=member_ids)
                    break

        if recursive and hasattr(aq_base(obj), 'contentValues'):
            for subobj in obj.contentValues():
                self.deleteLocalRoles(subobj, member_ids, 0, 1)

        if reindex:
            # reindexObjectSecurity is always recursive
            obj.reindexObjectSecurity()

    security.declarePrivate('addMember')

    def addMember(self, id, password, roles, domains, properties=None):
        '''Adds a new member to the user folder.  Security checks will have
        already been performed.  Called by portal_registration.
        '''
        self.acl_users._doAddUser(id, password, roles, domains)

        if properties is not None:
            member = self.getMemberById(id)
            member.setMemberProperties(properties)

    security.declareProtected(ManageUsers, 'deleteMembers')

    @postonly
    def deleteMembers(self,
                      member_ids,
                      delete_memberareas=1,
                      delete_localroles=1,
                      REQUEST=None):
        """ Delete members specified by member_ids.
        """
        # XXX: this method violates the rules for tools/utilities:
        # it depends on a non-utility tool

        # Delete members in acl_users.
        acl_users = self.acl_users
        if _checkPermission(ManageUsers, acl_users):
            if isinstance(member_ids, basestring):
                member_ids = (member_ids, )
            member_ids = list(member_ids)
            for member_id in member_ids[:]:
                if not acl_users.getUserById(member_id, None):
                    member_ids.remove(member_id)
            try:
                acl_users.userFolderDelUsers(member_ids)
            except (AttributeError, NotImplementedError):
                raise NotImplementedError('The underlying User Folder '
                                          'doesn\'t support deleting members.')
        else:
            raise AccessControl_Unauthorized(
                'You need the \'Manage users\' '
                'permission for the underlying User Folder.')

        # Delete member data in portal_memberdata.
        mdtool = getToolByName(self, 'portal_memberdata', None)
        if mdtool is not None:
            for member_id in member_ids:
                mdtool.deleteMemberData(member_id)

        # Delete members' home folders including all content items.
        if delete_memberareas:
            for member_id in member_ids:
                self.deleteMemberArea(member_id)

        # Delete members' local roles.
        if delete_localroles:
            self.deleteLocalRoles(getUtility(ISiteRoot),
                                  member_ids,
                                  reindex=1,
                                  recursive=1)

        return tuple(member_ids)

    security.declarePublic('getHomeFolder')

    def getHomeFolder(self, id=None, verifyPermission=0):
        """Returns a member's home folder object or None.
        Set verifyPermission to 1 to return None when the user
        doesn't have the View permission on the folder.
        """
        return None

    security.declarePublic('getHomeUrl')

    def getHomeUrl(self, id=None, verifyPermission=0):
        """Returns the URL to a member's home folder or None.
        Set verifyPermission to 1 to return None when the user
        doesn't have the View permission on the folder.
        """
        return None
class DavLockManager(Item, Implicit):
    id = 'DavLockManager'
    name = title = 'WebDAV Lock Manager'
    meta_type = 'WebDAV Lock Manager'
    zmi_icon = 'fa fa-lock'

    security = ClassSecurityInfo()

    security.declareProtected(
        webdav_manage_locks,  # NOQA: D001
        'manage_davlocks')
    manage_davlocks = manage_main = manage = manage_workspace = DTMLFile(
        'dtml/davLockManager', globals())
    manage_davlocks._setName('manage_davlocks')
    manage_options = (
        {
            'label': 'Control Panel',
            'action': '../manage_main'
        },
        {
            'label': 'Databases',
            'action': '../Database/manage_main'
        },
        {
            'label': 'Configuration',
            'action': '../Configuration/manage_main'
        },
        {
            'label': 'DAV Locks',
            'action': 'manage_main'
        },
        {
            'label': 'Debug Information',
            'action': '../DebugInfo/manage_main'
        },
    )

    @security.protected(webdav_manage_locks)
    def findLockedObjects(self, frompath=''):
        app = self.getPhysicalRoot()

        if frompath:
            if frompath[0] == '/':
                frompath = frompath[1:]
            # since the above will turn '/' into an empty string, check
            # for truth before chopping a final slash
            if frompath and frompath[-1] == '/':
                frompath = frompath[:-1]

        # Now we traverse to the node specified in the 'frompath' if
        # the user chose to filter the search, and run a ZopeFind with
        # the expression 'wl_isLocked()' to find locked objects.
        obj = app.unrestrictedTraverse(frompath)
        lockedobjs = self._findapply(obj, path=frompath)

        return lockedobjs

    @security.private
    def unlockObjects(self, paths=[]):
        app = self.getPhysicalRoot()

        for path in paths:
            ob = app.unrestrictedTraverse(path)
            ob.wl_clearLocks()

    @security.protected(webdav_manage_locks)
    def manage_unlockObjects(self, paths=[], REQUEST=None):
        " Management screen action to unlock objects. "
        if paths:
            self.unlockObjects(paths)
        if REQUEST is not None:
            m = '%s objects unlocked.' % len(paths)
            return self.manage_davlocks(self, REQUEST, manage_tabs_message=m)

    def _findapply(self, obj, result=None, path=''):
        # recursive function to actually dig through and find the locked
        # objects.

        if result is None:
            result = []
        base = aq_base(obj)
        if not hasattr(base, 'objectItems'):
            return result
        try:
            items = obj.objectItems()
        except Exception:
            return result

        addresult = result.append
        for id, ob in items:
            if path:
                p = f'{path}/{id}'
            else:
                p = id

            dflag = hasattr(ob, '_p_changed') and (ob._p_changed is None)
            bs = aq_base(ob)
            if wl_isLocked(ob):
                li = []
                addlockinfo = li.append
                for token, lock in ob.wl_lockItems():
                    addlockinfo({
                        'owner': lock.getCreatorPath(),
                        'token': token
                    })
                addresult((p, li))
                dflag = 0
            if hasattr(bs, 'objectItems'):
                self._findapply(ob, result, p)
            if dflag:
                ob._p_deactivate()

        return result
Beispiel #7
0
class Event(PortalContent, DefaultDublinCoreImpl):
    """Events are objects for the Calendar topical query.
    """

    # Declarative security
    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    implements(IMutableEvent, IEvent, IDAVAware)

    def __init__(self,
                 id,
                 title='',
                 description='',
                 effective_date=None,
                 expiration_date=None,
                 start_date=None,
                 end_date=None,
                 location='',
                 contact_name='',
                 contact_email='',
                 contact_phone='',
                 event_url=''):
        DefaultDublinCoreImpl.__init__(self)
        self.id = id
        self.setTitle(title)
        self.setDescription(description)
        self.effective_date = effective_date
        self.expiration_date = expiration_date

        if start_date is None:
            # Round time to a value that exists in buildTimes().
            dt = DateTime((int(DateTime()) / 1800 + 1) * 1800)
            # Parse time the same way as in edit(). The result is offset-naive.
            start_date = DateTime(str(dt).rsplit(' ', 1)[0])
        else:
            start_date = self._datify(start_date)

        if end_date is None:
            end_date = start_date
        else:
            end_date = self._datify(end_date)
        if end_date < start_date:
            end_date = start_date

        self.setStartDate(start_date)
        self.setEndDate(end_date)
        self.location = location
        self.contact_name = contact_name
        self.contact_email = contact_email
        self.contact_phone = contact_phone
        self.event_url = event_url

    security.declarePrivate('_datify')

    def _datify(self, attrib):
        if attrib == 'None':
            attrib = None
        elif not isinstance(attrib, DateTime):
            if attrib is not None:
                attrib = DateTime(attrib)
        return attrib

    security.declarePublic('getEndStrings')

    def getEndStrings(self):
        """ Returns a mapping with string representations for the end time

        o keys are 'day', 'month' and 'year'
        """
        return _dateStrings(self.end())

    security.declarePublic('getStartStrings')

    def getStartStrings(self):
        """ Returns a mapping with string representations for the start time

        o keys are 'day', 'month' and 'year'
        """
        return _dateStrings(self.start())

    security.declareProtected(ChangeEvents, 'edit')

    def edit(self,
             title=None,
             description=None,
             eventType=None,
             effectiveDay=None,
             effectiveMo=None,
             effectiveYear=None,
             expirationDay=None,
             expirationMo=None,
             expirationYear=None,
             start_time=None,
             startAMPM=None,
             stop_time=None,
             stopAMPM=None,
             location=None,
             contact_name=None,
             contact_email=None,
             contact_phone=None,
             event_url=None):
        """\
        """

        if title is not None:
            self.setTitle(title)
        if description is not None:
            self.setDescription(description)
        if eventType is not None:
            self.setSubject(eventType)

        start_date = end_date = None

        if effectiveDay and effectiveMo and effectiveYear and start_time:
            efdate = '%s/%s/%s %s %s' % (effectiveYear, effectiveMo,
                                         effectiveDay, start_time, startAMPM)
            start_date = DateTime(efdate)

        if expirationDay and expirationMo and expirationYear and stop_time:
            exdate = '%s/%s/%s %s %s' % (expirationYear, expirationMo,
                                         expirationDay, stop_time, stopAMPM)
            end_date = DateTime(exdate)

        if start_date and end_date:
            if end_date < start_date:
                end_date = start_date

            self.setStartDate(start_date)
            self.setEndDate(end_date)

        if location is not None:
            self.location = location
        if contact_name is not None:
            self.contact_name = contact_name
        if contact_email is not None:
            self.contact_email = contact_email
        if contact_phone is not None:
            self.contact_phone = contact_phone
        if event_url is not None:
            self.event_url = event_url
        self.reindexObject()

    security.declarePublic('buildTimes')

    def buildTimes(self):
        result = []
        for hour in range(1, 13):
            for min in (00, 30):
                result.append('%02d:%02d' % (hour, min))
        return result

    security.declarePublic('buildDays')

    def buildDays(self):
        result = []
        for day in range(1, 32):
            result.append(str('%d' % (day)))
        return result

    security.declarePublic('buildMonths')

    def buildMonths(self):
        result = []
        for month in range(1, 13):
            result.append(str('%d' % (month)))
        return result

    security.declarePublic('buildYears')

    def buildYears(self):
        result = []
        start = (DateTime().year() - 2)
        end = (DateTime().year() + 5)
        for year in range(start, end):
            result.append(str(year))
        return result

    security.declareProtected(ChangeEvents, 'setStartDate')

    def setStartDate(self, start):
        """ Setting the event start date when the event is scheduled to begin.
        """
        self.start_date = self._datify(start)

    security.declareProtected(ChangeEvents, 'setEndDate')

    def setEndDate(self, end):
        """ Setting the event end date, when the event ends.
        """
        self.end_date = self._datify(end)

    security.declarePublic('start')

    def start(self):
        """ Return our start time as a DateTime object
        """
        date = getattr(self, 'start_date', None)
        return date is None and self.created() or date

    security.declarePublic('end')

    def end(self):
        """ Return our end time as a DateTime object
        """
        date = getattr(self, 'end_date', None)
        return date is None and self.start() or date

    security.declarePublic('getStartTimeString')

    def getStartTimeString(self):
        """ Return our start time as a string.
        """
        return self.start().AMPMMinutes()

    security.declarePublic('getStopTimeString')

    def getStopTimeString(self):
        """ Return our stop time as a string.
        """
        return self.end().AMPMMinutes()

    security.declarePrivate('handleText')

    def handleText(self, text, format=None):
        """ Handles the raw text, returning headers, body, cooked, format """
        headers = {}
        if format == 'html':
            parser = SimpleHTMLParser()
            parser.feed(text)
            headers.update(parser.metatags)
            if parser.title:
                headers['Title'] = parser.title
            bodyfound = bodyfinder(text)
            if bodyfound:
                body = bodyfound
        else:
            headers, body = parseHeadersBody(text, headers)

        return headers, body, format

    security.declareProtected(ModifyPortalContent, 'setMetadata')

    def setMetadata(self, headers):
        """ Set an Event's metadata

        o headers is a mapping containing keys corresponding to
        Dublin Core metadata fields
        o Only those attributes that are passed in with the mapping are
        manipulated
        """
        headers['Format'] = self.Format()
        new_subject = keywordsplitter(headers)
        headers['Subject'] = new_subject or self.Subject()
        new_contrib = contributorsplitter(headers)
        headers['Contributors'] = new_contrib or self.Contributors()
        for key, value in self.getMetadataHeaders():
            if not key in headers:
                headers[key] = value
        self._editMetadata(title=headers['Title'],
                           subject=headers['Subject'],
                           contributors=headers['Contributors'],
                           effective_date=headers['Effective_date'],
                           expiration_date=headers['Expiration_date'],
                           format=headers['Format'],
                           language=headers['Language'],
                           rights=headers['Rights'])

    security.declarePublic('getMetadataHeaders')

    def getMetadataHeaders(self):
        """ Return metadata attributes in RFC-822-style header spec.
        """
        fmt = '%Y-%m-%d %H:%M:%S'
        hdrlist = [
            x for x in DefaultDublinCoreImpl.getMetadataHeaders(self)
            if x[0] != 'Description'
        ]
        hdrlist.append(('Startdate', self.start().strftime(fmt)))
        hdrlist.append(('Enddate', self.end().strftime(fmt)))
        hdrlist.append(('Location', self.location))
        hdrlist.append(('Contactname', self.contact_name))
        hdrlist.append(('Contactemail', self.contact_email))
        hdrlist.append(('Contactphone', self.contact_phone))
        hdrlist.append(('Eventurl', self.event_url))

        return hdrlist

    ## FTP handlers

    security.declareProtected(ModifyPortalContent, 'PUT')

    def PUT(self, REQUEST, RESPONSE):
        """ Handle HTTP (and presumably FTP?) PUT requests """
        self.dav__init(REQUEST, RESPONSE)
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
        body = REQUEST.get('BODY', '')
        guessedformat = REQUEST.get_header('Content-Type', 'text/plain')
        ishtml = (guessedformat == 'text/html') or html_headcheck(body)

        if ishtml:
            self.setFormat('text/html')
        else:
            self.setFormat('text/plain')

        try:
            headers, body, format = self.handleText(text=body)
            self.setMetadata(headers)
            self.setStartDate(headers['Startdate'])
            self.setEndDate(headers['Enddate'])
            self.edit(location=headers['Location'],
                      contact_name=headers['Contactname'],
                      contact_email=headers['Contactemail'],
                      contact_phone=headers['Contactphone'],
                      event_url=headers['Eventurl'],
                      description=body)

        except ResourceLockedError:
            transaction.abort()
            RESPONSE.setStatus(423)
            return RESPONSE

        RESPONSE.setStatus(204)
        self.reindexObject()
        return RESPONSE

    security.declareProtected(View, 'manage_FTPget')

    def manage_FTPget(self):
        "Get the document body for FTP download (also used for the WebDAV SRC)"
        hdrlist = self.getMetadataHeaders()
        hdrtext = formatRFC822Headers(hdrlist)
        bodytext = '%s\r\n\r\n%s' % (hdrtext, self.Description())

        return bodytext

    security.declareProtected(View, 'get_size')

    def get_size(self):
        """ Used for FTP and apparently the ZMI now too """
        return len(self.manage_FTPget())
Beispiel #8
0
class DynamicType:

    """
    Mixin for portal content that allows the object to take on
    a dynamic type property.
    """

    implements(IDynamicType)

    portal_type = None

    security = ClassSecurityInfo()

    def _setPortalTypeName(self, pt):
        """ Set the portal type name.

        Called by portal_types during construction, records an ID that will be
        used later to locate the correct ContentTypeInformation.
        """
        self.portal_type = pt

    #
    #   'IDynamicType' interface methods
    #
    security.declarePublic('getPortalTypeName')
    def getPortalTypeName(self):
        """ Get the portal type name that can be passed to portal_types.
        """
        pt = self.portal_type
        if callable( pt ):
            pt = pt()
        return pt

    # deprecated alias
    _getPortalTypeName = getPortalTypeName

    security.declarePublic('getTypeInfo')
    def getTypeInfo(self):
        """ Get the TypeInformation object specified by the portal type.
        """
        tool = queryUtility(ITypesTool)
        if tool is None:
            return None
        return tool.getTypeInfo(self)  # Can return None.

    security.declarePublic('getActionInfo')
    def getActionInfo(self, action_chain, check_visibility=0,
                      check_condition=0):
        """ Get an Action info mapping specified by a chain of actions.
        """
        ti = self.getTypeInfo()
        if ti:
            return ti.getActionInfo(action_chain, self, check_visibility,
                                    check_condition)
        else:
            msg = 'Action "%s" not available for %s' % (
                        action_chain, '/'.join(self.getPhysicalPath()))
            raise ValueError(msg)

    security.declarePublic('getIconURL')
    def getIconURL(self):
        """ Get the absolute URL of the icon for the object.
        """
        ti = self.getTypeInfo()
        if ti is None:
            try:
                utool = getUtility(IURLTool)
            except ComponentLookupError:
                # BBB: fallback for CMF 2.2 instances
                utool = aq_get(self, 'portal_url')
            return '%s/misc_/OFSP/dtmldoc.gif' % utool()
        icon_expr_object = ti.getIconExprObject()
        if icon_expr_object is None:
            return ''
        ec = getExprContext(self)
        return icon_expr_object(ec)

    #
    #   'IItem' interface method
    #
    security.declarePublic('icon')
    def icon(self, relative_to_portal=0):
        """
        Using this method allows the content class
        creator to grab icons on the fly instead of using a fixed
        attribute on the class.
        """
        try:
            utool = getUtility(IURLTool)
        except ComponentLookupError:
            # BBB: fallback for CMF 2.2 instances
            utool = aq_get(self, 'portal_url')
        portal_url = utool()
        icon = self.getIconURL()
        if icon.startswith(portal_url):
            icon = icon[len(portal_url)+1:]
            if not relative_to_portal:
                # Relative to REQUEST['BASEPATH1']
                icon = '%s/%s' % (utool(relative=1), icon)
        try:
            utool.getPortalObject().unrestrictedTraverse(icon)
        except (AttributeError, KeyError):
            icon = ''
        return icon

    # deprecated alias
    security.declarePublic('getIcon')
    getIcon = icon

    def __before_publishing_traverse__(self, arg1, arg2=None):
        """ Pre-traversal hook.
        """
        # XXX hack around a bug(?) in BeforeTraverse.MultiHook
        REQUEST = arg2 or arg1

        if REQUEST['REQUEST_METHOD'] not in ('GET', 'POST'):
            return

        stack = REQUEST['TraversalRequestNameStack']
        key = stack and stack[-1] or '(Default)'

        # if there's a Zope3-style default view name set and the
        # corresponding view exists, take that in favour of the FTI's
        # default view
        if key == '(Default)':
            viewname = queryDefaultViewName(self, REQUEST)
            if (viewname and
                queryMultiAdapter((self, REQUEST), name=viewname) is not None):
                stack.append(viewname)
                REQUEST._hacked_path = 1
                return

        ti = self.getTypeInfo()
        method_id = ti and ti.queryMethodID(key, context=self)
        if method_id:
            if key != '(Default)':
                stack.pop()
            if method_id != '(Default)':
                stack.append(method_id)
            REQUEST._hacked_path = 1
Beispiel #9
0
class MembershipTool(BaseTool):
    """ Implement 'portal_membership' interface using "stock" policies.
    """

    implements(IMembershipTool)

    meta_type = 'Default Membership Tool'
    membersfolder_id = 'Members'

    security = ClassSecurityInfo()

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainMembershipTool', _dtmldir)

    security.declareProtected(ManagePortal, 'manage_mapRoles')
    manage_mapRoles = DTMLFile('membershipRolemapping', _dtmldir)

    security.declareProtected(ManagePortal, 'manage_setMembersFolderById')

    def manage_setMembersFolderById(self, id='', REQUEST=None):
        """ ZMI method to set the members folder object by its id.
        """
        self.setMembersFolderById(id)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_mapRoles' +
                '?manage_tabs_message=Members+folder+changed.')

    #
    #   'portal_membership' interface methods
    #
    security.declareProtected(ListPortalMembers, 'getRoster')

    def getRoster(self):
        """ Return a list of mappings for 'listed' members.

        If Manager, return a list of all usernames.  The mapping
        contains the id and listed variables.
        """
        isUserManager = _checkPermission(ManageUsers, self)
        roster = []
        for member in self.listMembers():
            listed = member.getProperty('listed')
            if isUserManager or listed:
                roster.append({'id': member.getId(), 'listed': listed})
        return roster

    security.declareProtected(ManagePortal, 'setMembersFolderById')

    def setMembersFolderById(self, id=''):
        """ Set the members folder object by its id.
        """
        self.membersfolder_id = id.strip()

    security.declarePublic('getMembersFolder')

    def getMembersFolder(self):
        """ Get the members folder object.
        """
        parent = aq_parent(aq_inner(self))
        try:
            members_folder = parent.restrictedTraverse(self.membersfolder_id)
        except (AttributeError, KeyError):
            return None
        request_container = RequestContainer(REQUEST=getRequest())
        return members_folder.__of__(request_container)

    security.declarePublic('createMemberArea')

    def createMemberArea(self, member_id=''):
        """ Create a member area for 'member_id' or authenticated user.
        """
        if not self.getMemberareaCreationFlag():
            return None
        members = self.getMembersFolder()
        if members is None:
            return None
        if self.isAnonymousUser():
            return None
        if member_id:
            if not self.isMemberAccessAllowed(member_id):
                return None
            member = self.getMemberById(member_id)
            if member is None:
                return None
        else:
            member = self.getAuthenticatedMember()
            member_id = member.getId()
        if hasattr(aq_base(members), member_id):
            return None

        # Note: We can't use invokeFactory() to add folder and content because
        # the user might not have the necessary permissions.

        # Create Member's home folder.
        members.manage_addPortalFolder(id=member_id,
                                       title="%s's Home" % member_id)
        f = members._getOb(member_id)

        # Grant Ownership and Owner role to Member
        f.changeOwnership(member)
        f.__ac_local_roles__ = None
        f.manage_setLocalRoles(member_id, ['Owner'])

        # Create Member's initial content.
        if hasattr(self, 'createMemberContent'):
            self.createMemberContent(member=member,
                                     member_id=member_id,
                                     member_folder=f)
        else:
            addDocument(f, 'index_html', member_id + "'s Home",
                        member_id + "'s front page", "structured-text",
                        (DEFAULT_MEMBER_CONTENT % member_id))

            # Grant Ownership and Owner role to Member
            f.index_html.changeOwnership(member)
            f.index_html.__ac_local_roles__ = None
            f.index_html.manage_setLocalRoles(member_id, ['Owner'])

            f.index_html._setPortalTypeName('Document')
            f.index_html.reindexObject()
            f.index_html.notifyWorkflowCreated()
        return f

    security.declarePublic('createMemberarea')
    createMemberarea = createMemberArea

    def getHomeFolder(self, id=None, verifyPermission=0):
        """ Return a member's home folder object, or None.
        """
        if id is None:
            member = self.getAuthenticatedMember()
            if not hasattr(member, 'getMemberId'):
                return None
            id = member.getMemberId()
        members = self.getMembersFolder()
        if members:
            try:
                folder = members._getOb(id)
                if verifyPermission and not _checkPermission(View, folder):
                    # Don't return the folder if the user can't get to it.
                    return None
                return folder
            except (AttributeError, TypeError, KeyError):
                pass
        return None

    def getHomeUrl(self, id=None, verifyPermission=0):
        """ Return the URL to a member's home folder, or None.
        """
        home = self.getHomeFolder(id, verifyPermission)
        if home is not None:
            return home.absolute_url()
        else:
            return None
Beispiel #10
0
class DTMLMethod(
        RestrictedDTML,
        HTML,
        Implicit,
        RoleManager,
        ElementWithTitle,
        Item_w__name__,
        Historical,
        Cacheable,
):
    """ DocumentTemplate.HTML objects that act as methods of their containers.
    """
    meta_type = 'DTML Method'
    _proxy_roles = ()
    index_html = None  # Prevent accidental acquisition
    _cache_namespace_keys = ()

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    # Documents masquerade as functions:
    class func_code:
        pass

    func_code = func_code()
    func_code.co_varnames = 'self', 'REQUEST', 'RESPONSE'
    func_code.co_argcount = 3

    manage_options = ((
        {
            'label': 'Edit',
            'action': 'manage_main'
        },
        {
            'label': 'View',
            'action': ''
        },
        {
            'label': 'Proxy',
            'action': 'manage_proxyForm'
        },
    ) + Historical.manage_options + RoleManager.manage_options +
                      Item_w__name__.manage_options + Cacheable.manage_options)

    # Careful in permission changes--used by DTMLDocument!

    security.declareProtected(change_dtml_methods, 'manage_historyCopy')
    security.declareProtected(change_dtml_methods, 'manage_beforeHistoryCopy')
    security.declareProtected(change_dtml_methods, 'manage_afterHistoryCopy')

    # More reasonable default for content-type for http HEAD requests.
    default_content_type = 'text/html'

    security.declareProtected(View, '__call__')

    def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        """Render using the given client object

        o If client is not passed, we are being called as a sub-template:
          don't do any error propagation.

        o If supplied, use the REQUEST mapping, Response, and key word
        arguments.
        """
        if not self._cache_namespace_keys:
            data = self.ZCacheable_get(default=_marker)
            if data is not _marker:
                if (IStreamIterator.isImplementedBy(data)
                        and RESPONSE is not None):
                    # This is a stream iterator and we need to set some
                    # headers now before giving it to medusa
                    if RESPONSE.headers.get('content-length', None) is None:
                        RESPONSE.setHeader('content-length', len(data))

                    if (RESPONSE.headers.get('content-type', None) is None
                            and RESPONSE.headers.get('Content-type',
                                                     None) is None):
                        ct = (self.__dict__.get('content_type')
                              or self.default_content_type)
                        RESPONSE.setHeader('content-type', ct)

                # Return cached results.
                return data

        __traceback_supplement__ = (PathTracebackSupplement, self)
        kw['document_id'] = self.getId()
        kw['document_title'] = self.title

        security = getSecurityManager()
        security.addContext(self)
        if self.__dict__.has_key('validate'):
            first_time_through = 0
        else:
            self.__dict__['validate'] = security.DTMLValidate
            first_time_through = 1
        try:

            if client is None:
                # Called as subtemplate, so don't need error propagation!
                r = apply(HTML.__call__, (self, client, REQUEST), kw)
                if RESPONSE is None:
                    result = r
                else:
                    result = decapitate(r, RESPONSE)
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(result)
                return result

            r = apply(HTML.__call__, (self, client, REQUEST), kw)
            if type(r) is not str or RESPONSE is None:
                if not self._cache_namespace_keys:
                    self.ZCacheable_set(r)
                return r

        finally:
            security.removeContext(self)
            if first_time_through:
                del self.__dict__['validate']

        have_key = RESPONSE.headers.has_key
        if not (have_key('content-type') or have_key('Content-Type')):
            if self.__dict__.has_key('content_type'):
                c = self.content_type
            else:
                c, e = guess_content_type(self.getId(), r)
            RESPONSE.setHeader('Content-Type', c)
        result = decapitate(r, RESPONSE)
        if not self._cache_namespace_keys:
            self.ZCacheable_set(result)
        return result

    def validate(self, inst, parent, name, value, md=None):
        return getSecurityManager().validate(inst, parent, name, value)

    def ZDocumentTemplate_beforeRender(self, md, default):
        # Tries to get a cached value.
        if self._cache_namespace_keys:
            # Use the specified keys from the namespace to identify a
            # cache entry.
            kw = {}
            for key in self._cache_namespace_keys:
                try:
                    val = md[key]
                except:
                    val = None
                kw[key] = val
            return self.ZCacheable_get(keywords=kw, default=default)
        return default

    def ZDocumentTemplate_afterRender(self, md, result):
        # Tries to set a cache value.
        if self._cache_namespace_keys:
            kw = {}
            for key in self._cache_namespace_keys:
                try:
                    val = md[key]
                except:
                    val = None
                kw[key] = val
            self.ZCacheable_set(result, keywords=kw)

    security.declareProtected(change_dtml_methods, 'ZCacheable_configHTML')
    ZCacheable_configHTML = DTMLFile('dtml/cacheNamespaceKeys', globals())

    security.declareProtected(change_dtml_methods, 'getCacheNamespaceKeys')

    def getCacheNamespaceKeys(self):
        # Return the cacheNamespaceKeys.
        return self._cache_namespace_keys

    security.declareProtected(change_dtml_methods, 'setCacheNamespaceKeys')

    def setCacheNamespaceKeys(self, keys, REQUEST=None):
        # Set the list of names looked up to provide a cache key.
        ks = []
        for key in keys:
            key = str(key).strip()
            if key:
                ks.append(key)
        self._cache_namespace_keys = tuple(ks)
        if REQUEST is not None:
            return self.ZCacheable_manage(self, REQUEST)

    security.declareProtected(View, 'get_size')

    def get_size(self):
        return len(self.raw)

    # deprecated; use get_size!
    getSize = get_size

    security.declareProtected(change_dtml_methods, 'manage')

    security.declareProtected(change_dtml_methods, 'manage_editForm')
    manage_editForm = DTMLFile('dtml/documentEdit', globals())
    manage_editForm._setName('manage_editForm')

    # deprecated!
    manage_uploadForm = manage_editForm

    security.declareProtected(change_dtml_methods, 'manage_main')
    manage = manage_main = manage_editDocument = manage_editForm

    security.declareProtected(change_proxy_roles, 'manage_proxyForm')
    manage_proxyForm = DTMLFile('dtml/documentProxy', globals())

    _size_changes = {
        'Bigger': (5, 5),
        'Smaller': (-5, -5),
        'Narrower': (0, -5),
        'Wider': (0, 5),
        'Taller': (5, 0),
        'Shorter': (-5, 0),
    }

    def _er(self, data, title, SUBMIT, dtpref_cols, dtpref_rows, REQUEST):
        dr, dc = self._size_changes[SUBMIT]
        rows = str(max(1, int(dtpref_rows) + dr))
        cols = str(dtpref_cols)
        if cols.endswith('%'):
            cols = str(min(100, max(25, int(cols[:-1]) + dc))) + '%'
        else:
            cols = str(max(35, int(cols) + dc))
        e = (DateTime("GMT") + 365).rfc822()
        setCookie = REQUEST["RESPONSE"].setCookie
        setCookie("dtpref_rows", rows, path='/', expires=e)
        setCookie("dtpref_cols", cols, path='/', expires=e)
        REQUEST.other.update({"dtpref_cols": cols, "dtpref_rows": rows})
        return self.manage_main(self,
                                REQUEST,
                                title=title,
                                __str__=self.quotedHTML(data))

    security.declareProtected(change_dtml_methods, 'manage_edit')

    def manage_edit(self,
                    data,
                    title,
                    SUBMIT='Change',
                    dtpref_cols='100%',
                    dtpref_rows='20',
                    REQUEST=None):
        """ Replace contents with 'data', title with 'title'.

        The SUBMIT parameter is also used to change the size of the editing
        area on the default Document edit screen.  If the value is "Smaller",
        the rows and columns decrease by 5.  If the value is "Bigger", the
        rows and columns increase by 5.  If any other or no value is supplied,
        the data gets checked for DTML errors and is saved.
        """
        self._validateProxy(REQUEST)
        if self._size_changes.has_key(SUBMIT):
            return self._er(data, title, SUBMIT, dtpref_cols, dtpref_rows,
                            REQUEST)
        if self.wl_isLocked():
            raise ResourceLockedError('This item is locked via WebDAV')

        self.title = str(title)
        if isinstance(data, TaintedString):
            data = data.quoted()
        if not isinstance(data, basestring):
            data = data.read()
        self.munge(data)
        self.ZCacheable_invalidate()
        if REQUEST:
            message = "Saved changes."
            return self.manage_main(self, REQUEST, manage_tabs_message=message)

    security.declareProtected(change_dtml_methods, 'manage_upload')

    def manage_upload(self, file='', REQUEST=None):
        """ Replace the contents of the document with the text in 'file'.
        """
        self._validateProxy(REQUEST)
        if self.wl_isLocked():
            raise ResourceLockedError('This DTML Method is locked via WebDAV')

        if type(file) is not str:
            if REQUEST and not file:
                raise ValueError('No file specified')
            file = file.read()

        self.munge(file)
        self.ZCacheable_invalidate()
        if REQUEST:
            message = "Saved changes."
            return self.manage_main(self, REQUEST, manage_tabs_message=message)

    def manage_haveProxy(self, r):
        return r in self._proxy_roles

    def _validateProxy(self, request, roles=None):
        if roles is None:
            roles = self._proxy_roles
        if not roles:
            return
        user = u = getSecurityManager().getUser()
        user = user.allowed
        for r in roles:
            if r and not user(self, (r, )):
                user = None
                break

        if user is not None:
            return

        raise Forbidden(
            'You are not authorized to change <em>%s</em> because you '
            'do not have proxy roles.\n<!--%s, %s-->' %
            (self.__name__, u, roles))

    security.declareProtected(change_proxy_roles, 'manage_proxy')

    @requestmethod('POST')
    def manage_proxy(self, roles=(), REQUEST=None):
        "Change Proxy Roles"
        self._validateProxy(REQUEST, roles)
        self._validateProxy(REQUEST)
        self._proxy_roles = tuple(roles)
        self.ZCacheable_invalidate()
        if REQUEST:
            message = "Saved changes."
            return self.manage_proxyForm(self,
                                         REQUEST,
                                         manage_tabs_message=message)

    security.declareProtected(view_management_screens, 'PrincipiaSearchSource')

    def PrincipiaSearchSource(self):
        # Support for searching - the document's contents are searched.
        return self.read()

    security.declareProtected(view_management_screens, 'document_src')

    def document_src(self, REQUEST=None, RESPONSE=None):
        # Return unprocessed document source.
        if RESPONSE is not None:
            RESPONSE.setHeader('Content-Type', 'text/plain')
        return self.read()

    ## Protocol handlers

    security.declareProtected(change_dtml_methods, 'PUT')

    def PUT(self, REQUEST, RESPONSE):
        """ Handle FTP / HTTP PUT requests.
        """
        self.dav__init(REQUEST, RESPONSE)
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
        body = REQUEST.get('BODY', '')
        self._validateProxy(REQUEST)
        self.munge(body)
        self.ZCacheable_invalidate()
        RESPONSE.setStatus(204)
        return RESPONSE

    security.declareProtected(ftp_access, 'manage_FTPstat')
    security.declareProtected(ftp_access, 'manage_FTPlist')

    security.declareProtected(ftp_access, 'manage_FTPget')

    def manage_FTPget(self):
        """ Get source for FTP download.
        """
        return self.read()

    def manage_historyCompare(self,
                              rev1,
                              rev2,
                              REQUEST,
                              historyComparisonResults=''):
        return DTMLMethod.inheritedAttribute('manage_historyCompare')(
            self,
            rev1,
            rev2,
            REQUEST,
            historyComparisonResults=html_diff(rev1.read(), rev2.read()))
class VirtualHostMonster(Persistent, Item, Implicit):
    """Provide a simple drop-in solution for virtual hosting.
    """

    meta_type = 'Virtual Host Monster'
    priority = 25

    id = 'VHM'
    title = ''
    lines = ()
    have_map = 0

    security = ClassSecurityInfo()

    manage_options = ({
        'label': 'About',
        'action': 'manage_main'
    }, {
        'label': 'Mappings',
        'action': 'manage_edit'
    })

    security.declareProtected(View, 'manage_main')
    manage_main = DTMLFile('www/VirtualHostMonster',
                           globals(),
                           __name__='manage_main')

    security.declareProtected('Add Site Roots', 'manage_edit')
    manage_edit = DTMLFile('www/manage_edit', globals())

    security.declareProtected('Add Site Roots', 'set_map')

    def set_map(self, map_text, RESPONSE=None):
        "Set domain to path mappings."
        lines = map_text.split('\n')
        self.fixed_map = fixed_map = {}
        self.sub_map = sub_map = {}
        new_lines = []
        for line in lines:
            line = line.split('#!')[0].strip()
            if not line:
                continue
            try:
                # Drop the protocol, if any
                line = line.split('://')[-1]
                try:
                    host, path = [x.strip() for x in line.split('/', 1)]
                except:
                    raise ValueError(
                        'Line needs a slash between host and path: %s' % line)
                pp = filter(None, path.split('/'))
                if pp:
                    obpath = pp[:]
                    if obpath[0] == 'VirtualHostBase':
                        obpath = obpath[3:]
                    if 'VirtualHostRoot' in obpath:
                        i1 = obpath.index('VirtualHostRoot')
                        i2 = i1 + 1
                        while i2 < len(obpath) and obpath[i2][:4] == '_vh_':
                            i2 = i2 + 1
                        del obpath[i1:i2]
                    if obpath:
                        try:
                            ob = self.unrestrictedTraverse(obpath)
                        except:
                            raise ValueError, ('Path not found: %s' % obpath)
                        if not getattr(ob.aq_base, 'isAnObjectManager', 0):
                            raise ValueError, (
                                'Path must lead to an Object Manager: %s' %
                                obpath)
                    if 'VirtualHostRoot' not in pp:
                        pp.append('/')
                    pp.reverse()
                try:
                    int(host.replace('.', ''))
                    raise ValueError, ('IP addresses are not mappable: %s' %
                                       host)
                except ValueError:
                    pass
                if host[:2] == '*.':
                    host_map = sub_map
                    host = host[2:]
                else:
                    host_map = fixed_map
                hostname, port = (host.split(':', 1) + [None])[:2]
                if hostname not in host_map:
                    host_map[hostname] = {}
                host_map[hostname][port] = pp
            except 'LineError', msg:
                line = '%s #! %s' % (line, msg)
            new_lines.append(line)
        self.lines = tuple(new_lines)
        self.have_map = not not (fixed_map or sub_map)  # booleanize
        if RESPONSE is not None:
            RESPONSE.redirect(
                'manage_edit?manage_tabs_message=Changes%20Saved.')
Beispiel #12
0
class DiscussionTool(UniqueObject, SimpleItem):
    """ Links content to discussions.
    """

    implements(IDiscussionTool)

    id = 'portal_discussion'
    meta_type = 'Default Discussion Tool'

    security = ClassSecurityInfo()

    manage_options = (({
        'label': 'Overview',
        'action': 'manage_overview'
    }, ) + SimpleItem.manage_options)

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainDiscussionTool', _dtmldir)

    #
    #   'portal_discussion' interface methods
    #

    security.declarePublic('overrideDiscussionFor')

    def overrideDiscussionFor(self, content, allowDiscussion):
        """ Override discussability for the given object or clear the setting.
        """
        if not _checkPermission(ModifyPortalContent, content):
            raise AccessControl_Unauthorized

        if allowDiscussion is None or allowDiscussion == 'None':
            disc_flag = getattr(aq_base(content), 'allow_discussion', _marker)
            if disc_flag is not _marker:
                try:
                    del content.allow_discussion
                except AttributeError:
                    # https://bugs.launchpad.net/zope-cmf/+bug/162532
                    pass
        else:
            content.allow_discussion = bool(allowDiscussion)

    security.declarePublic('getDiscussionFor')

    def getDiscussionFor(self, content):
        """ Get DiscussionItemContainer for content, create it if necessary.
        """
        if not self.isDiscussionAllowedFor(content):
            raise DiscussionNotAllowed

        if not IDiscussionResponse.providedBy(content) and \
                getattr( aq_base(content), 'talkback', None ) is None:
            # Discussion Items use the DiscussionItemContainer object of the
            # related content item, so only create one for other content items
            self._createDiscussionFor(content)

        return content.talkback  # Return wrapped talkback

    security.declarePublic('isDiscussionAllowedFor')

    def isDiscussionAllowedFor(self, content):
        """ Get boolean indicating whether discussion is allowed for content.
        """
        if hasattr(aq_base(content), 'allow_discussion'):
            return bool(content.allow_discussion)

        if IDynamicType.providedBy(content):
            # Grabbing type information objects only works for dynamic types
            typeInfo = content.getTypeInfo()
            if typeInfo:
                return bool(typeInfo.allowDiscussion())

        return False

    #
    #   Utility methods
    #
    security.declarePrivate('_createDiscussionFor')

    def _createDiscussionFor(self, content):
        """ Create DiscussionItemContainer for content, if allowed.
        """
        if not self.isDiscussionAllowedFor(content):
            raise DiscussionNotAllowed

        content.talkback = DiscussionItemContainer()
        return content.talkback
Beispiel #13
0
class UndoTool(UniqueObject, SimpleItem):
    """ This tool is used to undo changes.
    """

    implements(IUndoTool)

    id = 'portal_undo'
    meta_type = 'CMF Undo Tool'

    security = ClassSecurityInfo()

    manage_options = (SimpleItem.manage_options + ({
        'label': 'Overview',
        'action': 'manage_overview'
    }, ))

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainUndoTool', _dtmldir)

    #
    #   'IUndoTool' interface methods
    #
    security.declareProtected(ListUndoableChanges,
                              'listUndoableTransactionsFor')

    def listUndoableTransactionsFor(self,
                                    object,
                                    first_transaction=None,
                                    last_transaction=None,
                                    PrincipiaUndoBatchSize=None):
        """ List all transaction IDs the user is allowed to undo on 'object'.
        """
        transactions = object.undoable_transactions(
            first_transaction=first_transaction,
            last_transaction=last_transaction,
            PrincipiaUndoBatchSize=PrincipiaUndoBatchSize)
        for t in transactions:
            # Ensure transaction ids don't have embedded LF.
            t['id'] = t['id'].replace('\n', '')
        if not _checkPermission(ManagePortal, object):
            # Filter out transactions done by other members of the portal.
            user_id = _getAuthenticatedUser(self).getId()
            transactions = filter(lambda record, user_id=user_id: record[
                'user_name'].split()[-1] == user_id,
                                  transactions)
        return transactions

    security.declarePublic('undo')

    def undo(self, object, transaction_info):
        """
            Undo the list of transactions passed in 'transaction_info',
            first verifying that the current user is allowed to undo them.
        """
        # Belt and suspenders:  make sure that the user is actually
        # allowed to undo the transation(s) in transaction_info.

        xids = {}  # set of allowed transaction IDs

        allowed = self.listUndoableTransactionsFor(object)

        for xid in map(lambda x: x['id'], allowed):
            xids[xid] = 1

        if type(transaction_info) == type(''):
            transaction_info = [transaction_info]

        for tinfo in transaction_info:
            if not xids.get(tinfo, None):
                raise AccessControl_Unauthorized

        object.manage_undo_transactions(transaction_info)
Beispiel #14
0
class ZCTextIndex(Persistent, Implicit, SimpleItem):
    """Persistent text index.
    """
    implements(IZCTextIndex, IPluggableIndex)

    ## Magic class attributes ##

    meta_type = 'ZCTextIndex'
    query_options = ('query', )

    manage_options = ({'label': 'Overview', 'action': 'manage_main'}, )

    security = ClassSecurityInfo()
    security.declareObjectProtected(manage_zcatalog_indexes)

    ## Constructor ##

    def __init__(self,
                 id,
                 extra=None,
                 caller=None,
                 index_factory=None,
                 field_name=None,
                 lexicon_id=None):
        self.id = id

        # Arguments can be passed directly to the constructor or
        # via the silly "extra" record.
        self._fieldname = field_name or getattr(extra, 'doc_attr', '') or id
        self._indexed_attrs = self._fieldname.split(',')
        self._indexed_attrs = [
            attr.strip() for attr in self._indexed_attrs if attr
        ]

        lexicon_id = lexicon_id or getattr(extra, 'lexicon_id', '')
        lexicon = getattr(caller, lexicon_id, None)

        if lexicon is None:
            raise LookupError, 'Lexicon "%s" not found' % escape(lexicon_id)

        if not ILexicon.providedBy(lexicon):
            raise ValueError('Object "%s" does not implement '
                             'ZCTextIndex Lexicon interface' % lexicon.getId())

        self.lexicon_id = lexicon.getId()
        self._v_lexicon = lexicon

        if index_factory is None:
            if extra.index_type not in index_types.keys():
                raise ValueError, 'Invalid index type "%s"' % escape(
                    extra.index_type)
            self._index_factory = index_types[extra.index_type]
            self._index_type = extra.index_type
        else:
            self._index_factory = index_factory

        self.index = self._index_factory(aq_base(self.getLexicon()))

    ## Private Methods ##

    security.declarePrivate('getLexicon')

    def getLexicon(self):
        """Get the lexicon for this index
        """
        if hasattr(aq_base(self), 'lexicon'):
            # Fix up old ZCTextIndexes by removing direct lexicon ref
            # and changing it to an ID
            lexicon = getattr(aq_parent(aq_inner(self)), self.lexicon.getId())
            self.lexicon_id = lexicon.getId()
            del self.lexicon

        if getattr(aq_base(self), 'lexicon_path', None):
            # Fix up slightly less old ZCTextIndexes by removing
            # the physical path and changing it to an ID.
            # There's no need to use a physical path, which otherwise
            # makes it difficult to move or rename ZCatalogs.
            self.lexicon_id = self.lexicon_path[-1]
            del self.lexicon_path

        try:
            return self._v_lexicon
        except AttributeError:
            lexicon = getattr(aq_parent(aq_inner(self)), self.lexicon_id)
            if not ILexicon.providedBy(lexicon):
                raise TypeError('Object "%s" is not a ZCTextIndex Lexicon' %
                                repr(lexicon))
            self._v_lexicon = lexicon
            return lexicon

    ## External methods not in the Pluggable Index API ##

    security.declareProtected(search_zcatalog, 'query')

    def query(self, query, nbest=10):
        """Return pair (mapping from docids to scores, num results).

        The num results is the total number of results before trimming
        to the nbest results.
        """
        tree = QueryParser(self.getLexicon()).parseQuery(query)
        results = tree.executeQuery(self.index)
        if results is None:
            return [], 0
        chooser = NBest(nbest)
        chooser.addmany(results.items())
        return chooser.getbest(), len(results)

    ## Pluggable Index APIs ##

    def index_object(self, documentId, obj, threshold=None):
        """Wrapper for  index_doc()  handling indexing of multiple attributes.

        Enter the document with the specified documentId in the index
        under the terms extracted from the indexed text attributes,
        each of which should yield either a string or a list of
        strings (Unicode or otherwise) to be passed to index_doc().
        """
        # XXX We currently ignore subtransaction threshold

        # needed for backward compatibility
        try:
            fields = self._indexed_attrs
        except:
            fields = [self._fieldname]

        res = 0
        all_texts = []
        for attr in fields:
            text = getattr(obj, attr, None)
            if text is None:
                continue
            if safe_callable(text):
                text = text()
            if text is None:
                continue
            if text:
                if isinstance(text, (
                        list,
                        tuple,
                )):
                    all_texts.extend(text)
                else:
                    all_texts.append(text)

        # Check that we're sending only strings
        all_texts = filter(lambda text: isinstance(text, basestring), \
                           all_texts)
        if all_texts:
            return self.index.index_doc(documentId, all_texts)
        return res

    def unindex_object(self, docid):
        if self.index.has_doc(docid):
            self.index.unindex_doc(docid)

    def _apply_index(self, request):
        """Apply query specified by request, a mapping containing the query.

        Returns two object on success, the resultSet containing the
        matching record numbers and a tuple containing the names of
        the fields used

        Returns None if request is not valid for this index.
        """
        record = parseIndexRequest(request, self.id, self.query_options)
        if record.keys is None:
            return None

        query_str = ' '.join(record.keys)
        if not query_str:
            return None
        tree = QueryParser(self.getLexicon()).parseQuery(query_str)
        results = tree.executeQuery(self.index)
        return results, (self.id, )

    def getEntryForObject(self, documentId, default=None):
        """Return the list of words indexed for documentId"""
        try:
            word_ids = self.index.get_words(documentId)
        except KeyError:
            return default
        get_word = self.getLexicon().get_word
        return [get_word(wid) for wid in word_ids]

    def uniqueValues(self, name=None, withLengths=0):
        raise NotImplementedError

    ## The ZCatalog Index management screen uses these methods ##

    def numObjects(self):
        """Return number of unique words in the index"""
        return self.index.length()

    def indexSize(self):
        """Return the number of indexes objects """
        return self.index.document_count()

    def clear(self):
        """reinitialize the index (but not the lexicon)"""
        try:
            # Remove the cached reference to the lexicon
            # So that it is refreshed
            del self._v_lexicon
        except (AttributeError, KeyError):
            pass
        self.index = self._index_factory(aq_base(self.getLexicon()))

    ## User Interface Methods ##

    manage_main = DTMLFile('dtml/manageZCTextIndex', globals())

    def getIndexSourceNames(self):
        """Return sequence of names of indexed attributes"""
        try:
            return self._indexed_attrs
        except:
            return [self._fieldname]

    def getIndexType(self):
        """Return index type string"""
        return getattr(self, '_index_type', self._index_factory.__name__)

    def getLexiconURL(self):
        """Return the url of the lexicon used by the index"""
        try:
            lex = self.getLexicon()
        except (KeyError, AttributeError):
            return None
        else:
            return lex.absolute_url()
Beispiel #15
0
class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
    """ browser id management class
    """
    implements(IBrowserIdManager)
    meta_type = 'Browser Id Manager'

    security = ClassSecurityInfo()
    security.declareObjectPublic()
    ok = {'meta_type': 1, 'id': 1, 'title': 1, 'icon': 1, 'title_or_id': 1}
    security.setDefaultAccess(ok)
    security.setPermissionDefault(MGMT_SCREEN_PERM, ['Manager'])
    security.setPermissionDefault(ACCESS_CONTENTS_PERM,
                                  ['Manager', 'Anonymous'])
    security.setPermissionDefault(CHANGE_IDMGR_PERM, ['Manager'])

    # BBB
    auto_url_encoding = 0
    cookie_http_only = 0

    def __init__(self,
                 id,
                 title='',
                 idname='_ZopeId',
                 location=('cookies', 'form'),
                 cookiepath=('/'),
                 cookiedomain='',
                 cookielifedays=0,
                 cookiesecure=0,
                 cookiehttponly=0,
                 auto_url_encoding=0):
        self.id = str(id)
        self.title = str(title)
        self.setBrowserIdName(idname)
        self.setBrowserIdNamespaces(location)
        self.setCookiePath(cookiepath)
        self.setCookieDomain(cookiedomain)
        self.setCookieLifeDays(cookielifedays)
        self.setCookieSecure(cookiesecure)
        self.setCookieHTTPOnly(cookiehttponly)
        self.setAutoUrlEncoding(auto_url_encoding)

    # IBrowserIdManager
    security.declareProtected(ACCESS_CONTENTS_PERM, 'hasBrowserId')

    def hasBrowserId(self):
        """ See IBrowserIdManager.
        """
        try:
            return self.getBrowserId(create=0) is not None
        except BrowserIdManagerErr:
            return False

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserId')

    def getBrowserId(self, create=1):
        """ See IBrowserIdManager.
        """
        REQUEST = self.REQUEST
        # let's see if bid has already been attached to request
        bid = getattr(REQUEST, 'browser_id_', None)
        if bid is not None:
            # it's already set in this request so we can just return it
            # if it's well-formed
            if not isAWellFormedBrowserId(bid):
                # somebody screwed with the REQUEST instance during
                # this request.
                raise BrowserIdManagerErr('Ill-formed browserid in '
                                          'REQUEST.browser_id_:  %s' %
                                          escape(bid))
            return bid
        # fall through & ck form/cookie namespaces if bid is not in request.
        tk = self.browserid_name
        ns = self.browserid_namespaces
        for name in ns:
            if name == 'url':
                continue  # browser ids in url are checked by Traverser class
            current_ns = getattr(REQUEST, name, None)
            if current_ns is None:
                continue
            bid = current_ns.get(tk, None)
            if bid is not None:
                # hey, we got a browser id!
                if isAWellFormedBrowserId(bid):
                    # bid is not "plain old broken"
                    REQUEST.browser_id_ = bid
                    REQUEST.browser_id_ns_ = name
                    return bid
        # fall through if bid is invalid or not in namespaces
        if create:
            # create a brand new bid
            bid = getNewBrowserId()
            if 'cookies' in ns:
                self._setCookie(bid, REQUEST)
            REQUEST.browser_id_ = bid
            REQUEST.browser_id_ns_ = None
            return bid
        # implies a return of None if:
        # (not create=1) and (invalid or ((not in req) and (not in ns)))

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdName')

    def getBrowserIdName(self):
        """ See IBrowserIdManager.
        """
        return self.browserid_name

    security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdNew')

    def isBrowserIdNew(self):
        """ See IBrowserIdManager.
        """
        if not self.getBrowserId(create=False):
            raise BrowserIdManagerErr('There is no current browser id.')
        # ns will be None if new
        return getattr(self.REQUEST, 'browser_id_ns_', None) is None

    security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdFromCookie')

    def isBrowserIdFromCookie(self):
        """ See IBrowserIdManager.
        """
        if not self.getBrowserId(create=False):
            raise BrowserIdManagerErr('There is no current browser id.')
        if getattr(self.REQUEST, 'browser_id_ns_') == 'cookies':
            return 1

    security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdFromForm')

    def isBrowserIdFromForm(self):
        """ See IBrowserIdManager.
        """
        if not self.getBrowserId(create=False):
            raise BrowserIdManagerErr('There is no current browser id.')
        if getattr(self.REQUEST, 'browser_id_ns_') == 'form':
            return 1

    security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdFromUrl')

    def isBrowserIdFromUrl(self):
        """ See IBrowserIdManager.
        """
        if not self.getBrowserId(create=False):
            raise BrowserIdManagerErr('There is no current browser id.')
        if getattr(self.REQUEST, 'browser_id_ns_') == 'url':
            return 1

    security.declareProtected(ACCESS_CONTENTS_PERM, 'flushBrowserIdCookie')

    def flushBrowserIdCookie(self):
        """ See IBrowserIdManager.
        """
        if 'cookies' not in self.browserid_namespaces:
            raise BrowserIdManagerErr('Cookies are not now being used as a '
                                      'browser id namespace, thus the '
                                      'browserid cookie cannot be flushed.')
        self._setCookie('deleted', self.REQUEST, remove=1)

    security.declareProtected(ACCESS_CONTENTS_PERM,
                              'setBrowserIdCookieByForce')

    def setBrowserIdCookieByForce(self, bid):
        """ See IBrowserIdManager.
        """
        if 'cookies' not in self.browserid_namespaces:
            raise BrowserIdManagerErr('Cookies are not now being used as a '
                                      'browser id namespace, thus the '
                                      'browserid cookie cannot be forced.')
        self._setCookie(bid, self.REQUEST)

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getHiddenFormField')

    def getHiddenFormField(self):
        """ See IBrowserIdManager.
        """
        s = '<input type="hidden" name="%s" value="%s" />'
        return s % (self.getBrowserIdName(), self.getBrowserId())

    security.declareProtected(ACCESS_CONTENTS_PERM, 'encodeUrl')

    def encodeUrl(self, url, style='querystring', create=1):
        # See IBrowserIdManager
        bid = self.getBrowserId(create)
        if bid is None:
            raise BrowserIdManagerErr('There is no current browser id.')
        name = self.getBrowserIdName()
        if style == 'querystring':  # encode bid in querystring
            if '?' in url:
                return '%s&amp;%s=%s' % (url, name, bid)
            else:
                return '%s?%s=%s' % (url, name, bid)
        else:  # encode bid as first two URL path segments
            proto, host, path, params, query, frag = urlparse(url)
            path = '/%s/%s%s' % (name, bid, path)
            return urlunparse((proto, host, path, params, query, frag))

    # Non-IBrowserIdManager accessors / mutators.
    security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdName')

    def setBrowserIdName(self, k):
        """ Set browser id name string

        o Enforce "valid" values.
        """
        if not (type(k) is type('') and k and not badidnamecharsin(k)):
            raise BrowserIdManagerErr('Bad id name string %s' %
                                      escape(repr(k)))
        self.browserid_name = k

    security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdNamespaces')

    def setBrowserIdNamespaces(self, ns):
        """
        accepts list of allowable browser id namespaces 
        """
        for name in ns:
            if name not in ALLOWED_BID_NAMESPACES:
                raise BrowserIdManagerErr('Bad browser id namespace %s' %
                                          repr(name))
        self.browserid_namespaces = tuple(ns)

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdNamespaces')

    def getBrowserIdNamespaces(self):
        """ """
        return self.browserid_namespaces

    security.declareProtected(CHANGE_IDMGR_PERM, 'setCookiePath')

    def setCookiePath(self, path=''):
        """ sets cookie 'path' element for id cookie """
        if not (type(path) is type('') and not badcookiecharsin(path)):
            raise BrowserIdManagerErr('Bad cookie path %s' %
                                      escape(repr(path)))
        self.cookie_path = path

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookiePath')

    def getCookiePath(self):
        """ """
        return self.cookie_path

    security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieLifeDays')

    def setCookieLifeDays(self, days):
        """ offset for id cookie 'expires' element """
        if type(days) not in (type(1), type(1.0)):
            raise BrowserIdManagerErr('Bad cookie lifetime in days %s '
                                      '(requires integer value)' %
                                      escape(repr(days)))
        self.cookie_life_days = int(days)

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookieLifeDays')

    def getCookieLifeDays(self):
        """ """
        return self.cookie_life_days

    security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieDomain')

    def setCookieDomain(self, domain):
        """ sets cookie 'domain' element for id cookie """
        if type(domain) is not type(''):
            raise BrowserIdManagerErr('Cookie domain must be string: %s' %
                                      escape(repr(domain)))
        if not domain:
            self.cookie_domain = ''
            return
        if not twodotsin(domain):
            raise BrowserIdManagerErr(
                'Cookie domain must contain at least two dots '
                '(e.g. ".zope.org" or "www.zope.org") or it must '
                'be left blank. : '
                '%s' % escape( ` domain `))
        if badcookiecharsin(domain):
            raise BrowserIdManagerErr('Bad characters in cookie domain %s' %
                                      escape( ` domain `))
        self.cookie_domain = domain

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookieDomain')

    def getCookieDomain(self):
        """ """
        return self.cookie_domain

    security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieHTTPOnly')

    def setCookieHTTPOnly(self, http_only):
        """ sets cookie 'HTTPOnly' on or off """
        self.cookie_http_only = bool(http_only)

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookieHTTPOnly')

    def getCookieHTTPOnly(self):
        """ retrieve the 'HTTPOnly' flag """
        return self.cookie_http_only

    security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure')

    def setCookieSecure(self, secure):
        """ sets cookie 'secure' element for id cookie """
        self.cookie_secure = not not secure

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookieSecure')

    def getCookieSecure(self):
        """ """
        return self.cookie_secure

    security.declareProtected(CHANGE_IDMGR_PERM, 'setAutoUrlEncoding')

    def setAutoUrlEncoding(self, auto_url_encoding):
        """ sets 'auto url encoding' on or off """
        self.auto_url_encoding = not not auto_url_encoding

    security.declareProtected(ACCESS_CONTENTS_PERM, 'getAutoUrlEncoding')

    def getAutoUrlEncoding(self):
        """ """
        return self.auto_url_encoding

    security.declareProtected(ACCESS_CONTENTS_PERM, 'isUrlInBidNamespaces')

    def isUrlInBidNamespaces(self):
        """ Returns true if 'url' is in the browser id namespaces
        for this browser id """
        return 'url' in self.browserid_namespaces

    def _setCookie(self,
                   bid,
                   REQUEST,
                   remove=0,
                   now=time.time,
                   strftime=time.strftime,
                   gmtime=time.gmtime):
        """ """
        expires = None
        if remove:
            expires = "Sun, 10-May-1971 11:59:00 GMT"
        elif self.cookie_life_days:
            expires = now() + self.cookie_life_days * 86400
            # Wdy, DD-Mon-YYYY HH:MM:SS GMT
            expires = strftime('%a %d-%b-%Y %H:%M:%S GMT', gmtime(expires))

        # cookie attributes managed by BrowserIdManager
        d = {
            'domain': self.cookie_domain,
            'path': self.cookie_path,
            'secure': self.cookie_secure,
            'http_only': self.cookie_http_only,
            'expires': expires
        }

        if self.cookie_secure:
            URL1 = REQUEST.get('URL1', None)
            if URL1 is None:
                return  # should we raise an exception?
            if string.split(URL1, ':')[0] != 'https':
                return  # should we raise an exception?

        cookies = REQUEST.RESPONSE.cookies
        cookie = cookies[self.browserid_name] = {}
        for k, v in d.items():
            if v:
                cookie[k] = v  #only stuff things with true values
        cookie['value'] = bid

    def _setId(self, id):
        if id != self.id:
            raise ValueError('Cannot rename a browser id manager')

    # Jukes for handling URI-munged browser IDS
    def hasTraversalHook(self, parent):
        name = TRAVERSAL_APPHANDLE
        return not not queryBeforeTraverse(parent, name)

    def updateTraversalData(self):
        if 'url' in self.browserid_namespaces:
            self.registerTraversalHook()
        else:
            self.unregisterTraversalHook()

    def unregisterTraversalHook(self):
        parent = aq_parent(aq_inner(self))
        name = TRAVERSAL_APPHANDLE
        if self.hasTraversalHook(parent):
            unregisterBeforeTraverse(parent, name)

    def registerTraversalHook(self):
        parent = aq_parent(aq_inner(self))
        if not self.hasTraversalHook(parent):
            hook = BrowserIdManagerTraverser()
            name = TRAVERSAL_APPHANDLE
            priority = 40  # "higher" priority than session data traverser
            registerBeforeTraverse(parent, hook, name, priority)

    # ZMI
    manage_options = (
        {
            'label': 'Settings',
            'action': 'manage_browseridmgr'
        },
        {
            'label': 'Security',
            'action': 'manage_access'
        },
        {
            'label': 'Ownership',
            'action': 'manage_owner'
        },
    )

    def manage_afterAdd(self, item, container):
        """ Maybe add our traversal hook """
        self.updateTraversalData()

    def manage_beforeDelete(self, item, container):
        """ Remove our traversal hook if it exists """
        self.unregisterTraversalHook()

    security.declareProtected(MGMT_SCREEN_PERM, 'manage_browseridmgr')
    manage_browseridmgr = DTMLFile('dtml/manageIdManager', globals())

    security.declareProtected(CHANGE_IDMGR_PERM,
                              'manage_changeBrowserIdManager')

    def manage_changeBrowserIdManager(self,
                                      title='',
                                      idname='_ZopeId',
                                      location=('cookies', 'form'),
                                      cookiepath='/',
                                      cookiedomain='',
                                      cookielifedays=0,
                                      cookiesecure=0,
                                      cookiehttponly=0,
                                      auto_url_encoding=0,
                                      REQUEST=None):
        """ """
        self.title = str(title)
        self.setBrowserIdName(idname)
        self.setCookiePath(cookiepath)
        self.setCookieDomain(cookiedomain)
        self.setCookieLifeDays(cookielifedays)
        self.setCookieSecure(cookiesecure)
        self.setCookieHTTPOnly(cookiehttponly)
        self.setBrowserIdNamespaces(location)
        self.setAutoUrlEncoding(auto_url_encoding)
        self.updateTraversalData()
        if REQUEST is not None:
            msg = '/manage_browseridmgr?manage_tabs_message=Changes saved'
            REQUEST.RESPONSE.redirect(self.absolute_url() + msg)
Beispiel #16
0
class SetupTool(Folder):
    """ Profile-based site configuration manager.
    """

    implements(ISetupTool)

    meta_type = 'Generic Setup Tool'

    _baseline_context_id = ''

    _profile_upgrade_versions = {}

    security = ClassSecurityInfo()

    def __init__(self, id):
        self.id = str(id)
        self._import_registry = ImportStepRegistry()
        self._export_registry = ExportStepRegistry()
        self._toolset_registry = ToolsetRegistry()

    #
    #   ISetupTool API
    #
    security.declareProtected(ManagePortal, 'getEncoding')

    def getEncoding(self):
        """ See ISetupTool.
        """
        return 'utf-8'

    security.declareProtected(ManagePortal, 'getBaselineContextID')

    def getBaselineContextID(self):
        """ See ISetupTool.
        """
        return self._baseline_context_id

    security.declareProtected(ManagePortal, 'setBaselineContext')

    def setBaselineContext(self, context_id, encoding=None):
        """ See ISetupTool.
        """
        self._baseline_context_id = context_id
        self.applyContextById(context_id, encoding)

    security.declareProtected(ManagePortal, 'applyContextById')

    def applyContextById(self, context_id, encoding=None):
        context = self._getImportContext(context_id)
        self.applyContext(context, encoding)

    security.declareProtected(ManagePortal, 'applyContext')

    def applyContext(self, context, encoding=None):
        self._updateImportStepsRegistry(context, encoding)
        self._updateExportStepsRegistry(context, encoding)

    security.declareProtected(ManagePortal, 'getImportStepRegistry')

    def getImportStepRegistry(self):
        """ See ISetupTool.
        """
        return self._import_registry

    security.declareProtected(ManagePortal, 'getExportStepRegistry')

    def getExportStepRegistry(self):
        """ See ISetupTool.
        """
        return self._export_registry

    security.declareProtected(ManagePortal, 'getExportStep')

    def getExportStep(self, step, default=None):
        """Simple wrapper to query both the global and local step registry."""
        res = _export_step_registry.getStep(step, default)
        if res is not default:
            return res
        return self._export_registry.getStep(step, default)

    security.declareProtected(ManagePortal, 'listExportSteps')

    def listExportSteps(self):
        steps = _export_step_registry.listSteps() + \
                self._export_registry.listSteps()
        return tuple(set(steps))

    security.declareProtected(ManagePortal, 'getImportStep')

    def getImportStep(self, step, default=None):
        """Simple wrapper to query both the global and local step registry."""
        res = _import_step_registry.getStep(step, default)
        if res is not default:
            return res
        return self._import_registry.getStep(step, default)

    security.declareProtected(ManagePortal, 'getSortedImportSteps')

    def getSortedImportSteps(self):
        steps = _import_step_registry.listSteps() + \
                self._import_registry.listSteps()
        step_infos = [self.getImportStepMetadata(step) for step in set(steps)]
        return tuple(_computeTopologicalSort(step_infos))

    security.declareProtected(ManagePortal, 'getImportStepMetadata')

    def getImportStepMetadata(self, step, default=None):
        """Simple wrapper to query both the global and local step registry."""
        res = self._import_registry.getStepMetadata(step, default)
        if res is not default:
            return res
        return _import_step_registry.getStepMetadata(step, default)

    security.declareProtected(ManagePortal, 'getExportStepMetadata')

    def getExportStepMetadata(self, step, default=None):
        """Simple wrapper to query both the global and local step registry."""
        res = self._export_registry.getStepMetadata(step, default)
        if res is not default:
            return res
        return _export_step_registry.getStepMetadata(step, default)

    security.declareProtected(ManagePortal, 'getToolsetRegistry')

    def getToolsetRegistry(self):
        """ See ISetupTool.
        """
        return self._toolset_registry

    security.declareProtected(ManagePortal, 'runImportStepFromProfile')

    def runImportStepFromProfile(self,
                                 profile_id,
                                 step_id,
                                 run_dependencies=True,
                                 purge_old=None):
        """ See ISetupTool.
        """
        context = self._getImportContext(profile_id, purge_old)

        self.applyContext(context)

        info = self.getImportStepMetadata(step_id)

        if info is None:
            raise ValueError, 'No such import step: %s' % step_id

        dependencies = info.get('dependencies', ())

        messages = {}
        steps = []

        if run_dependencies:
            for dependency in dependencies:
                if dependency not in steps:
                    steps.append(dependency)
        steps.append(step_id)

        full_import = (set(steps) == set(self.getSortedImportSteps()))
        event.notify(
            BeforeProfileImportEvent(self, profile_id, steps, full_import))

        for step in steps:
            message = self._doRunImportStep(step, context)
            messages[step] = message or ''

        message_list = filter(None, [message])
        message_list.extend(['%s: %s' % x[1:] for x in context.listNotes()])
        messages[step_id] = '\n'.join(message_list)

        event.notify(ProfileImportedEvent(self, profile_id, steps,
                                          full_import))

        return {'steps': steps, 'messages': messages}

    security.declareProtected(ManagePortal, 'runAllImportStepsFromProfile')

    def runAllImportStepsFromProfile(self,
                                     profile_id,
                                     purge_old=None,
                                     ignore_dependencies=False,
                                     archive=None):
        """ See ISetupTool.
        """
        __traceback_info__ = profile_id

        result = self._runImportStepsFromContext(
            purge_old=purge_old,
            profile_id=profile_id,
            archive=archive,
            ignore_dependencies=ignore_dependencies)
        if profile_id is None:
            prefix = 'import-all-from-tar'
        else:
            prefix = 'import-all-%s' % profile_id.replace(':', '_')
        name = self._mangleTimestampName(prefix, 'log')
        self._createReport(name, result['steps'], result['messages'])

        return result

    security.declareProtected(ManagePortal, 'runExportStep')

    def runExportStep(self, step_id):
        """ See ISetupTool.
        """
        return self._doRunExportSteps([step_id])

    security.declareProtected(ManagePortal, 'runAllExportSteps')

    def runAllExportSteps(self):
        """ See ISetupTool.
        """
        return self._doRunExportSteps(self.listExportSteps())

    security.declareProtected(ManagePortal, 'createSnapshot')

    def createSnapshot(self, snapshot_id):
        """ See ISetupTool.
        """
        context = SnapshotExportContext(self, snapshot_id)
        messages = {}
        steps = self.listExportSteps()

        for step_id in steps:

            handler = self.getExportStep(step_id)

            if handler is None:
                logger = logging.getLogger('GenericSetup')
                logger.error('Step %s has an invalid handler' % step_id)
                continue

            messages[step_id] = handler(context)

        return {
            'steps': steps,
            'messages': messages,
            'url': context.getSnapshotURL(),
            'snapshot': context.getSnapshotFolder()
        }

    security.declareProtected(ManagePortal, 'compareConfigurations')

    def compareConfigurations(
        self,
        lhs_context,
        rhs_context,
        missing_as_empty=False,
        ignore_blanks=False,
        skip=SKIPPED_FILES,
    ):
        """ See ISetupTool.
        """
        differ = ConfigDiff(
            lhs_context,
            rhs_context,
            missing_as_empty,
            ignore_blanks,
            skip,
        )

        return differ.compare()

    security.declareProtected(ManagePortal, 'markupComparison')

    def markupComparison(self, lines):
        """ See ISetupTool.
        """
        result = []

        for line in lines.splitlines():

            if line.startswith('** '):

                if line.find('File') > -1:
                    if line.find('replaced') > -1:
                        result.append(('file-to-dir', line))
                    elif line.find('added') > -1:
                        result.append(('file-added', line))
                    else:
                        result.append(('file-removed', line))
                else:
                    if line.find('replaced') > -1:
                        result.append(('dir-to-file', line))
                    elif line.find('added') > -1:
                        result.append(('dir-added', line))
                    else:
                        result.append(('dir-removed', line))

            elif line.startswith('@@'):
                result.append(('diff-range', line))

            elif line.startswith(' '):
                result.append(('diff-context', line))

            elif line.startswith('+'):
                result.append(('diff-added', line))

            elif line.startswith('-'):
                result.append(('diff-removed', line))

            elif line == '\ No newline at end of file':
                result.append(('diff-context', line))

            else:
                result.append(('diff-header', line))

        return '<pre>\n%s\n</pre>' % ('\n'.join(
            [('<span class="%s">%s</span>' % (cl, escape(l)))
             for cl, l in result]))

    #
    #   ZMI
    #
    manage_options = (
        Folder.manage_options[:1] + (
            {
                'label': 'Profiles',
                'action': 'manage_tool'
            },
            {
                'label': 'Import',
                'action': 'manage_importSteps'
            },
            {
                'label': 'Export',
                'action': 'manage_exportSteps'
            },
            {
                'label': 'Upgrades',
                'action': 'manage_upgrades'
            },
            {
                'label': 'Snapshots',
                'action': 'manage_snapshots'
            },
            {
                'label': 'Comparison',
                'action': 'manage_showDiff'
            },
            {
                'label': 'Manage',
                'action': 'manage_stepRegistry'
            },
        ) + Folder.manage_options[3:]  # skip "View", "Properties"
    )

    security.declareProtected(ManagePortal, 'manage_tool')
    manage_tool = PageTemplateFile('sutProperties', _wwwdir)

    security.declareProtected(ManagePortal, 'manage_updateToolProperties')

    def manage_updateToolProperties(self, context_id, RESPONSE):
        """ Update the tool's settings.
        """
        self.setBaselineContext(context_id)

        RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' %
                          (self.absolute_url(), 'Properties+updated.'))

    security.declareProtected(ManagePortal, 'manage_importSteps')
    manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir)

    security.declareProtected(ManagePortal, 'manage_importSelectedSteps')

    def manage_importSelectedSteps(self,
                                   ids,
                                   run_dependencies,
                                   context_id=None):
        """ Import the steps selected by the user.
        """
        messages = {}
        if not ids:
            summary = 'No steps selected.'

        else:
            if context_id is None:
                context_id = self.getBaselineContextID()
            steps_run = []
            for step_id in ids:
                result = self.runImportStepFromProfile(context_id, step_id,
                                                       run_dependencies)
                steps_run.extend(result['steps'])
                messages.update(result['messages'])

            summary = 'Steps run: %s' % ', '.join(steps_run)

            name = self._mangleTimestampName('import-selected', 'log')
            self._createReport(name, result['steps'], result['messages'])

        return self.manage_importSteps(manage_tabs_message=summary,
                                       messages=messages)

    security.declareProtected(ManagePortal, 'manage_importSelectedSteps')

    def manage_importAllSteps(self, context_id=None):
        """ Import all steps.
        """
        if context_id is None:
            context_id = self.getBaselineContextID()
        result = self.runAllImportStepsFromProfile(context_id, purge_old=None)

        steps_run = 'Steps run: %s' % ', '.join(result['steps'])

        return self.manage_importSteps(manage_tabs_message=steps_run,
                                       messages=result['messages'])

    security.declareProtected(ManagePortal, 'manage_importExtensions')

    def manage_importExtensions(self, RESPONSE, profile_ids=()):
        """ Import all steps for the selected extension profiles.
        """
        detail = {}
        if len(profile_ids) == 0:
            message = 'Please select one or more extension profiles.'
            RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' %
                              (self.absolute_url(), message))
        else:
            message = 'Imported profiles: %s' % ', '.join(profile_ids)

            for profile_id in profile_ids:

                result = self.runAllImportStepsFromProfile(profile_id)

                for k, v in result['messages'].items():
                    detail['%s:%s' % (profile_id, k)] = v

            return self.manage_importSteps(manage_tabs_message=message,
                                           messages=detail)

    security.declareProtected(ManagePortal, 'manage_importTarball')

    def manage_importTarball(self, tarball):
        """ Import steps from the uploaded tarball.
        """
        if getattr(tarball, 'read', None) is not None:
            tarball = tarball.read()

        result = self.runAllImportStepsFromProfile(None, True, archive=tarball)

        steps_run = 'Steps run: %s' % ', '.join(result['steps'])

        return self.manage_importSteps(manage_tabs_message=steps_run,
                                       messages=result['messages'])

    security.declareProtected(ManagePortal, 'manage_exportSteps')
    manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir)

    security.declareProtected(ManagePortal, 'manage_exportSelectedSteps')

    def manage_exportSelectedSteps(self, ids, RESPONSE):
        """ Export the steps selected by the user.
        """
        if not ids:
            RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s' %
                              (self.absolute_url(), 'No+steps+selected.'))

        result = self._doRunExportSteps(ids)
        RESPONSE.setHeader('Content-type', 'application/x-gzip')
        RESPONSE.setHeader('Content-disposition',
                           'attachment; filename=%s' % result['filename'])
        return result['tarball']

    security.declareProtected(ManagePortal, 'manage_exportAllSteps')

    def manage_exportAllSteps(self, RESPONSE):
        """ Export all steps.
        """
        result = self.runAllExportSteps()
        RESPONSE.setHeader('Content-type', 'application/x-gzip')
        RESPONSE.setHeader('Content-disposition',
                           'attachment; filename=%s' % result['filename'])
        return result['tarball']

    security.declareProtected(ManagePortal, 'manage_upgrades')
    manage_upgrades = PageTemplateFile('setup_upgrades', _wwwdir)

    security.declareProtected(ManagePortal, 'upgradeStepMacro')
    upgradeStepMacro = PageTemplateFile('upgradeStep', _wwwdir)

    security.declareProtected(ManagePortal, 'manage_snapshots')
    manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir)

    security.declareProtected(ManagePortal, 'listSnapshotInfo')

    def listSnapshotInfo(self):
        """ Return a list of mappings describing available snapshots.

        o Keys include:

          'id' -- snapshot ID

          'title' -- snapshot title or ID

          'url' -- URL of the snapshot folder
        """
        result = []
        snapshots = self._getOb('snapshots', None)

        if snapshots:

            for id, folder in snapshots.objectItems('Folder'):

                result.append({
                    'id': id,
                    'title': folder.title_or_id(),
                    'url': folder.absolute_url()
                })
        return result

    security.declareProtected(ManagePortal, 'listProfileInfo')

    def listProfileInfo(self, for_=None):
        """ Return a list of mappings describing registered profiles.
        Base profile is listed first, extensions are sorted.

        o Keys include:

          'id' -- profile ID

          'title' -- profile title or ID

          'description' -- description of the profile

          'path' -- path to the profile within its product

          'product' -- name of the registering product
        """
        base = []
        ext = []
        for info in _profile_registry.listProfileInfo(for_):
            if info.get('type', BASE) == BASE:
                base.append(info)
            else:
                ext.append(info)
        ext.sort(lambda x, y: cmp(x['id'], y['id']))
        return base + ext

    security.declareProtected(ManagePortal, 'listContextInfos')

    def listContextInfos(self):
        """ List registered profiles and snapshots.
        """
        def readableType(x):
            if x is BASE:
                return 'base'
            elif x is EXTENSION:
                return 'extension'
            return 'unknown'

        s_infos = [{
            'id': 'snapshot-%s' % info['id'],
            'title': info['title'],
            'type': 'snapshot',
        } for info in self.listSnapshotInfo()]
        p_infos = [{
            'id': 'profile-%s' % info['id'],
            'title': info['title'],
            'type': readableType(info['type']),
        } for info in self.listProfileInfo()]

        return tuple(s_infos + p_infos)

    security.declareProtected(ManagePortal, 'getProfileImportDate')

    def getProfileImportDate(self, profile_id):
        """ See ISetupTool.
        """
        prefix = ('import-all-%s-' % profile_id).replace(':', '_')
        candidates = [
            x for x in self.objectIds('File')
            if x[:-18] == prefix and x.endswith('.log')
        ]
        if len(candidates) == 0:
            return None
        candidates.sort()
        last = candidates[-1]
        stamp = last[-18:-4]
        return '%s-%s-%sT%s:%s:%sZ' % (
            stamp[0:4],
            stamp[4:6],
            stamp[6:8],
            stamp[8:10],
            stamp[10:12],
            stamp[12:14],
        )

    security.declareProtected(ManagePortal, 'manage_createSnapshot')

    def manage_createSnapshot(self, RESPONSE, snapshot_id=None):
        """ Create a snapshot with the given ID.

        o If no ID is passed, generate one.
        """
        if snapshot_id is None:
            snapshot_id = self._mangleTimestampName('snapshot')

        self.createSnapshot(snapshot_id)

        return RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s' %
                                 (self.absolute_url(), 'Snapshot+created.'))

    security.declareProtected(ManagePortal, 'manage_showDiff')
    manage_showDiff = PageTemplateFile('sutCompare', _wwwdir)

    def manage_downloadDiff(
        self,
        lhs,
        rhs,
        missing_as_empty,
        ignore_blanks,
        RESPONSE,
    ):
        """ Crack request vars and call compareConfigurations.

        o Return the result as a 'text/plain' stream, suitable for framing.
        """
        comparison = self.manage_compareConfigurations(
            lhs,
            rhs,
            missing_as_empty,
            ignore_blanks,
        )
        RESPONSE.setHeader('Content-Type', 'text/plain')
        return _PLAINTEXT_DIFF_HEADER % (lhs, rhs, comparison)

    security.declareProtected(ManagePortal, 'manage_compareConfigurations')

    def manage_compareConfigurations(
        self,
        lhs,
        rhs,
        missing_as_empty,
        ignore_blanks,
    ):
        """ Crack request vars and call compareConfigurations.
        """
        lhs_context = self._getImportContext(lhs)
        rhs_context = self._getImportContext(rhs)

        return self.compareConfigurations(
            lhs_context,
            rhs_context,
            missing_as_empty,
            ignore_blanks,
        )

    security.declareProtected(ManagePortal, 'manage_stepRegistry')
    manage_stepRegistry = PageTemplateFile('sutManage', _wwwdir)

    security.declareProtected(ManagePortal, 'manage_deleteImportSteps')

    def manage_deleteImportSteps(self, ids, request=None):
        if request is None:
            request = self.REQUEST
        for id in ids:
            self._import_registry.unregisterStep(id)
        self._p_changed = True
        url = self.absolute_url()
        request.RESPONSE.redirect("%s/manage_stepRegistry" % url)

    security.declareProtected(ManagePortal, 'manage_deleteExportSteps')

    def manage_deleteExportSteps(self, ids, request=None):
        if request is None:
            request = self.REQUEST
        for id in ids:
            self._export_registry.unregisterStep(id)
        self._p_changed = True
        url = self.absolute_url()
        request.RESPONSE.redirect("%s/manage_stepRegistry" % url)

    #
    # Upgrades management
    #
    security.declareProtected(ManagePortal, 'getLastVersionForProfile')

    def getLastVersionForProfile(self, profile_id):
        """Return the last upgraded version for the specified profile.
        """
        version = self._profile_upgrade_versions.get(profile_id, 'unknown')
        return version

    security.declareProtected(ManagePortal, 'setLastVersionForProfile')

    def setLastVersionForProfile(self, profile_id, version):
        """Set the last upgraded version for the specified profile.
        """
        if isinstance(version, basestring):
            version = tuple(version.split('.'))
        prof_versions = self._profile_upgrade_versions.copy()
        prof_versions[profile_id] = version
        self._profile_upgrade_versions = prof_versions

    security.declareProtected(ManagePortal, 'getVersionForProfile')

    def getVersionForProfile(self, profile_id):
        """Return the registered filesystem version for the specified
        profile.
        """
        return self.getProfileInfo(profile_id).get('version', 'unknown')

    security.declareProtected(ManagePortal, 'profileExists')

    def profileExists(self, profile_id):
        """Check if a profile exists."""
        try:
            self.getProfileInfo(profile_id)
        except KeyError:
            return False
        else:
            return True

    security.declareProtected(ManagePortal, "getProfileInfo")

    def getProfileInfo(self, profile_id):
        if profile_id.startswith("profile-"):
            profile_id = profile_id[len('profile-'):]
        elif profile_id.startswith("snapshot-"):
            profile_id = profile_id[len('snapshot-'):]
        return _profile_registry.getProfileInfo(profile_id)

    security.declareProtected(ManagePortal, 'getDependenciesForProfile')

    def getDependenciesForProfile(self, profile_id):
        if profile_id.startswith("snapshot-"):
            return ()

        if not self.profileExists(profile_id):
            raise KeyError, profile_id
        try:
            return self.getProfileInfo(profile_id).get('dependencies', ())
        except KeyError:
            return ()

    security.declareProtected(ManagePortal, 'listProfilesWithUpgrades')

    def listProfilesWithUpgrades(self):
        return listProfilesWithUpgrades()

    security.declarePrivate('_massageUpgradeInfo')

    def _massageUpgradeInfo(self, info):
        """Add a couple of data points to the upgrade info dictionary.
        """
        info = info.copy()
        info['haspath'] = info['source'] and info['dest']
        info['ssource'] = '.'.join(info['source'] or ('all', ))
        info['sdest'] = '.'.join(info['dest'] or ('all', ))
        info['done'] = (not info['proposed']
                        and info['step'].checker is not None
                        and not info['step'].checker(self))
        return info

    security.declareProtected(ManagePortal, 'listUpgrades')

    def listUpgrades(self, profile_id, show_old=False):
        """Get the list of available upgrades.
        """
        if show_old:
            source = None
        else:
            source = self.getLastVersionForProfile(profile_id)
        upgrades = listUpgradeSteps(self, profile_id, source)
        res = []
        for info in upgrades:
            if type(info) == list:
                subset = []
                for subinfo in info:
                    subset.append(self._massageUpgradeInfo(subinfo))
                res.append(subset)
            else:
                res.append(self._massageUpgradeInfo(info))
        return res

    security.declareProtected(ManagePortal, 'manage_doUpgrades')

    def manage_doUpgrades(self, request=None):
        """Perform all selected upgrade steps.
        """
        if request is None:
            request = self.REQUEST
        logger = logging.getLogger('GenericSetup')
        steps_to_run = request.form.get('upgrades', [])
        profile_id = request.get('profile_id', '')
        step = None
        for step_id in steps_to_run:
            step = _upgrade_registry.getUpgradeStep(profile_id, step_id)
            if step is not None:
                step.doStep(self)
                msg = "Ran upgrade step %s for profile %s" % (step.title,
                                                              profile_id)
                logger.log(logging.INFO, msg)

        # We update the profile version to the last one we have reached
        # with running an upgrade step.
        if step and step.dest is not None and step.checker is None:
            self.setLastVersionForProfile(profile_id, step.dest)

        url = self.absolute_url()
        request.RESPONSE.redirect("%s/manage_upgrades?saved=%s" %
                                  (url, profile_id))

    #
    #   Helper methods
    #
    security.declarePrivate('_getImportContext')

    def _getImportContext(self, context_id, should_purge=None, archive=None):
        """ Crack ID and generate appropriate import context.
        """
        encoding = self.getEncoding()

        if context_id is not None:
            if context_id.startswith('profile-'):
                context_id = context_id[len('profile-'):]
                info = _profile_registry.getProfileInfo(context_id)

                if info.get('product'):
                    path = os.path.join(_getProductPath(info['product']),
                                        info['path'])
                else:
                    path = info['path']
                if should_purge is None:
                    should_purge = (info.get('type') != EXTENSION)
                return DirectoryImportContext(self, path, should_purge,
                                              encoding)

            elif context_id.startswith('snapshot-'):
                context_id = context_id[len('snapshot-'):]
                if should_purge is None:
                    should_purge = True
                return SnapshotImportContext(self, context_id, should_purge,
                                             encoding)

        if archive is not None:
            return TarballImportContext(
                tool=self,
                archive_bits=archive,
                encoding='UTF8',
                should_purge=should_purge,
            )

        raise KeyError, 'Unknown context "%s"' % context_id

    security.declarePrivate('_updateImportStepsRegistry')

    def _updateImportStepsRegistry(self, context, encoding):
        """ Update our import steps registry from our profile.
        """
        xml = context.readDataFile(IMPORT_STEPS_XML)
        if xml is None:
            return

        info_list = self._import_registry.parseXML(xml, encoding)

        for step_info in info_list:

            id = step_info['id']
            version = step_info['version']
            handler = step_info['handler']
            dependencies = tuple(step_info.get('dependencies', ()))
            title = step_info.get('title', id)
            description = ''.join(step_info.get('description', []))

            self._import_registry.registerStep(
                id=id,
                version=version,
                handler=handler,
                dependencies=dependencies,
                title=title,
                description=description,
            )

    security.declarePrivate('_updateExportStepsRegistry')

    def _updateExportStepsRegistry(self, context, encoding):
        """ Update our export steps registry from our profile.
        """
        xml = context.readDataFile(EXPORT_STEPS_XML)
        if xml is None:
            return

        info_list = self._export_registry.parseXML(xml, encoding)

        for step_info in info_list:

            id = step_info['id']
            handler = step_info['handler']
            title = step_info.get('title', id)
            description = ''.join(step_info.get('description', []))

            self._export_registry.registerStep(
                id=id,
                handler=handler,
                title=title,
                description=description,
            )

    security.declarePrivate('_doRunImportStep')

    def _doRunImportStep(self, step_id, context):
        """ Run a single import step, using a pre-built context.
        """
        __traceback_info__ = step_id
        marker = object()

        handler = self.getImportStep(step_id)

        if handler is marker:
            raise ValueError('Invalid import step: %s' % step_id)

        if handler is None:
            msg = 'Step %s has an invalid import handler' % step_id
            logger = logging.getLogger('GenericSetup')
            logger.error(msg)
            return 'ERROR: ' + msg

        return handler(context)

    security.declarePrivate('_doRunExportSteps')

    def _doRunExportSteps(self, steps):
        """ See ISetupTool.
        """
        context = TarballExportContext(self)
        messages = {}
        marker = object()

        for step_id in steps:

            handler = self.getExportStep(step_id, marker)

            if handler is marker:
                raise ValueError('Invalid export step: %s' % step_id)

            if handler is None:
                msg = 'Step %s has an invalid import handler' % step_id
                logger = logging.getLogger('GenericSetup')
                logger.error(msg)
                messages[step_id] = msg
            else:
                messages[step_id] = handler(context)

        return {
            'steps': steps,
            'messages': messages,
            'tarball': context.getArchive(),
            'filename': context.getArchiveFilename()
        }

    security.declareProtected(ManagePortal, 'getProfileDependencyChain')

    def getProfileDependencyChain(self, profile_id, seen=None):
        if seen is None:
            seen = set()
        elif profile_id in seen:
            return []  # cycle break
        seen.add(profile_id)
        chain = []

        dependencies = self.getDependenciesForProfile(profile_id)
        for dependency in dependencies:
            chain.extend(self.getProfileDependencyChain(dependency, seen))

        chain.append(profile_id)

        return chain

    security.declarePrivate('_runImportStepsFromContext')

    def _runImportStepsFromContext(self,
                                   steps=None,
                                   purge_old=None,
                                   profile_id=None,
                                   archive=None,
                                   ignore_dependencies=False,
                                   seen=None):

        if profile_id is not None and not ignore_dependencies:
            try:
                chain = self.getProfileDependencyChain(profile_id)
            except KeyError, e:
                logger = logging.getLogger('GenericSetup')
                logger.error('Unknown step in dependency chain: %s' % str(e))
                raise
        else:
Beispiel #17
0
class DynamicType:
    """
    Mixin for portal content that allows the object to take on
    a dynamic type property.
    """

    implements(IDynamicType)

    portal_type = None

    security = ClassSecurityInfo()

    def _setPortalTypeName(self, pt):
        """ Set the portal type name.

        Called by portal_types during construction, records an ID that will be
        used later to locate the correct ContentTypeInformation.
        """
        self.portal_type = pt

    security.declarePublic('getPortalTypeName')

    def getPortalTypeName(self):
        """ Get the portal type name that can be passed to portal_types.
        """
        pt = self.portal_type
        if callable(pt):
            pt = pt()
        return pt

    # deprecated alias
    _getPortalTypeName = getPortalTypeName

    security.declarePublic('getTypeInfo')

    def getTypeInfo(self):
        """ Get the TypeInformation object specified by the portal type.
        """
        tool = getToolByName(self, 'portal_types', None)
        if tool is None:
            return None
        return tool.getTypeInfo(self)  # Can return None.

    security.declarePublic('getActionInfo')

    def getActionInfo(self,
                      action_chain,
                      check_visibility=0,
                      check_condition=0):
        """ Get an Action info mapping specified by a chain of actions.
        """
        ti = self.getTypeInfo()
        if ti:
            return ti.getActionInfo(action_chain, self, check_visibility,
                                    check_condition)
        else:
            msg = 'Action "%s" not available for %s' % (action_chain, '/'.join(
                self.getPhysicalPath()))
            raise ValueError(msg)

    # Support for dynamic icons

    security.declarePublic('getIcon')

    def getIcon(self, relative_to_portal=0):
        """
        Using this method allows the content class
        creator to grab icons on the fly instead of using a fixed
        attribute on the class.
        """
        ti = self.getTypeInfo()
        if ti is not None:
            icon = quote(ti.getIcon())
            if icon:
                if relative_to_portal:
                    return icon
                else:
                    # Relative to REQUEST['BASEPATH1']
                    portal_url = getToolByName(self, 'portal_url')
                    res = portal_url(relative=1) + '/' + icon
                    while res[:1] == '/':
                        res = res[1:]
                    return res
        return 'misc_/OFSP/dtmldoc.gif'

    security.declarePublic('icon')
    icon = getIcon  # For the ZMI

    def __before_publishing_traverse__(self, arg1, arg2=None):
        """ Pre-traversal hook.
        """
        # XXX hack around a bug(?) in BeforeTraverse.MultiHook
        REQUEST = arg2 or arg1

        if REQUEST['REQUEST_METHOD'] not in ('GET', 'POST'):
            return

        stack = REQUEST['TraversalRequestNameStack']
        key = stack and stack[-1] or '(Default)'

        # if there's a Zope3-style default view name set and the
        # corresponding view exists, take that in favour of the FTI's
        # default view
        if key == '(Default)':
            viewname = queryDefaultViewName(self, REQUEST)
            if (viewname and queryMultiAdapter(
                (self, REQUEST), name=viewname) is not None):
                stack.append(viewname)
                REQUEST._hacked_path = 1
                return

        ti = self.getTypeInfo()
        method_id = ti and ti.queryMethodID(key, context=self)
        if method_id:
            if key != '(Default)':
                stack.pop()
            if method_id != '(Default)':
                stack.append(method_id)
            REQUEST._hacked_path = 1
Beispiel #18
0
class CookieAuthHelper(Folder, BasePlugin):
    """ Multi-plugin for managing details of Cookie Authentication. """

    meta_type = 'Cookie Auth Helper'
    cookie_name = '__ginger_snap'
    login_path = 'login_form'
    security = ClassSecurityInfo()

    _properties = ( { 'id'    : 'title'
                    , 'label' : 'Title'
                    , 'type'  : 'string'
                    , 'mode'  : 'w'
                    }
                  , { 'id'    : 'cookie_name'
                    , 'label' : 'Cookie Name'
                    , 'type'  : 'string'
                    , 'mode'  : 'w'
                    }
                  , { 'id'    : 'login_path'
                    , 'label' : 'Login Form'
                    , 'type'  : 'string'
                    , 'mode'  : 'w'
                    }
                  )

    manage_options = ( BasePlugin.manage_options[:1]
                     + Folder.manage_options[:1]
                     + Folder.manage_options[2:]
                     )

    def __init__(self, id, title=None, cookie_name=''):
        self._setId(id)
        self.title = title

        if cookie_name:
            self.cookie_name = cookie_name


    security.declarePrivate('extractCredentials')
    def extractCredentials(self, request):
        """ Extract credentials from cookie or 'request'. """
        creds = {}
        cookie = request.get(self.cookie_name, '')
        # Look in the request.form for the names coming from the login form
        login = request.form.get('__ac_name', '')

        if login and request.form.has_key('__ac_password'):
            creds['login'] = login
            creds['password'] = request.form.get('__ac_password', '')

        elif cookie and cookie != 'deleted':
            raw = unquote(cookie)
            try:
                cookie_val = decodestring(raw)
            except Error:
                # Cookie is in a different format, so it is not ours
                return creds

            try:
                login, password = cookie_val.split(':')
            except ValueError:
                # Cookie is in a different format, so it is not ours
                return creds

            try:
                creds['login'] = login.decode('hex')
                creds['password'] = password.decode('hex')
            except TypeError:
                # Cookie is in a different format, so it is not ours
                return {}

        if creds:
            creds['remote_host'] = request.get('REMOTE_HOST', '')

            try:
                creds['remote_address'] = request.getClientAddr()
            except AttributeError:
                creds['remote_address'] = request.get('REMOTE_ADDR', '')

        return creds


    security.declarePrivate('challenge')
    def challenge(self, request, response, **kw):
        """ Challenge the user for credentials. """
        return self.unauthorized()


    security.declarePrivate('updateCredentials')
    def updateCredentials(self, request, response, login, new_password):
        """ Respond to change of credentials (NOOP for basic auth). """
        cookie_str = '%s:%s' % (login.encode('hex'), new_password.encode('hex'))
        cookie_val = encodestring(cookie_str)
        cookie_val = cookie_val.rstrip()
        response.setCookie(self.cookie_name, quote(cookie_val), path='/')


    security.declarePrivate('resetCredentials')
    def resetCredentials(self, request, response):
        """ Raise unauthorized to tell browser to clear credentials. """
        response.expireCookie(self.cookie_name, path='/')


    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        """ Setup tasks upon instantiation """
        if not 'login_form' in self.objectIds():
            login_form = ZopePageTemplate( id='login_form'
                                           , text=BASIC_LOGIN_FORM
                                           )
            login_form.title = 'Login Form'
            login_form.manage_permission(view, roles=['Anonymous'], acquire=1)
            self._setObject( 'login_form', login_form, set_owner=0 )


    security.declarePrivate('unauthorized')
    def unauthorized(self):
        req = self.REQUEST
        resp = req['RESPONSE']

        # If we set the auth cookie before, delete it now.
        if resp.cookies.has_key(self.cookie_name):
            del resp.cookies[self.cookie_name]

        # Redirect if desired.
        url = self.getLoginURL()
        if url is not None:
            came_from = req.get('came_from', None)

            if came_from is None:
                came_from = req.get('ACTUAL_URL', '')
                query = req.get('QUERY_STRING')
                if query:
                    if not query.startswith('?'):
                        query = '?' + query
                    came_from = came_from + query
            else:
                # If came_from contains a value it means the user
                # must be coming through here a second time
                # Reasons could be typos when providing credentials
                # or a redirect loop (see below)
                req_url = req.get('ACTUAL_URL', '')

                if req_url and req_url == url:
                    # Oops... The login_form cannot be reached by the user -
                    # it might be protected itself due to misconfiguration -
                    # the only sane thing to do is to give up because we are
                    # in an endless redirect loop.
                    return 0

            if '?' in url:
                sep = '&'
            else:
                sep = '?'
            url = '%s%scame_from=%s' % (url, sep, quote(came_from))
            resp.redirect(url, lock=1)
            resp.setHeader('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
            resp.setHeader('Cache-Control', 'no-cache')
            return 1

        # Could not challenge.
        return 0


    security.declarePrivate('getLoginURL')
    def getLoginURL(self):
        """ Where to send people for logging in """
        if self.login_path.startswith('/') or '://' in self.login_path:
            return self.login_path
        elif self.login_path != '':
            return '%s/%s' % (self.absolute_url(), self.login_path)
        else:
            return None

    security.declarePublic('login')
    def login(self):
        """ Set a cookie and redirect to the url that we tried to
        authenticate against originally.
        """
        request = self.REQUEST
        response = request['RESPONSE']

        login = request.get('__ac_name', '')
        password = request.get('__ac_password', '')

        # In order to use the CookieAuthHelper for its nice login page
        # facility but store and manage credentials somewhere else we need
        # to make sure that upon login only plugins activated as
        # IUpdateCredentialPlugins get their updateCredentials method
        # called. If the method is called on the CookieAuthHelper it will
        # simply set its own auth cookie, to the exclusion of any other
        # plugins that might want to store the credentials.
        pas_instance = self._getPAS()

        if pas_instance is not None:
            pas_instance.updateCredentials(request, response, login, password)

        came_from = request.form['came_from']

        return response.redirect(came_from)
Beispiel #19
0
class ActionProviderBase:
    """ Provide ActionTabs and management methods for ActionProviders
    """

    implements(IActionProvider)

    security = ClassSecurityInfo()

    _actions = ()

    _actions_form = DTMLFile('editToolsActions', _dtmldir)

    manage_options = ({
        'label': 'Actions',
        'action': 'manage_editActionsForm',
        'help': ('CMFCore', 'Actions.stx')
    }, )

    #
    #   ActionProvider interface
    #
    security.declarePrivate('listActions')

    def listActions(self, info=None, object=None):
        """ List all the actions defined by a provider.
        """
        oldstyle_actions = self._actions or ()
        if oldstyle_actions:
            warn(
                'Old-style actions are deprecated and will be removed in CMF '
                '2.4. Use Action and Action Category objects instead.',
                DeprecationWarning,
                stacklevel=2)

        return oldstyle_actions

    security.declarePrivate('getActionObject')

    def getActionObject(self, action):
        """Return the actions object or None if action doesn't exist.
        """
        # separate cataegory and id from action
        sep = action.rfind('/')
        if sep == -1:
            raise ValueError('Actions must have the format <category>/<id>.')
        category, id = action[:sep], action[sep + 1:]

        # search for action and return first one found
        for ai in self.listActions():
            try:
                if id == ai.getId() and category == ai.getCategory():
                    return ai
            except AttributeError:
                continue

        # no action found
        return None

    security.declarePublic('listActionInfos')

    def listActionInfos(self,
                        action_chain=None,
                        object=None,
                        check_visibility=1,
                        check_permissions=1,
                        check_condition=1,
                        max=-1):
        # List ActionInfo objects.
        # (method is without docstring to disable publishing)
        #
        ec = self._getExprContext(object)
        actions = self.listActions(object=object)
        actions = [ActionInfo(action, ec) for action in actions]

        if action_chain:
            filtered_actions = []
            if isinstance(action_chain, basestring):
                action_chain = (action_chain, )
            for action_ident in action_chain:
                sep = action_ident.rfind('/')
                category, id = action_ident[:sep], action_ident[sep + 1:]
                for ai in actions:
                    if id == ai['id'] and category == ai['category']:
                        filtered_actions.append(ai)
            actions = filtered_actions

        action_infos = []
        for ai in actions:
            if check_visibility and not ai['visible']:
                continue
            if check_permissions and not ai['allowed']:
                continue
            if check_condition and not ai['available']:
                continue
            action_infos.append(ai)
            if max + 1 and len(action_infos) >= max:
                break
        return action_infos

    security.declarePublic('getActionInfo')

    def getActionInfo(self,
                      action_chain,
                      object=None,
                      check_visibility=0,
                      check_condition=0):
        """ Get an ActionInfo object specified by a chain of actions.
        """
        action_infos = self.listActionInfos(action_chain,
                                            object,
                                            check_visibility=check_visibility,
                                            check_permissions=False,
                                            check_condition=check_condition)
        if not action_infos:
            if object is None:
                provider = self
            else:
                provider = object
            msg = 'Action "%s" not available for %s' % (action_chain, '/'.join(
                provider.getPhysicalPath()))
            raise ValueError(msg)
        for ai in action_infos:
            if ai['allowed']:
                return ai
        raise AccessControl_Unauthorized('You are not allowed to access any '
                                         'of the specified Actions.')

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_editActionsForm')

    def manage_editActionsForm(self, REQUEST, manage_tabs_message=None):
        """ Show the 'Actions' management tab.
        """
        actions = []
        for action in self.listActions():
            # The Actions tab currently only shows old-style actions,
            # so we need to weed out everything else.
            if getattr(action, 'getMapping', None) is not None:
                actions.append(action.getMapping())

        # possible_permissions is in AccessControl.Role.RoleManager.
        pp = self.possible_permissions()
        return self._actions_form(self,
                                  REQUEST,
                                  actions=actions,
                                  possible_permissions=pp,
                                  management_view='Actions',
                                  manage_tabs_message=manage_tabs_message)

    security.declareProtected(ManagePortal, 'addAction')

    def addAction(self,
                  id,
                  name,
                  action,
                  condition,
                  permission,
                  category,
                  visible=1,
                  icon_expr='',
                  link_target='',
                  REQUEST=None):
        """ Add an action to our list.
        """
        if not name:
            raise ValueError('A name is required.')

        action = action and str(action) or ''
        condition = condition and str(condition) or ''

        if not isinstance(permission, tuple):
            permission = (str(permission), )

        new_actions = self._cloneActions()

        new_action = ActionInformation(id=str(id),
                                       title=str(name),
                                       category=str(category),
                                       condition=condition,
                                       permissions=permission,
                                       visible=bool(visible),
                                       action=action,
                                       icon_expr=icon_expr,
                                       link_target=link_target)

        new_actions.append(new_action)
        self._actions = tuple(new_actions)

        if REQUEST is not None:
            return self.manage_editActionsForm(REQUEST,
                                               manage_tabs_message='Added.')

    security.declareProtected(ManagePortal, 'changeActions')

    def changeActions(self, properties=None, REQUEST=None):
        """ Update our list of actions.
        """
        if properties is None:
            properties = REQUEST

        actions = []

        for index in range(len(self._actions)):
            actions.append(self._extractAction(properties, index))

        self._actions = tuple(actions)

        if REQUEST is not None:
            return self.manage_editActionsForm(
                REQUEST, manage_tabs_message='Actions changed.')

    security.declareProtected(ManagePortal, 'deleteActions')

    def deleteActions(self, selections=(), REQUEST=None):
        """ Delete actions indicated by indexes in 'selections'.
        """
        sels = list(map(int, selections))  # Convert to a list of integers.

        old_actions = self._cloneActions()
        new_actions = []

        for index in range(len(old_actions)):
            if index not in sels:
                new_actions.append(old_actions[index])

        self._actions = tuple(new_actions)

        if REQUEST is not None:
            return self.manage_editActionsForm(
                REQUEST,
                manage_tabs_message=('Deleted %d action(s).' % len(sels)))

    security.declareProtected(ManagePortal, 'moveUpActions')

    def moveUpActions(self, selections=(), REQUEST=None):
        """ Move the specified actions up one slot in our list.
        """
        sels = list(map(int, selections))  # Convert to a list of integers.
        sels.sort()

        new_actions = self._cloneActions()

        for idx in sels:
            idx2 = idx - 1
            if idx2 < 0:
                # Wrap to the bottom.
                idx2 = len(new_actions) - 1
            # Swap.
            a = new_actions[idx2]
            new_actions[idx2] = new_actions[idx]
            new_actions[idx] = a

        self._actions = tuple(new_actions)

        if REQUEST is not None:
            return self.manage_editActionsForm(
                REQUEST,
                manage_tabs_message=('Moved up %d action(s).' % len(sels)))

    security.declareProtected(ManagePortal, 'moveDownActions')

    def moveDownActions(self, selections=(), REQUEST=None):
        """ Move the specified actions down one slot in our list.
        """
        sels = list(map(int, selections))  # Convert to a list of integers.
        sels.sort()
        sels.reverse()

        new_actions = self._cloneActions()

        for idx in sels:
            idx2 = idx + 1
            if idx2 >= len(new_actions):
                # Wrap to the top.
                idx2 = 0
            # Swap.
            a = new_actions[idx2]
            new_actions[idx2] = new_actions[idx]
            new_actions[idx] = a

        self._actions = tuple(new_actions)

        if REQUEST is not None:
            return self.manage_editActionsForm(
                REQUEST,
                manage_tabs_message=('Moved down %d action(s).' % len(sels)))

    #
    #   Helper methods
    #
    security.declarePrivate('_cloneActions')

    def _cloneActions(self):
        """ Return a list of actions, cloned from our current list.
        """
        return map(lambda x: x.clone(), list(self._actions))

    security.declarePrivate('_extractAction')

    def _extractAction(self, properties, index):
        """ Extract an ActionInformation from the funky form properties.
        """
        id = str(properties.get('id_%d' % index, ''))
        title = str(properties.get('name_%d' % index, ''))
        action = str(properties.get('action_%d' % index, ''))
        icon_expr = str(properties.get('icon_expr_%d' % index, ''))
        condition = str(properties.get('condition_%d' % index, ''))
        category = str(properties.get('category_%d' % index, ''))
        visible = bool(properties.get('visible_%d' % index, False))
        permissions = properties.get('permission_%d' % index, ())
        link_target = str(properties.get('link_target_%d' % index, ''))

        if not title:
            raise ValueError('A title is required.')

        if category == '':
            category = 'object'

        if isinstance(permissions, basestring):
            permissions = (permissions, )

        return ActionInformation(id=id,
                                 title=title,
                                 action=action,
                                 condition=condition,
                                 permissions=permissions,
                                 category=category,
                                 visible=visible,
                                 icon_expr=icon_expr,
                                 link_target=link_target)

    def _getOAI(self, object):
        return getOAI(self, object)

    def _getExprContext(self, object):
        return getExprContext(self, object)
Beispiel #20
0
class RegistrationTool(UniqueObject, SimpleItem):
    """ Create and modify users by making calls to portal_membership.
    """

    id = 'portal_registration'
    meta_type = 'CMF Registration Tool'
    member_id_pattern = ''
    default_member_id_pattern = "^[A-Za-z][A-Za-z0-9_]*$"
    _ALLOWED_MEMBER_ID_PATTERN = re.compile(default_member_id_pattern)

    security = ClassSecurityInfo()

    manage_options = (({
        'label': 'Overview',
        'action': 'manage_overview'
    }, {
        'label': 'Configure',
        'action': 'manage_configuration'
    }) + SimpleItem.manage_options)

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainRegistrationTool', _dtmldir)

    security.declareProtected(ManagePortal, 'manage_configuration')
    manage_configuration = DTMLFile('configureRegistrationTool', _dtmldir)

    @security.protected(ManagePortal)
    def manage_editIDPattern(self, pattern, REQUEST=None):
        """Edit the allowable member ID pattern TTW"""
        pattern.strip()

        if len(pattern) > 0:
            self.member_id_pattern = pattern
            self._ALLOWED_MEMBER_ID_PATTERN = re.compile(pattern)
        else:
            self.member_id_pattern = ''
            self._ALLOWED_MEMBER_ID_PATTERN = re.compile(
                self.default_member_id_pattern)

        if REQUEST is not None:
            msg = 'Member ID Pattern changed'
            return self.manage_configuration(manage_tabs_message=msg)

    @security.protected(ManagePortal)
    def getIDPattern(self):
        """ Return the currently-used member ID pattern """
        return self.member_id_pattern

    @security.protected(ManagePortal)
    def getDefaultIDPattern(self):
        """ Return the currently-used member ID pattern """
        return self.default_member_id_pattern

    #
    #   'portal_registration' interface methods
    #
    @security.public
    def isRegistrationAllowed(self, REQUEST):
        '''Returns a boolean value indicating whether the user
        is allowed to add a member to the portal.
        '''
        return _checkPermission(AddPortalMember, self.aq_inner.aq_parent)

    @security.public
    def testPasswordValidity(self, password, confirm=None):
        '''If the password is valid, returns None.  If not, returns
        a string explaining why.
        '''
        return None

    @security.public
    def testPropertiesValidity(self, new_properties, member=None):
        '''If the properties are valid, returns None.  If not, returns
        a string explaining why.
        '''
        return None

    @security.public
    def generatePassword(self):
        """ Generate a valid password.
        """
        # we don't use these to avoid typos: OQ0Il1
        chars = 'ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789'
        return ''.join([choice(chars) for i in range(6)])

    @security.protected(AddPortalMember)
    def addMember(self,
                  id,
                  password,
                  roles=('Member', ),
                  domains='',
                  properties=None,
                  REQUEST=None):
        # XXX Do not make this a normal method comment. Doing so makes
        # this method publishable

        # Creates a PortalMember and returns it. The properties argument
        # can be a mapping with additional member properties. Raises an
        # exception if the given id already exists, the password does not
        # comply with the policy in effect, or the authenticated user is not
        # allowed to grant one of the roles listed (where Member is a special
        # role that can always be granted); these conditions should be
        # detected before the fact so that a cleaner message can be printed.

        if not self.isMemberIdAllowed(id):
            raise ValueError(
                _(u'The login name you selected is already in '
                  u'use or is not valid. Please choose another.'))

        failMessage = self.testPasswordValidity(password)
        if failMessage is not None:
            raise ValueError(failMessage)

        if properties is not None:
            failMessage = self.testPropertiesValidity(properties)
            if failMessage is not None:
                raise ValueError(failMessage)

        # Limit the granted roles.
        # Anyone is always allowed to grant the 'Member' role.
        _limitGrantedRoles(roles, self, ('Member', ))

        mtool = getUtility(IMembershipTool)
        mtool.addMember(id, password, roles, domains, properties)

        member = mtool.getMemberById(id)
        self.afterAdd(member, id, password, properties)
        return member

    @security.protected(AddPortalMember)
    def isMemberIdAllowed(self, id):
        '''Returns 1 if the ID is not in use and is not reserved.
        '''
        if len(id) < 1 or id == 'Anonymous User':
            return 0
        if not self._ALLOWED_MEMBER_ID_PATTERN.match(id):
            return 0
        mtool = getUtility(IMembershipTool)
        if mtool.getMemberById(id) is not None:
            return 0
        return 1

    @security.public
    def afterAdd(self, member, id, password, properties):
        '''Called by portal_registration.addMember()
        after a member has been added successfully.'''
        pass

    @security.protected(MailForgottenPassword)
    def mailPassword(self, forgotten_userid, REQUEST):
        '''Email a forgotten password to a member.  Raises an exception
        if user ID is not found.
        '''
        raise NotImplementedError
Beispiel #21
0
class Traversable(object):

    security = ClassSecurityInfo()

    @security.public
    def absolute_url(self, relative=0):
        """Return the absolute URL of the object.

        This a canonical URL based on the object's physical
        containment path.  It is affected by the virtual host
        configuration, if any, and can be used by external
        agents, such as a browser, to address the object.

        If the relative argument is provided, with a true value, then
        the value of virtual_url_path() is returned.

        Some Products incorrectly use '/'+absolute_url(1) as an
        absolute-path reference.  This breaks in certain virtual
        hosting situations, and should be changed to use
        absolute_url_path() instead.
        """
        if relative:
            return self.virtual_url_path()

        spp = self.getPhysicalPath()

        try:
            toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
        except AttributeError:
            return path2url(spp[1:])
        return toUrl(spp)

    @security.public
    def absolute_url_path(self):
        """Return the path portion of the absolute URL of the object.

        This includes the leading slash, and can be used as an
        'absolute-path reference' as defined in RFC 2396.
        """
        spp = self.getPhysicalPath()
        try:
            toUrl = aq_acquire(self, 'REQUEST').physicalPathToURL
        except AttributeError:
            return path2url(spp) or '/'
        return toUrl(spp, relative=1) or '/'

    @security.public
    def virtual_url_path(self):
        """Return a URL for the object, relative to the site root.

        If a virtual host is configured, the URL is a path relative to
        the virtual host's root object.  Otherwise, it is the physical
        path.  In either case, the URL does not begin with a slash.
        """
        spp = self.getPhysicalPath()
        try:
            toVirt = aq_acquire(self, 'REQUEST').physicalPathToVirtualPath
        except AttributeError:
            return path2url(spp[1:])
        return path2url(toVirt(spp))

    # decorators did not work on variables
    security.declarePrivate('getPhysicalRoot')  # NOQA: D001
    getPhysicalRoot = Acquired

    @security.public
    def getPhysicalPath(self):
        # Get the physical path of the object.
        #
        # Returns a path (an immutable sequence of strings) that can be used to
        # access this object again later, for example in a copy/paste
        # operation.  getPhysicalRoot() and getPhysicalPath() are designed to
        # operate together.

        # This implementation is optimized to avoid excessive amounts of
        # function calls while walking up from an object on a deep level.
        try:
            id = self.id or self.getId()
        except AttributeError:
            id = self.getId()

        path = (id, )
        p = aq_parent(aq_inner(self))
        if p is None:
            return path

        func = self.getPhysicalPath.__func__
        while p is not None:
            if func is p.getPhysicalPath.__func__:
                try:
                    pid = p.id or p.getId()
                except AttributeError:
                    pid = p.getId()

                path = (pid, ) + path
                p = aq_parent(aq_inner(p))
            else:
                if IApplication.providedBy(p):
                    path = ('', ) + path
                else:
                    path = p.getPhysicalPath() + path
                break

        return path

    @security.private
    def unrestrictedTraverse(self, path, default=_marker, restricted=False):
        """Lookup an object by path.

        path -- The path to the object. May be a sequence of strings or a slash
        separated string. If the path begins with an empty path element
        (i.e., an empty string or a slash) then the lookup is performed
        from the application root. Otherwise, the lookup is relative to
        self. Two dots (..) as a path element indicates an upward traversal
        to the acquisition parent.

        default -- If provided, this is the value returned if the path cannot
        be traversed for any reason (i.e., no object exists at that path or
        the object is inaccessible).

        restricted -- If false (default) then no security checking is
        performed. If true, then all of the objects along the path are
        validated with the security machinery. Usually invoked using
        restrictedTraverse().
        """
        if not path:
            return self

        if isinstance(path, str):
            path = path.split('/')
        else:
            path = list(path)
            for part in path:
                if not isinstance(part, str):
                    raise TypeError(
                        "path should be a string or an iterable of strings")

        REQUEST = {'TraversalRequestNameStack': path}
        path.reverse()
        path_pop = path.pop

        if len(path) > 1 and not path[0]:
            # Remove trailing slash
            path_pop(0)

        if restricted:
            validate = getSecurityManager().validate

        if not path[-1]:
            # If the path starts with an empty string, go to the root first.
            path_pop()
            obj = self.getPhysicalRoot()
            if restricted:
                validate(None, None, None, obj)  # may raise Unauthorized
        else:
            obj = self

        try:
            while path:
                name = path_pop()
                __traceback_info__ = path, name

                if name[0] == '_':
                    # Never allowed in a URL.
                    raise NotFound(name)

                if name == '..':
                    next = aq_parent(obj)
                    if next is not None:
                        if restricted and not validate(obj, obj, name, next):
                            raise Unauthorized(name)
                        obj = next
                        continue

                bobo_traverse = getattr(obj, '__bobo_traverse__', None)
                try:
                    if (name and name[:1] in '@+' and name != '+'
                            and nsParse(name)[1]):
                        # Process URI segment parameters.
                        ns, nm = nsParse(name)
                        try:
                            next = namespaceLookup(ns, nm, obj,
                                                   aq_acquire(self, 'REQUEST'))
                            if IAcquirer.providedBy(next):
                                next = next.__of__(obj)
                            if restricted and not validate(
                                    obj, obj, name, next):
                                raise Unauthorized(name)
                        except LocationError:
                            raise AttributeError(name)

                    else:
                        next = UseTraversalDefault  # indicator
                        try:
                            if bobo_traverse is not None:
                                next = bobo_traverse(REQUEST, name)
                                if restricted:
                                    if aq_base(next) is not next:
                                        # The object is wrapped, so the
                                        # acquisition context is the container.
                                        container = aq_parent(aq_inner(next))
                                    elif getattr(next, '__self__',
                                                 None) is not None:
                                        # Bound method, the bound instance
                                        # is the container
                                        container = next.__self__
                                    elif getattr(aq_base(obj), name,
                                                 _marker) is next:
                                        # Unwrapped direct attribute of the
                                        # object so object is the container
                                        container = obj
                                    else:
                                        # Can't determine container
                                        container = None
                                    # If next is a simple unwrapped property,
                                    # its parentage is indeterminate, but it
                                    # may have been acquired safely. In this
                                    # case validate will raise an error, and
                                    # we can explicitly check that our value
                                    # was acquired safely.
                                    try:
                                        ok = validate(obj, container, name,
                                                      next)
                                    except Unauthorized:
                                        ok = False
                                    if not ok:
                                        if (container is not None
                                                or guarded_getattr(
                                                    obj, name, _marker) is
                                                not next  # NOQA: E501
                                            ):
                                            raise Unauthorized(name)
                        except UseTraversalDefault:
                            # behave as if there had been no
                            # '__bobo_traverse__'
                            bobo_traverse = None
                        if next is UseTraversalDefault:
                            if getattr(aq_base(obj), name,
                                       _marker) is not _marker:
                                if restricted:
                                    next = guarded_getattr(obj, name)
                                else:
                                    next = getattr(obj, name)
                            else:
                                try:
                                    next = obj[name]
                                except (AttributeError, TypeError):
                                    # Raise NotFound for easier debugging
                                    # instead of AttributeError: __getitem__
                                    # or TypeError: not subscriptable
                                    raise NotFound(name)
                                if restricted and not validate(
                                        obj, obj, None, next):
                                    raise Unauthorized(name)

                except (AttributeError, NotFound, KeyError) as e:
                    # Try to look for a view
                    next = queryMultiAdapter(
                        (obj, aq_acquire(self, 'REQUEST')), Interface, name)

                    if next is not None:
                        if IAcquirer.providedBy(next):
                            next = next.__of__(obj)
                        if restricted and not validate(obj, obj, name, next):
                            raise Unauthorized(name)
                    elif bobo_traverse is not None:
                        # Attribute lookup should not be done after
                        # __bobo_traverse__:
                        raise e
                    else:
                        # No view, try acquired attributes
                        try:
                            if restricted:
                                next = guarded_getattr(obj, name, _marker)
                            else:
                                next = getattr(obj, name, _marker)
                        except AttributeError:
                            raise e
                        if next is _marker:
                            raise e

                obj = next

            return obj

        except ConflictError:
            raise
        except Exception:
            if default is not _marker:
                return default
            else:
                raise

    @security.public
    def restrictedTraverse(self, path, default=_marker):
        # Trusted code traversal code, always enforces securitys
        return self.unrestrictedTraverse(path, default, restricted=True)
Beispiel #22
0
class SkinsTool(UniqueObject, SkinsContainer, Folder, ActionProviderBase):
    """ This tool is used to supply skins to a portal.
    """

    id = 'portal_skins'
    meta_type = 'CMF Skins Tool'
    allow_any = 0
    cookie_persistence = 0
    default_skin = ''
    request_varname = 'portal_skin'
    selections = None

    security = ClassSecurityInfo()

    manage_options = (modifiedOptions() + ({
        'label': 'Overview',
        'action': 'manage_overview'
    }, ) + ActionProviderBase.manage_options)

    def __init__(self):
        self.selections = PersistentMapping()

    def _getSelections(self):
        sels = self.selections
        if sels is None:
            # Backward compatibility.
            self.selections = sels = PersistentMapping()
        return sels

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainSkinsTool', _dtmldir)

    security.declareProtected(ManagePortal, 'manage_propertiesForm')
    manage_propertiesForm = DTMLFile('dtml/skinProps', globals())

    # the following two methods override those in FindSupport, to
    # support marking of objects used in specific skins
    security.declareProtected(ManagePortal, 'manage_findResult')
    manage_findResult = DTMLFile('findResult',
                                 _dtmldir,
                                 management_view='Find')

    security.declareProtected(ManagePortal, 'manage_findForm')
    manage_findForm = DTMLFile('findForm', _dtmldir, management_view='Find')

    security.declareProtected(ManagePortal, 'manage_compareResults')
    manage_compareResults = DTMLFile('compareResults',
                                     _dtmldir,
                                     management_view='Compare')

    @security.protected(ManagePortal)
    def manage_skinLayers(self,
                          chosen=(),
                          add_skin=0,
                          del_skin=0,
                          skinname='',
                          skinpath='',
                          REQUEST=None):
        """ Change the skinLayers.
        """
        sels = self._getSelections()
        if del_skin:
            for name in chosen:
                del sels[name]

        if REQUEST is not None:
            for key in sels.keys():
                fname = 'skinpath_%s' % key
                val = REQUEST[fname]

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

                if sels[key] != val:
                    self.testSkinPath(val)
                    sels[key] = val

        if add_skin:
            skinpath = ','.join([layer.strip() for layer in skinpath])
            self.testSkinPath(skinpath)
            sels[str(skinname)] = skinpath

        if REQUEST is not None:
            return self.manage_propertiesForm(
                self,
                REQUEST,
                management_view='Properties',
                manage_tabs_message='Skins changed.')

    @security.protected(ManagePortal)
    def isFirstInSkin(self, template_path, skin=None):
        """
        Is the specified template the one that would get returned from the current
        skin?
        """
        if skin is None or skin == 'None':
            skin = self.getDefaultSkin()
        template = self.restrictedTraverse(template_path)
        name = template.getId()
        skin_path = self.getSkinPath(skin)
        if not skin_path:
            return 0
        parts = list(skin_path.split(","))
        found = ""
        for part in parts:
            part = part.strip()
            if part[0] == "_":
                continue
            partob = getattr(self, part, None)
            if partob:
                skin_template = getattr(partob.aq_base, name, None)
                if skin_template:
                    found = skin_template
                    break
        if found == template:
            return 1
        else:
            return 0

    @security.protected(ManagePortal)
    def manage_properties(self,
                          default_skin='',
                          request_varname='',
                          allow_any=0,
                          chosen=(),
                          add_skin=0,
                          del_skin=0,
                          skinname='',
                          skinpath='',
                          cookie_persistence=0,
                          REQUEST=None):
        """ Changes portal_skin properties. """
        self.default_skin = str(default_skin)
        self.request_varname = str(request_varname)
        self.allow_any = allow_any and 1 or 0
        self.cookie_persistence = cookie_persistence and 1 or 0
        if REQUEST is not None:
            return self.manage_propertiesForm(
                self,
                REQUEST,
                management_view='Properties',
                manage_tabs_message='Properties changed.')

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

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

        if major == 'text':

            if minor == 'x-python':
                return PythonScript(id=name)

            if minor in ('html', 'xml'):
                return ZopePageTemplate(name)

            return DTMLMethod(__name__=name)

        return None

    # Make the PUT_factory replaceable
    PUT_factory__replaceable__ = REPLACEABLE

    @security.private
    def testSkinPath(self, p):
        """ Calls SkinsContainer.getSkinByPath().
        """
        self.getSkinByPath(p, raise_exc=1)

    #
    #   'SkinsContainer' interface methods
    #
    @security.protected(AccessContentsInformation)
    def getSkinPath(self, name):
        """ Convert a skin name to a skin path.
        """
        sels = self._getSelections()
        p = sels.get(name, None)
        if p is None:
            if self.allow_any:
                return name
        return p  # Can be None

    @security.protected(AccessContentsInformation)
    def getDefaultSkin(self):
        """ Get the default skin name.
        """
        return self.default_skin

    @security.protected(AccessContentsInformation)
    def getRequestVarname(self):
        """ Get the variable name to look for in the REQUEST.
        """
        return self.request_varname

    #
    #   UI methods
    #
    @security.protected(AccessContentsInformation)
    def getAllowAny(self):
        '''
        Used by the management UI.  Returns a flag indicating whether
        users are allowed to use arbitrary skin paths.
        '''
        return self.allow_any

    @security.protected(AccessContentsInformation)
    def getCookiePersistence(self):
        '''
        Used by the management UI.  Returns a flag indicating whether
        the skins cookie is persistent or not.
        '''
        return self.cookie_persistence

    @security.protected(AccessContentsInformation)
    def getSkinPaths(self):
        '''
        Used by the management UI.  Returns the list of skin name to
        skin path mappings as a sorted list of tuples.
        '''
        sels = self._getSelections()
        rval = []
        for key, value in sels.items():
            rval.append((key, value))
        rval.sort()
        return rval

    #
    #   'portal_skins' interface methods
    #
    @security.public
    def getSkinSelections(self):
        """ Get the sorted list of available skin names.
        """
        sels = self._getSelections()
        rval = list(sels.keys())
        rval.sort()
        return rval

    @security.protected(View)
    def updateSkinCookie(self):
        """ If needed, updates the skin cookie based on the member preference.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getAuthenticatedMember()
        if hasattr(aq_base(member), 'getProperty'):
            mskin = member.getProperty('portal_skin')
            if mskin:
                req = getRequest()
                cookie = req.cookies.get(self.request_varname, None)
                if cookie != mskin:
                    resp = req.RESPONSE
                    utool = getUtility(IURLTool)
                    portal_path = req['BASEPATH1'] + '/' + utool(1)

                    if not self.cookie_persistence:
                        # *Don't* make the cookie persistent!
                        resp.setCookie(self.request_varname,
                                       mskin,
                                       path=portal_path)
                    else:
                        expires = (DateTime('GMT') + 365).rfc822()
                        resp.setCookie(self.request_varname,
                                       mskin,
                                       path=portal_path,
                                       expires=expires)
                    # Ensure updateSkinCookie() doesn't try again
                    # within this request.
                    req.cookies[self.request_varname] = mskin
                    req[self.request_varname] = mskin
                    return 1
        return 0

    @security.protected(View)
    def clearSkinCookie(self):
        """ Expire the skin cookie.
        """
        req = getRequest()
        resp = req.RESPONSE
        utool = getUtility(IURLTool)
        portal_path = req['BASEPATH1'] + '/' + utool(1)
        resp.expireCookie(self.request_varname, path=portal_path)

    @security.protected(ManagePortal)
    def addSkinSelection(self, skinname, skinpath, test=0, make_default=0):
        '''
        Adds a skin selection.
        '''
        sels = self._getSelections()
        skinpath = str(skinpath)

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

        for elem in path_elems[:]:
            if elem in ignored or ignore_re.match(elem):
                path_elems.remove(elem)

        skinpath = ','.join(path_elems)

        if test:
            self.testSkinPath(skinpath)
        sels[str(skinname)] = skinpath
        if make_default:
            self.default_skin = skinname

    @security.protected(AccessContentsInformation)
    def getDiff(self, item_one_path, item_two_path, reverse=0):
        """ Return a diff between one and two.
        """
        if not reverse:
            item_one = self.unrestrictedTraverse(item_one_path)
            item_two = self.unrestrictedTraverse(item_two_path)
        else:
            item_one = self.unrestrictedTraverse(item_two_path)
            item_two = self.unrestrictedTraverse(item_one_path)

        res = unified_diff(item_one.read().splitlines(),
                           item_two.read().splitlines(),
                           item_one_path,
                           item_two_path,
                           '',
                           '',
                           lineterm="")
        return res
class DirectoryViewSurrogate(Folder):
    """ Folderish DirectoryView.
    """

    meta_type = 'Filesystem Directory View'
    zmi_icon = 'far fa-folder-open'
    all_meta_types = ()

    security = ClassSecurityInfo()

    def __init__(self, real, data, objects):
        d = self.__dict__
        d.update(data)
        d.update(real.__dict__)
        d['_real'] = real
        d['_objects'] = objects

    def __setattr__(self, name, value):
        d = self.__dict__
        d[name] = value
        setattr(d['_real'], name, value)

    def __delattr__(self, name):
        d = self.__dict__
        del d[name]
        delattr(d['_real'], name)

    security.declareProtected(ManagePortal, 'manage_propertiesForm')
    manage_propertiesForm = DTMLFile('dirview_properties', _dtmldir)

    @security.protected(ManagePortal)
    def manage_properties(self, reg_key, REQUEST=None):
        """ Update the directory path of the DirectoryView.
        """
        self.__dict__['_real']._dirpath = reg_key
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect('%s/manage_propertiesForm' %
                                         self.absolute_url())

    @security.protected(ACI)
    def getCustomizableObject(self):
        ob = aq_parent(aq_inner(self))
        while ob:
            if IDirectoryView.providedBy(ob):
                ob = aq_parent(ob)
            else:
                break
        return ob

    @security.protected(ACI)
    def listCustFolderPaths(self, adding_meta_type=None):
        """ List possible customization folders as key, value pairs.
        """
        rval = []
        ob = self.getCustomizableObject()
        listFolderHierarchy(ob, '', rval, adding_meta_type)
        rval.sort()
        return rval

    @security.protected(ACI)
    def getDirPath(self):
        return self.__dict__['_real']._dirpath

    @security.public
    def getId(self):
        return self.id
Beispiel #24
0
class MethodDoc(Persistent):
    """ Describes a method of an API.

    required - a sequence of required arguments
    optional - a sequence of tuples (name, default value)
    varargs - the name of the variable argument or None
    kwargs - the name of the kw argument or None
    """

    security = ClassSecurityInfo()
    security.setDefaultAccess({
        'doc': True,
        'kwargs': True,
        'name': True,
        'optional': True,
        'required': True,
        'varargs': True
    })

    varargs = None
    kwargs = None

    def __init__(self, func, isInterface=0):
        if isInterface:
            self._createFromInterfaceMethod(func)
        else:
            self._createFromFunc(func)

    def _createFromInterfaceMethod(self, func):
        self.name = func.__name__
        self.doc = trim_doc_string(func.__doc__)
        self.required = func.required
        opt = []
        for p in func.positional[len(func.required):]:
            opt.append((p, func.optional[p]))
        self.optional = tuple(opt)
        if func.varargs:
            self.varargs = func.varargs
        if func.kwargs:
            self.kwargs = func.kwargs

    def _createFromFunc(self, func):
        if hasattr(func, 'im_func'):
            func = func.im_func

        self.name = func.__name__
        self.doc = trim_doc_string(func.__doc__)

        # figure out the method arguments
        # mostly stolen from pythondoc
        CO_VARARGS = 4
        CO_VARKEYWORDS = 8
        names = func.func_code.co_varnames
        nrargs = func.func_code.co_argcount
        if func.func_defaults:
            nrdefaults = len(func.func_defaults)
        else:
            nrdefaults = 0
        self.required = names[:nrargs - nrdefaults]
        if func.func_defaults:
            self.optional = tuple(
                map(None, names[nrargs - nrdefaults:nrargs],
                    func.func_defaults))
        else:
            self.optional = ()
        varargs = []
        ix = nrargs
        if func.func_code.co_flags & CO_VARARGS:
            self.varargs = names[ix]
            ix = ix + 1
        if func.func_code.co_flags & CO_VARKEYWORDS:
            self.kwargs = names[ix]

    view = DTMLFile('dtml/methodView', globals())
Beispiel #25
0
class ImportConfiguratorBase(Implicit):
    # old code, will become deprecated
    """ Synthesize data from XML description.
    """
    security = ClassSecurityInfo()
    security.setDefaultAccess('allow')

    def __init__(self, site, encoding=None):

        self._site = site
        self._encoding = encoding

    security.declareProtected(ManagePortal, 'parseXML')

    def parseXML(self, xml):
        """ Pseudo API.
        """
        reader = getattr(xml, 'read', None)

        if reader is not None:
            xml = reader()

        dom = parseString(xml)
        root = dom.documentElement

        return self._extractNode(root)

    def _extractNode(self, node):
        """ Please see docs/configurator.txt for information about the
        import mapping syntax.
        """
        nodes_map = self._getImportMapping()
        if node.nodeName not in nodes_map:
            nodes_map = self._getSharedImportMapping()
            if node.nodeName not in nodes_map:
                raise ValueError('Unknown node: %s' % node.nodeName)
        node_map = nodes_map[node.nodeName]
        info = {}

        for name, val in node.attributes.items():
            key = node_map[name].get(KEY, str(name))
            val = self._encoding and val.encode(self._encoding) or val
            info[key] = val

        for child in node.childNodes:
            name = child.nodeName

            if name == '#comment':
                continue

            if not name == '#text':
                key = node_map[name].get(KEY, str(name))
                info[key] = info.setdefault(key,
                                            ()) + (self._extractNode(child), )

            elif '#text' in node_map:
                key = node_map['#text'].get(KEY, 'value')
                val = child.nodeValue.lstrip()
                val = self._encoding and val.encode(self._encoding) or val
                info[key] = info.setdefault(key, '') + val

        for k, v in node_map.items():
            key = v.get(KEY, k)

            if DEFAULT in v and not key in info:
                if isinstance(v[DEFAULT], basestring):
                    info[key] = v[DEFAULT] % info
                else:
                    info[key] = v[DEFAULT]

            elif CONVERTER in v and key in info:
                info[key] = v[CONVERTER](info[key])

            if key is None:
                info = info[key]

        return info

    def _getSharedImportMapping(self):

        return {
            'object': {
                'i18n:domain': {},
                'name': {
                    KEY: 'id'
                },
                'meta_type': {},
                'insert-before': {},
                'insert-after': {},
                'property': {
                    KEY: 'properties',
                    DEFAULT: ()
                },
                'object': {
                    KEY: 'objects',
                    DEFAULT: ()
                },
                'xmlns:i18n': {}
            },
            'property': {
                'name': {
                    KEY: 'id'
                },
                '#text': {
                    KEY: 'value',
                    DEFAULT: ''
                },
                'element': {
                    KEY: 'elements',
                    DEFAULT: ()
                },
                'type': {},
                'select_variable': {},
                'i18n:translate': {}
            },
            'element': {
                'value': {
                    KEY: None
                }
            },
            'description': {
                '#text': {
                    KEY: None,
                    DEFAULT: ''
                }
            }
        }

    def _convertToBoolean(self, val):

        return val.lower() in ('true', 'yes', '1')

    def _convertToUnique(self, val):

        assert len(val) == 1
        return val[0]
Beispiel #26
0
class APIDoc(Persistent):
    """ Describes an API.
    """

    security = ClassSecurityInfo()
    security.setDefaultAccess({
        'attributes': True,
        'constructor': True,
        'doc': True,
        'extends': True,
        'name': True,
        'methods': True
    })

    extends = ()

    def __init__(self, klass, isInterface=0):
        if isInterface:
            self._createFromInterface(klass)
        else:
            self._createFromClass(klass)

    def _createFromInterface(self, klass):
        # Creates an APIDoc instance given an interface object.
        self.name = klass.__name__
        self.doc = trim_doc_string(klass.__doc__)

        # inheritence information
        self.extends = []

        # Get info on methods and attributes, ignore special items
        self.attributes = []
        self.methods = []
        for k, v in klass.namesAndDescriptions():
            if hasattr(v, 'getSignatureInfo'):
                self.methods.append(MethodDoc(v, 1))
            else:
                self.attributes.append(AttributeDoc(k, v.__doc__))

    def _createFromClass(self, klass):
        # Creates an APIDoc instance given a python class.
        # the class describes the API; it contains
        # methods, arguments and doc strings.
        #
        # The name of the API is deduced from the name
        # of the class.

        self.name = klass.__name__
        self.doc = trim_doc_string(klass.__doc__)

        # Get info on methods and attributes, ignore special items
        self.attributes = []
        self.methods = []
        for k, v in klass.__dict__.items():
            if k not in ('__extends__', '__doc__', '__constructor__'):
                if type(v) == types.FunctionType:
                    self.methods.append(MethodDoc(v, 0))
                else:
                    self.attributes.append(AttributeDoc(k, v))

    def SearchableText(self):
        """
        The full text of the API, for indexing purposes.
        """
        text = "%s %s" % (self.name, self.doc)
        for attribute in self.attributes:
            text = "%s %s" % (text, attribute.name)
        for method in self.methods:
            text = "%s %s %s" % (text, method.name, method.doc)
        return text

    view = DTMLFile('dtml/APIView', globals())
Beispiel #27
0
class PropertyManager(Base, ElementWithAttributes):
    """
    The PropertyManager mixin class provides an object with
    transparent property management. An object which wants to
    have properties should inherit from PropertyManager.

    An object may specify that it has one or more predefined
    properties, by specifying an _properties structure in its
    class::

      _properties=({'id':'title', 'type': 'string', 'mode': 'w'},
                   {'id':'color', 'type': 'string', 'mode': 'w'},
                   )

    The _properties structure is a sequence of dictionaries, where
    each dictionary represents a predefined property. Note that if a
    predefined property is defined in the _properties structure, you
    must provide an attribute with that name in your class or instance
    that contains the default value of the predefined property.

    Each entry in the _properties structure must have at least an 'id'
    and a 'type' key. The 'id' key contains the name of the property,
    and the 'type' key contains a string representing the object's type.
    The 'type' string must be one of the values: 'float', 'int', 'long',
    'string', 'lines', 'text', 'date', 'tokens', 'selection', or
    'multiple section'.

    For 'selection' and 'multiple selection' properties, there is an
    addition item in the property dictionay, 'select_variable' which
    provides the name of a property or method which returns a list of
    strings from which the selection(s) can be chosen.

    Each entry in the _properties structure may *optionally* provide a
    'mode' key, which specifies the mutability of the property. The 'mode'
    string, if present, must contain 0 or more characters from the set
    'w','d'.

    A 'w' present in the mode string indicates that the value of the
    property may be changed by the user. A 'd' indicates that the user
    can delete the property. An empty mode string indicates that the
    property and its value may be shown in property listings, but that
    it is read-only and may not be deleted.

    Entries in the _properties structure which do not have a 'mode' key
    are assumed to have the mode 'wd' (writeable and deleteable).

    To fully support property management, including the system-provided
    tabs and user interfaces for working with properties, an object which
    inherits from PropertyManager should include the following entry in
    its manage_options structure::

      {'label':'Properties', 'action':'manage_propertiesForm',}

    to ensure that a 'Properties' tab is displayed in its management
    interface. Objects that inherit from PropertyManager should also
    include the following entry in its __ac_permissions__ structure::

      ('Manage properties', ('manage_addProperty',
                             'manage_editProperties',
                             'manage_delProperties',
                             'manage_changeProperties',)),
    """

    implements(IPropertyManager)

    security = ClassSecurityInfo()
    security.declareObjectProtected(access_contents_information)
    security.setPermissionDefault(access_contents_information,
                                  ('Anonymous', 'Manager'))

    manage_options = ({
        'label': 'Properties',
        'action': 'manage_propertiesForm'
    }, )

    security.declareProtected(manage_properties, 'manage_propertiesForm')
    manage_propertiesForm = DTMLFile('dtml/properties',
                                     globals(),
                                     property_extensible_schema__=1)
    security.declareProtected(manage_properties, 'manage_propertyTypeForm')
    manage_propertyTypeForm = DTMLFile('dtml/propertyType', globals())

    title = ''
    _properties = ({'id': 'title', 'type': 'string', 'mode': 'wd'}, )
    _reserved_names = ()

    __propsets__ = ()
    propertysheets = vps(DefaultPropertySheets)

    security.declareProtected(access_contents_information, 'valid_property_id')

    def valid_property_id(self, id):
        if not id or id[:1]=='_' or (id[:3]=='aq_') \
           or (' ' in id) or hasattr(aq_base(self), id) or escape(id) != id:
            return 0
        return 1

    security.declareProtected(access_contents_information, 'hasProperty')

    def hasProperty(self, id):
        """Return true if object has a property 'id'.
        """
        for p in self._properties:
            if id == p['id']:
                return 1
        return 0

    security.declareProtected(access_contents_information, 'getProperty')

    def getProperty(self, id, d=None):
        """Get the property 'id'.

        Returns the optional second argument or None if no such property is
        found.
        """
        for p in self._properties:
            if id == p['id']:
                return getattr(self, id)
        return d

    security.declareProtected(access_contents_information, 'getPropertyType')

    def getPropertyType(self, id):
        """Get the type of property 'id'.

        Returns None if no such property exists.
        """
        for md in self._properties:
            if md['id'] == id:
                return md.get('type', 'string')
        return None

    def _wrapperCheck(self, object):
        # Raise an error if an object is wrapped.
        if hasattr(object, 'aq_base'):
            raise ValueError, 'Invalid property value: wrapped object'
        return

    def _setPropValue(self, id, value):
        self._wrapperCheck(value)
        if type(value) == list:
            value = tuple(value)
        setattr(self, id, value)

    def _delPropValue(self, id):
        delattr(self, id)

    def _setProperty(self, id, value, type='string'):
        # for selection and multiple selection properties
        # the value argument indicates the select variable
        # of the property
        self._wrapperCheck(value)
        if not self.valid_property_id(id):
            raise BadRequest, 'Invalid or duplicate property id'

        if type in ('selection', 'multiple selection'):
            if not hasattr(self, value):
                raise BadRequest, 'No select variable %s' % value
            self._properties = self._properties + ({
                'id': id,
                'type': type,
                'select_variable': value
            }, )
            if type == 'selection':
                self._setPropValue(id, '')
            else:
                self._setPropValue(id, [])
        else:
            self._properties = self._properties + ({'id': id, 'type': type}, )
            self._setPropValue(id, value)

    def _updateProperty(self, id, value):
        # Update the value of an existing property. If value
        # is a string, an attempt will be made to convert
        # the value to the type of the existing property.
        self._wrapperCheck(value)
        if not self.hasProperty(id):
            raise BadRequest, 'The property %s does not exist' % escape(id)
        if type(value) is str:
            proptype = self.getPropertyType(id) or 'string'
            if proptype in type_converters:
                value = type_converters[proptype](value)
        self._setPropValue(id, value)

    def _delProperty(self, id):
        if not self.hasProperty(id):
            raise ValueError, 'The property %s does not exist' % escape(id)
        self._delPropValue(id)
        self._properties = tuple(i for i in self._properties if i['id'] != id)

    security.declareProtected(access_contents_information, 'propertyIds')

    def propertyIds(self):
        """Return a list of property ids.
        """
        return [i['id'] for i in self._properties]

    security.declareProtected(access_contents_information, 'propertyValues')

    def propertyValues(self):
        """Return a list of actual property objects.
        """
        return [getattr(self, i['id']) for i in self._properties]

    security.declareProtected(access_contents_information, 'propertyItems')

    def propertyItems(self):
        """Return a list of (id,property) tuples.
        """
        return [(i['id'], getattr(self, i['id'])) for i in self._properties]

    def _propertyMap(self):
        """Return a tuple of mappings, giving meta-data for properties.
        """
        return self._properties

    security.declareProtected(access_contents_information, 'propertyMap')

    def propertyMap(self):
        """Return a tuple of mappings, giving meta-data for properties.

        Return copies of the real definitions for security.
        """
        return tuple(dict.copy() for dict in self._propertyMap())

    security.declareProtected(access_contents_information, 'propertyLabel')

    def propertyLabel(self, id):
        """Return a label for the given property id
        """
        for p in self._properties:
            if p['id'] == id:
                return p.get('label', id)
        return id

    security.declareProtected(access_contents_information,
                              'propertyDescription')

    def propertyDescription(self, id):
        """Return a description for the given property id
        """
        for p in self._properties:
            if p['id'] == id:
                return p.get('description', '')
        return id

    security.declareProtected(access_contents_information, 'propdict')

    def propdict(self):
        dict = {}
        for p in self._properties:
            dict[p['id']] = p
        return dict

    # Web interface

    security.declareProtected(manage_properties, 'manage_addProperty')

    def manage_addProperty(self, id, value, type, REQUEST=None):
        """Add a new property via the web.

        Sets a new property with the given id, type, and value.
        """
        if type in type_converters:
            value = type_converters[type](value)
        self._setProperty(id.strip(), value, type)
        if REQUEST is not None:
            return self.manage_propertiesForm(self, REQUEST)

    security.declareProtected(manage_properties, 'manage_editProperties')

    def manage_editProperties(self, REQUEST):
        """Edit object properties via the web.

        The purpose of this method is to change all property values,
        even those not listed in REQUEST; otherwise checkboxes that
        get turned off will be ignored.  Use manage_changeProperties()
        instead for most situations.
        """
        for prop in self._propertyMap():
            name = prop['id']
            if 'w' in prop.get('mode', 'wd'):
                if prop['type'] == 'multiple selection':
                    value = REQUEST.get(name, [])
                else:
                    value = REQUEST.get(name, '')
                self._updateProperty(name, value)
        if REQUEST:
            message = "Saved changes."
            return self.manage_propertiesForm(self,
                                              REQUEST,
                                              manage_tabs_message=message)

    security.declareProtected(manage_properties, 'manage_changeProperties')

    def manage_changeProperties(self, REQUEST=None, **kw):
        """Change existing object properties.

        Change object properties by passing either a REQUEST object or
        name=value parameters
        """
        if REQUEST is None:
            props = {}
        else:
            props = REQUEST
        if kw:
            for name, value in kw.items():
                props[name] = value
        propdict = self.propdict()
        for name, value in props.items():
            if self.hasProperty(name):
                if not 'w' in propdict[name].get('mode', 'wd'):
                    raise BadRequest, '%s cannot be changed' % escape(name)
                self._updateProperty(name, value)
        if REQUEST:
            message = "Saved changes."
            return self.manage_propertiesForm(self,
                                              REQUEST,
                                              manage_tabs_message=message)

    # Note - this is experimental, pending some community input.

    security.declareProtected(manage_properties, 'manage_changePropertyTypes')

    def manage_changePropertyTypes(self, old_ids, props, REQUEST=None):
        """Replace one set of properties with another

        Delete all properties that have ids in old_ids, then add a
        property for each item in props.  Each item has a new_id,
        new_value, and new_type.  The type of new_value should match
        new_type.
        """
        err = self.manage_delProperties(old_ids)
        if err:
            if REQUEST is not None:
                return err
            return
        for prop in props:
            self._setProperty(prop.new_id, prop.new_value, prop.new_type)
        if REQUEST is not None:
            return self.manage_propertiesForm(self, REQUEST)

    security.declareProtected(manage_properties, 'manage_delProperties')

    def manage_delProperties(self, ids=None, REQUEST=None):
        """Delete one or more properties specified by 'ids'."""
        if REQUEST:
            # Bugfix for property named "ids" (Casey)
            if ids == self.getProperty('ids', None): ids = None
            ids = REQUEST.get('_ids', ids)
        if ids is None:
            return MessageDialog(
                title='No property specified',
                message='No properties were specified!',
                action='./manage_propertiesForm',
            )
        propdict = self.propdict()
        nd = self._reserved_names
        for id in ids:
            if not hasattr(aq_base(self), id):
                raise BadRequest, ('The property <em>%s</em> does not exist' %
                                   escape(id))
            if (not 'd' in propdict[id].get('mode', 'wd')) or (id in nd):
                return MessageDialog(
                    title='Cannot delete %s' % id,
                    message='The property <em>%s</em> cannot be deleted.' %
                    escape(id),
                    action='manage_propertiesForm')
            self._delProperty(id)

        if REQUEST is not None:
            return self.manage_propertiesForm(self, REQUEST)
class Collection(Item):
    """Convenience subclass for ``Collection`` portal type
    """
    security = ClassSecurityInfo()

    # BBB

    @security.protected(permissions.View)
    def listMetaDataFields(self, exclude=True):
        """Return a list of all metadata fields from portal_catalog.

        This is no longer used.  We use a vocabulary instead.
        """
        return []

    @security.protected(permissions.View)
    def selectedViewFields(self):
        """Returns a list of all metadata fields from the catalog that were
           selected.
        """
        from plone.app.contenttypes.behaviors.collection import \
            ICollection as ICollection_behavior
        return ICollection_behavior(self).selectedViewFields()

    @security.protected(permissions.ModifyPortalContent)
    def setQuery(self, query):
        self.query = query

    @security.protected(permissions.View)
    def getQuery(self):
        """Return the query as a list of dict; note that this method
        returns a list of CatalogContentListingObject in
        Products.ATContentTypes.
        """
        return self.query

    @deprecation.deprecate('getRawQuery() is deprecated; use getQuery().')
    @security.protected(permissions.View)
    def getRawQuery(self):
        return self.getQuery()

    @security.protected(permissions.ModifyPortalContent)
    def setSort_on(self, sort_on):
        self.sort_on = sort_on

    @security.protected(permissions.ModifyPortalContent)
    def setSort_reversed(self, sort_reversed):
        self.sort_reversed = sort_reversed

    @security.protected(permissions.View)
    def queryCatalog(self, batch=True, b_start=0, b_size=30, sort_on=None):
        from plone.app.contenttypes.behaviors.collection import \
            ICollection as ICollection_behavior
        return ICollection_behavior(self).results(batch,
                                                  b_start,
                                                  b_size,
                                                  sort_on=sort_on)

    @security.protected(permissions.View)
    def results(self, **kwargs):
        from plone.app.contenttypes.behaviors.collection import \
            ICollection as ICollection_behavior
        return ICollection_behavior(self).results(**kwargs)
Beispiel #29
0
class RegistrationTool(BaseTool):
    """ Manage through-the-web signup policies.
    """

    meta_type = 'Default Registration Tool'

    security = ClassSecurityInfo()

    def _getValidEmailAddress(self, member):
        email = member.getProperty('email')

        # assert that we can actually get an email address, otherwise
        # the template will be made with a blank To:, this is bad
        if email is None:
            msg = _(
                u'No email address is registered for member: '
                u'${member_id}',
                mapping={'member_id': member.getId()})
            raise ValueError(msg)

        checkEmailAddress(email)
        return email

    #
    #   'portal_registration' interface
    #
    security.declarePublic('testPasswordValidity')

    def testPasswordValidity(self, password, confirm=None):
        """ Verify that the password satisfies the portal's requirements.

        o If the password is valid, return None.
        o If not, return a string explaining why.
        """
        if not password:
            return _(u'You must enter a password.')

        if len(password) < 5 and not _checkPermission(ManagePortal, self):
            return _(u'Your password must contain at least 5 characters.')

        if confirm is not None and confirm != password:
            return _(u'Your password and confirmation did not match. '
                     u'Please try again.')

        return None

    security.declarePublic('testPropertiesValidity')

    def testPropertiesValidity(self, props, member=None):
        """ Verify that the properties supplied satisfy portal's requirements.

        o If the properties are valid, return None.
        o If not, return a string explaining why.
        """
        if member is None:  # New member.

            username = props.get('username', '')
            if not username:
                return _(u'You must enter a valid name.')

            if not self.isMemberIdAllowed(username):
                return _(u'The login name you selected is already in use or '
                         u'is not valid. Please choose another.')

            email = props.get('email')
            if email is None:
                return _(u'You must enter an email address.')

            try:
                checkEmailAddress(email)
            except ValidationError:
                return _(u'You must enter a valid email address.')

        else:  # Existing member.
            email = props.get('email')

            if email is not None:
                try:
                    checkEmailAddress(email)
                except ValidationError:
                    return _(u'You must enter a valid email address.')

            # Not allowed to clear an existing non-empty email.
            existing = member.getProperty('email')

            if existing and email == '':
                return _(u'You must enter a valid email address.')

        return None

    security.declarePublic('mailPassword')

    def mailPassword(self, forgotten_userid, REQUEST):
        """ Email a forgotten password to a member.

        o Raise an exception if user ID is not found.
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(forgotten_userid)

        if member is None:
            raise ValueError(
                _(u'The username you entered could not be '
                  u'found.'))

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('password_email')
        kw = {'member': member, 'password': member.getPassword()}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except TypeError:
            # fallback for mail hosts that don't implement the new signature
            host.send(mail_text)

        try:
            # BBB: for CMF 2.2's mail_password script
            return context.mail_password_response(self, REQUEST)
        except AttributeError:
            pass

    security.declarePublic('registeredNotify')

    def registeredNotify(self, new_member_id, password=None, REQUEST=None):
        """ Handle mailing the registration / welcome message.
        """
        if REQUEST is None:
            raise ValueError(u"'REQUEST' argument is missing.")

        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(new_member_id)

        if member is None:
            raise ValueError(
                _(u'The username you entered could not be '
                  u'found.'))

        if password is None:
            password = member.getPassword()

        email = self._getValidEmailAddress(member)

        # Rather than have the template try to use the mailhost, we will
        # render the message ourselves and send it from here (where we
        # don't need to worry about 'UseMailHost' permissions).
        if getattr(self, 'REQUEST', None) is None:
            context = RequestContainer(REQUEST=REQUEST)
            for item in reversed(aq_chain(self)):
                context = aq_base(item).__of__(context)
        else:
            context = self
        method = context.unrestrictedTraverse('registered_email')
        kw = {'member': member, 'password': password, 'email': email}

        if getattr(aq_base(method), 'isDocTemp', 0):
            mail_text = method(self, REQUEST, **kw)
        else:
            mail_text = method(**kw)

        host = getUtility(IMailHost)
        try:
            host.send(mail_text, immediate=True)
        except TypeError:
            # fallback for mail hosts that don't implement the new signature
            host.send(mail_text)

    security.declareProtected(ManagePortal, 'editMember')

    @postonly
    def editMember(self,
                   member_id,
                   properties=None,
                   password=None,
                   roles=None,
                   domains=None,
                   REQUEST=None):
        """ Edit a user's properties and security settings

        o Checks should be done before this method is called using
          testPropertiesValidity and testPasswordValidity
        """
        mtool = getUtility(IMembershipTool)
        member = mtool.getMemberById(member_id)
        member.setMemberProperties(properties)
        member.setSecurityProfile(password, roles, domains)

        return member
Beispiel #30
0
class PortalFolderBase(DynamicType, OpaqueItemManager, Folder):
    """Base class for portal folder.
    """

    implements(IFolderish, IMutableMinimalDublinCore)

    security = ClassSecurityInfo()

    description = ''

    manage_options = (Folder.manage_options[:1] +
                      ({
                          'label': 'Components',
                          'action': 'manage_components'
                      }, ) + ({
                          'label': 'Components Folder',
                          'action': '++etc++site/manage_main'
                      }, ) + Folder.manage_options[1:])

    def __init__(self, id, title='', description=''):
        self.id = id
        self.title = title
        self.description = description

    #
    #   'IMutableMinimalDublinCore' interface methods
    #
    security.declareProtected(View, 'Title')

    def Title(self):
        """ Dublin Core Title element - resource name.
        """
        return self.title

    security.declareProtected(View, 'Description')

    def Description(self):
        """ Dublin Core Description element - resource summary.
        """
        return self.description

    security.declareProtected(View, 'Type')

    def Type(self):
        """ Dublin Core Type element - resource type.
        """
        ti = self.getTypeInfo()
        return ti is not None and ti.Title() or 'Unknown'

    security.declareProtected(ManageProperties, 'setTitle')

    def setTitle(self, title):
        """ Set Dublin Core Title element - resource name.
        """
        self.title = title

    security.declareProtected(ManageProperties, 'setDescription')

    def setDescription(self, description):
        """ Set Dublin Core Description element - resource summary.
        """
        self.description = description

    #
    #   other methods
    #
    security.declareProtected(ManageProperties, 'edit')

    def edit(self, title='', description=''):
        """
        Edit the folder title (and possibly other attributes later)
        """
        self.setTitle(title)
        self.setDescription(description)
        # BBB: for ICatalogAware subclasses
        if getattr(self, 'reindexObject', None) is not None:
            self.reindexObject()

    security.declarePublic('allowedContentTypes')

    def allowedContentTypes(self):
        """
            List type info objects for types which can be added in
            this folder.
        """
        portal_types = getToolByName(self, 'portal_types')
        myType = portal_types.getTypeInfo(self)
        result = portal_types.listTypeInfo()

        if myType is not None:
            return [
                t for t in result if myType.allowType(t.getId())
                and t.isConstructionAllowed(self)
            ]

        return [t for t in result if t.isConstructionAllowed(self)]

    def _filteredItems(self, ids, filt):
        """
            Apply filter, a mapping, to child objects indicated by 'ids',
            returning a sequence of ( id, obj ) tuples.
        """
        # Restrict allowed content types
        if filt is None:
            filt = {}
        else:
            # We'll modify it, work on a copy.
            filt = filt.copy()
        pt = filt.get('portal_type', [])
        if isinstance(pt, basestring):
            pt = [pt]
        types_tool = getToolByName(self, 'portal_types')
        allowed_types = types_tool.listContentTypes()
        if not pt:
            pt = allowed_types
        else:
            pt = [t for t in pt if t in allowed_types]
        if not pt:
            # After filtering, no types remain, so nothing should be
            # returned.
            return []
        filt['portal_type'] = pt

        query = ContentFilter(**filt)
        result = []
        append = result.append
        get = self._getOb
        for id in ids:
            obj = get(id)
            if query(obj):
                append((id, obj))
        return result

    #
    #   'IFolderish' interface methods
    #
    security.declarePublic('contentItems')

    def contentItems(self, filter=None):
        # List contentish and folderish sub-objects and their IDs.
        # (method is without docstring to disable publishing)
        #
        ids = self.objectIds()
        return self._filteredItems(ids, filter)

    security.declarePublic('contentIds')

    def contentIds(self, filter=None):
        # List IDs of contentish and folderish sub-objects.
        # (method is without docstring to disable publishing)
        #
        return [item[0] for item in self.contentItems(filter)]

    security.declarePublic('contentValues')

    def contentValues(self, filter=None):
        # List contentish and folderish sub-objects.
        # (method is without docstring to disable publishing)
        #
        return [item[1] for item in self.contentItems(filter)]

    security.declareProtected(ListFolderContents, 'listFolderContents')

    def listFolderContents(self, contentFilter=None):
        """ List viewable contentish and folderish sub-objects.
        """
        l = []
        for id, obj in self.contentItems(contentFilter):
            # validate() can either raise Unauthorized or return 0 to
            # mean unauthorized.
            try:
                if getSecurityManager().validate(self, self, id, obj):
                    l.append(obj)
            except zExceptions_Unauthorized:  # Catch *all* Unauths!
                pass
        return l

    #
    #   webdav Resource method
    #

    # protected by 'WebDAV access'
    def listDAVObjects(self):
        # List sub-objects for PROPFIND requests.
        # (method is without docstring to disable publishing)
        #
        if _checkPermission(ManagePortal, self):
            return self.objectValues()
        else:
            return self.listFolderContents()

    #
    #   other methods
    #
    security.declarePublic('encodeFolderFilter')

    def encodeFolderFilter(self, REQUEST):
        """
            Parse cookie string for using variables in dtml.
        """
        filter = {}
        for key, value in REQUEST.items():
            if key[:10] == 'filter_by_':
                filter[key[10:]] = value
        encoded = base64.encodestring(marshal.dumps(filter)).strip()
        encoded = ''.join(encoded.split('\n'))
        return encoded

    security.declarePublic('decodeFolderFilter')

    def decodeFolderFilter(self, encoded):
        """
            Parse cookie string for using variables in dtml.
        """
        filter = {}
        if encoded:
            filter.update(marshal.loads(base64.decodestring(encoded)))
        return filter

    def content_type(self):
        """
            WebDAV needs this to do the Right Thing (TM).
        """
        return None

    def PUT_factory(self, name, typ, body):
        """ Factory for PUT requests to objects which do not yet exist.

        Used by NullResource.PUT.

        Returns -- Bare and empty object of the appropriate type (or None, if
        we don't know what to do)
        """
        registry = getToolByName(self, 'content_type_registry', None)
        if registry is None:
            return None

        typeObjectName = registry.findTypeName(name, typ, body)
        if typeObjectName is None:
            return None

        self.invokeFactory(typeObjectName, name)

        # invokeFactory does too much, so the object has to be removed again
        obj = aq_base(self._getOb(name))
        self._delObject(name)
        return obj

    security.declareProtected(AddPortalContent, 'invokeFactory')

    def invokeFactory(self, type_name, id, RESPONSE=None, *args, **kw):
        """ Invokes the portal_types tool.
        """
        pt = getToolByName(self, 'portal_types')
        myType = pt.getTypeInfo(self)

        if myType is not None:
            if not myType.allowType(type_name):
                raise ValueError('Disallowed subobject type: %s' % type_name)

        return pt.constructContent(type_name, self, id, RESPONSE, *args, **kw)

    security.declareProtected(AddPortalContent, 'checkIdAvailable')

    def checkIdAvailable(self, id):
        try:
            self._checkId(id)
        except BadRequest:
            return False
        else:
            return True

    def MKCOL_handler(self, id, REQUEST=None, RESPONSE=None):
        """
            Handle WebDAV MKCOL.
        """
        self.manage_addFolder(id=id, title='')

    def _checkId(self, id, allow_dup=0):
        PortalFolderBase.inheritedAttribute('_checkId')(self, id, allow_dup)

        if allow_dup:
            return

        # FIXME: needed to allow index_html for join code
        if id == 'index_html':
            return

        # Another exception: Must allow "syndication_information" to enable
        # Syndication...
        if id == 'syndication_information':
            return

        # IDs starting with '@@' are reserved for views.
        if id[:2] == '@@':
            raise BadRequest('The id "%s" is invalid because it begins with '
                             '"@@".' % id)

        # This code prevents people other than the portal manager from
        # overriding skinned names and tools.
        if not getSecurityManager().checkPermission(ManagePortal, self):
            ob = aq_inner(self)
            while ob is not None:
                if ISiteRoot.providedBy(ob):
                    break
                # BBB
                if getattr(ob, '_isPortalRoot', False):
                    warn(
                        "The '_isPortalRoot' marker attribute for site "
                        "roots is deprecated and will be removed in "
                        "CMF 2.3;  please mark the root object with "
                        "'ISiteRoot' instead.",
                        DeprecationWarning,
                        stacklevel=2)
                    break
                ob = aq_parent(ob)

            if ob is not None:
                # If the portal root has a non-contentish object by this name,
                # don't allow an override.
                if (hasattr(ob, id) and id not in ob.contentIds() and
                        # Allow root doted prefixed object name overrides
                        not id.startswith('.')):
                    raise BadRequest('The id "%s" is reserved.' % id)
            # Don't allow ids used by Method Aliases.
            ti = self.getTypeInfo()
            if ti and ti.queryMethodID(id, context=self):
                raise BadRequest('The id "%s" is reserved.' % id)
        # Otherwise we're ok.

    def _verifyObjectPaste(self, object, validate_src=1):
        # This assists the version in OFS.CopySupport.
        # It enables the clipboard to function correctly
        # with objects created by a multi-factory.
        mt = getattr(object, '__factory_meta_type__', None)
        meta_types = getattr(self, 'all_meta_types', None)

        if mt is not None and meta_types is not None:
            method_name = None
            mt_permission = None

            if callable(meta_types):
                meta_types = meta_types()

            for d in meta_types:
                if d['name'] == mt:
                    method_name = d['action']
                    mt_permission = d.get('permission')
                    break

            if mt_permission is not None:
                sm = getSecurityManager()

                if sm.checkPermission(mt_permission, self):
                    if validate_src:
                        # Ensure the user is allowed to access the object on
                        # the clipboard.
                        parent = aq_parent(aq_inner(object))

                        if not sm.validate(None, parent, None, object):
                            raise AccessControl_Unauthorized(object.getId())

                        if validate_src == 2:  # moving
                            if not sm.checkPermission(DeleteObjects, parent):
                                raise AccessControl_Unauthorized('Delete not '
                                                                 'allowed.')
                else:
                    raise AccessControl_Unauthorized(
                        'You do not possess the '
                        '%r permission in the context of the container '
                        'into which you are pasting, thus you are not '
                        'able to perform this operation.' % mt_permission)
            else:
                raise AccessControl_Unauthorized('The object %r does not '
                                                 'support this operation.' %
                                                 object.getId())
        else:
            # Call OFS' _verifyObjectPaste if necessary
            PortalFolderBase.inheritedAttribute('_verifyObjectPaste')(
                self, object, validate_src)

        # Finally, check allowed content types
        if hasattr(aq_base(object), 'getPortalTypeName'):

            type_name = object.getPortalTypeName()

            if type_name is not None:

                pt = getToolByName(self, 'portal_types')
                myType = pt.getTypeInfo(self)

                if myType is not None and not myType.allowType(type_name):
                    raise ValueError('Disallowed subobject type: %s' %
                                     type_name)

                # Check for workflow guards
                objType = pt.getTypeInfo(type_name)
                if (objType is not None
                        and not objType._checkWorkflowAllowed(self)):
                    raise ValueError('Pasting not allowed in this workflow')

    security.setPermissionDefault(AddPortalContent, ('Owner', 'Manager'))

    security.declareProtected(AddPortalFolders, 'manage_addFolder')

    def manage_addFolder(self, id, title='', REQUEST=None):
        """ Add a new folder-like object with id *id*.

        IF present, use the parent object's 'mkdir' alias; otherwise, just add
        a PortalFolder.
        """
        ti = self.getTypeInfo()
        method_id = ti and ti.queryMethodID('mkdir', context=self)
        if method_id:
            # call it
            getattr(self, method_id)(id=id)
        else:
            self.invokeFactory(type_name='Folder', id=id)

        ob = self._getOb(id)
        ob.setTitle(title)
        try:
            ob.reindexObject()
        except AttributeError:
            pass

        if REQUEST is not None:
            return self.manage_main(self, REQUEST, update_menu=1)