Example #1
0
class Taxonomy(SimpleItem):
    order = None
    count = None
    version = None

    def __init__(self, name, title, default_language):
        self.data = PersistentDict()
        self.order = PersistentDict()
        self.count = PersistentDict()
        self.version = PersistentDict()

        self.name = name
        self.title = title
        self.default_language = default_language

    @property
    def sm(self):
        return api.portal.get().getSiteManager()

    def __call__(self, context):
        if not self.data:
            return Vocabulary(self.name, {}, {}, {}, 2)

        request = getattr(context, "REQUEST", None)
        language = self.getCurrentLanguage(request)
        return self.makeVocabulary(language)

    @property
    @ram.cache(lambda method, self: (self.name, self.data._p_mtime))
    def inverted_data(self):
        inv_data = {}
        for (language, elements) in self.data.items():
            inv_data[language] = {}
            for (path, identifier) in elements.items():
                inv_data[language][identifier] = path
        return inv_data

    def getShortName(self):
        return self.name.split('.')[-1]

    def getGeneratedName(self):
        return 'collective.taxonomy.generated.' + self.getShortName()

    def getVocabularyName(self):
        return 'collective.taxonomy.' + self.getShortName()

    def makeVocabulary(self, language):
        self._fixup()
        data = self.data.get(language, {})
        order = self.order.get(language)
        version = self.version.get(language, 1)
        inverted_data = self.inverted_data.get(language, {})
        return Vocabulary(self.name, data, inverted_data, order, version)

    def getCurrentLanguage(self, request):
        language = get_lang_code()
        if language in self.data:
            return language
        elif self.default_language in self.data:
            return self.default_language
        else:
            # our best guess!
            return self.data.keys()[0]

    def getLanguages(self):
        return tuple(self.data)

    def iterLanguage(self, language=None):
        if language is None:
            language = self.default_language

        vocabulary = self.makeVocabulary(language)

        for path, identifier in vocabulary.iterEntries():
            parent_path = path.rsplit(PATH_SEPARATOR, 1)[0]
            if parent_path:
                parent = vocabulary.getTermByValue(parent_path)
            else:
                parent = None
            yield path, identifier, parent

    def registerBehavior(self, **kwargs):
        new_args = copy(kwargs)

        new_args['name'] = self.getGeneratedName()
        new_args['title'] = self.title
        new_args['description'] = kwargs.get('field_description', u'')
        new_args['field_description'] = new_args['description']

        behavior = TaxonomyBehavior(**new_args)
        self.sm.registerUtility(behavior, IBehavior,
                                name=self.getGeneratedName())

        behavior.addIndex()
        behavior.activateSearchable()

    def cleanupFTI(self):
        """Cleanup the FTIs"""
        generated_name = self.getGeneratedName()
        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if generated_name in fti.behaviors:
                fti.behaviors = [behavior for behavior in
                                 fti.behaviors
                                 if behavior != generated_name]
            modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def updateBehavior(self, **kwargs):
        behavior_name = self.getGeneratedName()
        short_name = self.getShortName()

        utility = self.sm.queryUtility(IBehavior, name=behavior_name)
        if utility:
            utility.deactivateSearchable()
            utility.activateSearchable()
            if 'field_title' in kwargs:
                utility.title = kwargs.pop('field_title')

            for k, v in kwargs.items():
                setattr(utility, k, v)

        delattr(generated, short_name)

        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if behavior_name in fti.behaviors:
                modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def unregisterBehavior(self):
        behavior_name = self.getGeneratedName()
        utility = self.sm.queryUtility(IBehavior, name=behavior_name)

        if utility is None:
            return

        self.cleanupFTI()

        utility.removeIndex()
        utility.deactivateSearchable()
        utility.unregisterInterface()

        self.sm.unregisterUtility(utility, IBehavior, name=behavior_name)

    def clean(self):
        self.data.clear()

    def add(self, language, value, key):
        self._fixup()
        tree = self.data.get(language)
        if tree is None:
            tree = self.data[language] = OOBTree()
        else:
            # Make sure we update the modification time.
            self.data[language] = tree

        update = key in tree
        tree[key] = value

        order = self.order.get(language)
        if order is None:
            order = self.order[language] = IOBTree()
            count = self.count[language] = 0
        else:
            if update:
                pop_value(tree, key)

            count = self.count[language] + 1

        self.count[language] = count
        order[count] = key

    def update(self, language, items, clear=False):
        self._fixup()

        tree = self.data.setdefault(language, OOBTree())
        if clear:
            tree.clear()

        # A new tree always uses the newest version.
        if not tree:
            version = self.version[language] = 2
        else:
            version = self.version.get(language, 1)

        order = self.order.setdefault(language, IOBTree())
        count = self.count.get(language, 0)

        if clear:
            order.clear()
            count = 0

        # Always migrate to newest version.
        if version == 1:
            def fix(path):
                return path.replace(LEGACY_PATH_SEPARATOR, PATH_SEPARATOR)

            for i in list(order):
                path = order[i]
                order[i] = fix(path)

            for path in list(tree):
                value = tree.pop(path)
                tree[fix(path)] = value

            version = self.version[language] = 2
            logger.info(
                "Taxonomy '%s' upgraded to version %d for language '%s'." % (
                    self.name, version, language
                )
            )

        # Make sure we update the modification time.
        self.data[language] = tree

        # The following structure is used to expunge updated entries.
        inv = {}
        if not clear:
            for i, key in order.items():
                inv[key] = i

        seen = set()
        for key, value in items:
            if key in seen:
                logger.warning("Duplicate key entry: %r" % (key, ))
                continue

            seen.add(key)
            update = key in tree
            tree[key] = value
            order[count] = key
            count += 1

            # If we're updating, then we have to pop out the old ordering
            # information in order to maintain relative ordering of new items.
            if update:
                i = inv.get(key)
                if i is not None:
                    del order[i]

        self.count[language] = count

    def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None, msgid_plural=None,
                  default_plural=None, number=None):

        if target_language is None or \
                target_language not in self.inverted_data:
            target_language = str(api.portal.get_current_language())

        if msgid not in self.inverted_data[target_language]:
            return ''

        if self.version is not None and self.version.get(target_language) != 2:
            path_sep = LEGACY_PATH_SEPARATOR
        else:
            path_sep = PATH_SEPARATOR

        path = self.inverted_data[target_language][msgid]
        pretty_path = path[1:].replace(path_sep, PRETTY_PATH_SEPARATOR)

        if mapping is not None and mapping.get(NODE):
            pretty_path = pretty_path.rsplit(PRETTY_PATH_SEPARATOR, 1)[-1]

        return pretty_path

    def _fixup(self):
        # due to compatibility reasons this method fixes data structure
        # for old Taxonomy instances.
        # XXX: remove this in version 2.0 to prevent write on read
        if self.order is None:
            safeWrite(self, getRequest())
            self.order = PersistentDict()
            self.count = PersistentDict()

        if self.version is None:
            safeWrite(self, getRequest())
            self.version = PersistentDict()
class Taxonomy(SimpleItem):
    implements(ITaxonomy)

    def __init__(self, name, title, default_language):
        super(Taxonomy, self).__init__(self)
        self.data = PersistentDict()
        self.name = name
        self.title = title
        self.default_language = default_language

    def __call__(self, context):

        if not self.data:
            return Vocabulary(self.name, {}, {})

        request = getattr(context, "REQUEST", None)

        current_language = self.getCurrentLanguage(request)
        data = self.data[current_language]
        inverted_data = self.inverted_data[current_language]

        return Vocabulary(self.name, data, inverted_data)

    @property
    @ram.cache(lambda method, self: (self.name, self.data._p_mtime))
    def inverted_data(self):
        inv_data = {}
        for (language, elements) in self.data.items():
            inv_data[language] = {}
            for (path, identifier) in elements.items():
                inv_data[language][identifier] = path
        return inv_data

    def getShortName(self):
        return self.name.split('.')[-1]

    def getGeneratedName(self):
        return 'collective.taxonomy.generated.' + self.getShortName()

    def getVocabularyName(self):
        return 'collective.taxonomy.' + self.getShortName()

    def getCurrentLanguage(self, request):
        try:
            portal_state = getMultiAdapter(
                (self, request), name=u'plone_portal_state'
            )

            language = portal_state.language().split('-', 1)[0]
        except ComponentLookupError:
            language = ''  # Force to return default language.

        if language in self.data:
            return language
        elif self.default_language in self.data:
            return self.default_language
        else:
            # our best guess!
            return self.data.keys()[0]

    def registerBehavior(self, **kwargs):
        context = getSite()
        sm = context.getSiteManager()
        new_args = copy(kwargs)

        new_args['name'] = self.name
        new_args['title'] = self.title
        new_args['description'] = kwargs.get('field_description', u'')
        new_args['field_description'] = new_args['description']

        behavior = TaxonomyBehavior(**new_args)
        sm.registerUtility(behavior, IBehavior,
                           name=self.getGeneratedName())

        behavior.addIndex()
        behavior.activateSearchable()

    def cleanupFTI(self):
        """Cleanup the FTIs"""
        generated_name = self.getGeneratedName()
        context = getSite()
        sm = context.getSiteManager()
        for (name, fti) in sm.getUtilitiesFor(IDexterityFTI):
            if generated_name in fti.behaviors:
                fti.behaviors = [behavior for behavior in
                                 fti.behaviors
                                 if behavior != generated_name]
            modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def updateBehavior(self, **kwargs):
        sm = getSite().getSiteManager()

        behavior_name = self.getGeneratedName()
        short_name = self.getShortName()

        utility = sm.queryUtility(IBehavior, name=behavior_name)
        if utility:
            utility.deactivateSearchable()
            utility.activateSearchable()
            utility.title = kwargs['field_title']

        delattr(generated, short_name)

        for (name, fti) in sm.getUtilitiesFor(IDexterityFTI):
            if behavior_name in fti.behaviors:
                modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def unregisterBehavior(self):
        context = getSite()
        sm = context.getSiteManager()
        behavior_name = self.getGeneratedName()
        utility = sm.queryUtility(IBehavior, name=behavior_name)

        if utility is None:
            return

        self.cleanupFTI()

        utility.removeIndex()
        utility.deactivateSearchable()
        utility.unregisterInterface()

        sm.unregisterUtility(utility, IBehavior, name=behavior_name)

    def clean(self):
        self.data.clear()

    def add(self, language, identifier, path):
        if not language in self.data:
            self.data[language] = OOBTree()

        self.data[language][path] = identifier

    def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None):

        if target_language is None or \
                target_language not in self.inverted_data:
            target_language = str(self.getCurrentLanguage(
                getattr(context, 'REQUEST')
            ))

        if msgid not in self.inverted_data[target_language]:
            return ''

        path = self.inverted_data[target_language][msgid]
        pretty_path = path[1:].replace(PATH_SEPARATOR, u' » ')

        return pretty_path
class Taxonomy(SimpleItem):
    def __init__(self, name, title, default_language):
        self.data = PersistentDict()
        self.name = name
        self.title = title
        self.default_language = default_language

    @property
    def sm(self):
        return api.portal.get().getSiteManager()

    def __call__(self, context):

        if not self.data:
            return Vocabulary(self.name, {}, {})

        request = getattr(context, "REQUEST", None)

        current_language = self.getCurrentLanguage(request)
        data = self.data[current_language]
        inverted_data = self.inverted_data[current_language]

        return Vocabulary(self.name, data, inverted_data)

    @property
    @ram.cache(lambda method, self: (self.name, self.data._p_mtime))
    def inverted_data(self):
        inv_data = {}
        for (language, elements) in self.data.items():
            inv_data[language] = {}
            for (path, identifier) in elements.items():
                inv_data[language][identifier] = path
        return inv_data

    def getShortName(self):
        return self.name.split('.')[-1]

    def getGeneratedName(self):
        return 'collective.taxonomy.generated.' + self.getShortName()

    def getVocabularyName(self):
        return 'collective.taxonomy.' + self.getShortName()

    def getCurrentLanguage(self, request):
        language = get_lang_code()
        if language in self.data:
            return language
        elif self.default_language in self.data:
            return self.default_language
        else:
            # our best guess!
            return self.data.keys()[0]

    def registerBehavior(self, **kwargs):
        new_args = copy(kwargs)

        new_args['name'] = self.getGeneratedName()
        new_args['title'] = self.title
        new_args['description'] = kwargs.get('field_description', u'')
        new_args['field_description'] = new_args['description']

        behavior = TaxonomyBehavior(**new_args)
        self.sm.registerUtility(behavior,
                                IBehavior,
                                name=self.getGeneratedName())

        behavior.addIndex()
        behavior.activateSearchable()

    def cleanupFTI(self):
        """Cleanup the FTIs"""
        generated_name = self.getGeneratedName()
        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if generated_name in fti.behaviors:
                fti.behaviors = [
                    behavior for behavior in fti.behaviors
                    if behavior != generated_name
                ]
            modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def updateBehavior(self, **kwargs):
        behavior_name = self.getGeneratedName()
        short_name = self.getShortName()

        utility = self.sm.queryUtility(IBehavior, name=behavior_name)
        if utility:
            utility.deactivateSearchable()
            utility.activateSearchable()
            if 'field_title' in kwargs:
                utility.title = kwargs.pop('field_title')

            for k, v in kwargs.iteritems():
                setattr(utility, k, v)

        delattr(generated, short_name)

        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if behavior_name in fti.behaviors:
                modified(fti,
                         DexterityFTIModificationDescription("behaviors", ''))

    def unregisterBehavior(self):
        behavior_name = self.getGeneratedName()
        utility = self.sm.queryUtility(IBehavior, name=behavior_name)

        if utility is None:
            return

        self.cleanupFTI()

        utility.removeIndex()
        utility.deactivateSearchable()
        utility.unregisterInterface()

        self.sm.unregisterUtility(utility, IBehavior, name=behavior_name)

    def clean(self):
        self.data.clear()

    def add(self, language, identifier, path):
        if language not in self.data:
            self.data[language] = OOBTree()

        self.data[language][path] = identifier

    def translate(self,
                  msgid,
                  mapping=None,
                  context=None,
                  target_language=None,
                  default=None):

        if target_language is None or \
                target_language not in self.inverted_data:
            target_language = str(
                self.getCurrentLanguage(getattr(context, 'REQUEST')))

        if msgid not in self.inverted_data[target_language]:
            return ''

        path = self.inverted_data[target_language][msgid]
        pretty_path = path[1:].replace(PATH_SEPARATOR, u' » ')

        return pretty_path
Example #4
0
class ShibbolethPermissions(BasePlugin):
    """Extend folder_localrole_form to grant permissions to Shibboleth users.

    Most testing is done in tests/ShibbolethPermissions.txt."""
    security = ClassSecurityInfo()

    def __init__(self, pluginId, title=None):
        self.id = pluginId
        self.title = title
        self.localRoles = PersistentDict()
        self.retest = re.compile(' ')
        config = ((httpSharingTokensKey, 'lines', 'w', []),
                  (httpSharingLabelsKey, 'lines', 'w', []))
        # Create any missing properties
        ids = {}
        for prop in config:
            # keep track of property names for quick lookup
            ids[prop[0]] = True
            if prop[0] not in self.propertyIds():
                self.manage_addProperty(id=prop[0],
                                        type=prop[1],
                                        value=prop[3])
                self._properties[-1]['mode'] = prop[2]
        # Delete any existing properties that aren't in config
        ids.update({'prefix':'', 'title':''})
        for prop in self._properties:
            if prop['id'] not in ids:
                self.manage_delProperties(prop['id'])

    security.declarePublic('getSharingConfig')
    def getSharingConfig(self):
        """Return the items end users can use to share with.

        Verify it returns an empty configuration.
        >>> from Products.ShibbolethPermissions.permissions import ShibbolethPermissions
        >>> handler = ShibbolethPermissions()
        >>> handler.getSharingConfig()
        {'http_sharing_tokens': (), 'http_sharing_labels': ()}
        """
        if IS_AUMPAS_INSTALLED:
            autoUserMaker = getToolByName(self, 'AutoUserMakerPASPlugin', None)
            if autoUserMaker is not None:
                return autoUserMaker.getSharingConfig()
        return {httpSharingTokensKey: self.getProperty(httpSharingTokensKey),
                 httpSharingLabelsKey: self.getProperty(httpSharingLabelsKey)}

    security.declarePrivate('getShibValues')
    def getShibValues(self):
        config = self.getSharingConfig()
        request = getattr(self, 'REQUEST')
        req_environ = getattr(request, 'environ', {})
        return dict([(ii, req_environ.get(ii))
                     for ii in config[httpSharingTokensKey]
                     if ii in req_environ])

    security.declarePublic('getLocalRoles')
    def getLocalRoles(self, path=None, **params):
        """Return the self.localRoles as a dictionary or list.

        Return a dictionay if neither path nor params is set (whole dictionary
        of lists of dictionaries). Return a dictionary if params is set, but
        path is not (subset of the whole dictionary of lists of dictionaries).
        Return a list of dictionaries when path is set, which may be an empty
        list if the path is not found or params is given in addition to path
        and none of the params match.

        This simple test returns everything, which at this point is nothing.
        """
        roles = {}
        param_keys = params.keys().sort()
        for ii in self.localRoles.iterkeys():
            roles[ii] = list(self.localRoles[ii])
        if not path and not params:
            return roles        # no select given, so return everything
        if path:
            if not path in roles:
                return []   # path not found, so return nothing
            if not params:  # path found, but no subcriteria so return whole list
                return roles[path]
            return _searchParams(roles[path], param_keys, **params)
        # no path, but params, so return a path keyed dict of lists of dicts
        rval = {}
        for ii in roles.iterkeys():  # each key is a Plone path
            found = _searchParams(roles[ii], param_keys, **params)
            if found:           # Don't save empty lists
                rval[ii] = found
        return rval

    security.declarePublic('addLocalRoles')
    def addLocalRoles(self, path, params, roles):
        """Add a pattern for path of params."""
        params['_roles'] = roles
        if path in self.localRoles:
            self.localRoles[path].append(params)
        else:
            self.localRoles[path] = PersistentList([params,])

    security.declarePublic('delLocalRoles')
    def delLocalRoles(self, path=None, row=None):
        """ Delete the specified roles.
        """
        if path is None and row is None:
            self.localRoles.clear()
        elif path in self.localRoles:
            if row is not None:
                del self.localRoles[path][row]
            else:
                del self.localRoles[path]

    security.declarePublic('updLocalRoles')
    def updLocalRoles(self, path=None, row=None, roles=None):
        """ Update the specified roles.
        """
        if roles is None:
            roles = []
        if path and path in self.localRoles and row is not None:
            try:
                self.localRoles[path][row]['_roles'] = roles
            except (IndexError, TypeError):
                logger.warning("updLocalRoles error updating row %s from %s"
                               % (str(row), str(path)), exc_info=True)

    def _findroles(self, context):
        uservals = self.getShibValues()
        regexs = self.getLocalRoles().get('/'.join(context.getPhysicalPath()), None)
        if regexs is None:
            return []

        for ii in regexs:
            # Make sure the incoming user has all of the
            # needed attributes
            for name in ii.iterkeys():
                if name == '_roles':
                    continue
                if not name in uservals:
                    break
            else:
                for name, pattern in ii.iteritems():
                    if name == '_roles' or uservals[name] is None:
                        continue
                    try:
                        regex = re.compile(pattern)
                        if not regex.search(uservals[name]):
                            break
                    except (ConflictError, KeyboardInterrupt):
                        raise
                    except Exception:
                        break
                else:
                    return list(ii['_roles'])
        return []

    def refreshlocalroles(self, user=None):
        if user is None:
            userid = _getAuthenticatedUser(None).getId()
        else:
            userid = user.getId()
        if not userid:
            return
        for path in self.localRoles.iterkeys():
            obj = self.unrestrictedTraverse(path, None)
            if obj is not None:
                roles = self._findroles(obj)
                reindex = False
                current_localroles = obj.get_local_roles_for_userid(userid)
                if not roles and current_localroles:
                    obj.manage_delLocalRoles((userid,))
                    reindex = True
                elif tuple(roles) != current_localroles:
                    obj.manage_setLocalRoles(userid, roles)
                    reindex = True
                if reindex:
                    obj.reindexObjectSecurity()
Example #5
0
class Taxonomy(SimpleItem):
    implements(ITaxonomy)

    def __init__(self, name, title, default_language):
        super(Taxonomy, self).__init__(self)
        self.data = PersistentDict()
        self.name = name
        self.title = title
        self.default_language = default_language

    def __call__(self, context):
        request = getattr(context, "REQUEST", None)

        current_language = self.getCurrentLanguage(request)
        data = self.data[current_language]
        inverted_data = self.inverted_data[current_language]

        return Vocabulary(self.name, data, inverted_data)

    @property
    @ram.cache(lambda method, self: (self.name, self.data._p_mtime))
    def inverted_data(self):
        inv_data = {}
        for (language, elements) in self.data.items():
            inv_data[language] = {}
            for (path, identifier) in elements.items():
                inv_data[language][identifier] = path
        return inv_data

    def getShortName(self):
        return self.name.split('.')[-1]

    def getGeneratedName(self):
        return 'collective.taxonomy.generated.' + self.getShortName()

    def getVocabularyName(self):
        return 'collective.taxonomy.' + self.getShortName()

    def getCurrentLanguage(self, request):
        try:
            portal_state = getMultiAdapter((self, request),
                                           name=u'plone_portal_state')

            language = portal_state.language().split('-', 1)[0]
        except ComponentLookupError:
            language = ''  # Force to return default language.

        if language in self.data:
            return language
        elif self.default_language in self.data:
            return self.default_language
        else:
            # our best guess!
            return self.data.keys()[0]

    def registerBehavior(self, **kwargs):
        context = getSite()
        sm = context.getSiteManager()
        new_args = copy(kwargs)

        new_args['name'] = self.name
        new_args['title'] = self.title
        new_args['description'] = kwargs['field_description']

        behavior = TaxonomyBehavior(**new_args)
        sm.registerUtility(behavior, IBehavior, name=self.getGeneratedName())

        behavior.addIndex()
        behavior.activateSearchable()

    def cleanupFTI(self):
        """Cleanup the FTIs"""
        generated_name = self.getGeneratedName()
        context = getSite()
        sm = context.getSiteManager()
        for (name, fti) in sm.getUtilitiesFor(IDexterityFTI):
            if generated_name in fti.behaviors:
                fti.behaviors = [
                    behavior for behavior in fti.behaviors
                    if behavior != generated_name
                ]
            modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def updateBehavior(self, **kwargs):
        sm = getSite().getSiteManager()

        behavior_name = self.getGeneratedName()
        short_name = self.getShortName()

        utility = sm.queryUtility(IBehavior, name=behavior_name)
        if utility:
            utility.deactivateSearchable()
            utility.activateSearchable()

        delattr(generated, short_name)

        for (name, fti) in sm.getUtilitiesFor(IDexterityFTI):
            if behavior_name in fti.behaviors:
                modified(fti,
                         DexterityFTIModificationDescription("behaviors", ''))

    def unregisterBehavior(self):
        context = getSite()
        sm = context.getSiteManager()
        behavior_name = self.getGeneratedName()
        utility = sm.queryUtility(IBehavior, name=behavior_name)

        if utility is None:
            return

        self.cleanupFTI()

        utility.removeIndex()
        utility.deactivateSearchable()
        utility.unregisterInterface()

        sm.unregisterUtility(utility, IBehavior, name=behavior_name)

    def clean(self):
        self.data.clear()

    def add(self, language, identifier, path):
        if not language in self.data:
            self.data[language] = OOBTree()

        self.data[language][path] = identifier

    def translate(self,
                  msgid,
                  mapping=None,
                  context=None,
                  target_language=None,
                  default=None):

        if target_language is None or \
                target_language not in self.inverted_data:
            target_language = str(
                self.getCurrentLanguage(getattr(context, 'REQUEST')))

        if msgid not in self.inverted_data[target_language]:
            return ''

        path = self.inverted_data[target_language][msgid]
        #we may have non-ascii now
        if not isinstance(path, unicode):
            path = path.decode('utf-8')
        pretty_path = path[1:].replace('/', u' » ')

        return pretty_path
class ShibbolethPermissions(BasePlugin):
    """Extend folder_localrole_form to grant permissions to Shibboleth users.

    Most testing is done in tests/ShibbolethPermissions.txt."""
    security = ClassSecurityInfo()

    def __init__(self, pluginId, title=None):
        self.id = pluginId
        self.title = title
        self.localRoles = PersistentDict()
        self.retest = re.compile(' ')
        config = ((httpSharingTokensKey, 'lines', 'w', []),
                  (httpSharingLabelsKey, 'lines', 'w', []))
        # Create any missing properties
        ids = {}
        for prop in config:
            # keep track of property names for quick lookup
            ids[prop[0]] = True
            if prop[0] not in self.propertyIds():
                self.manage_addProperty(id=prop[0],
                                        type=prop[1],
                                        value=prop[3])
                self._properties[-1]['mode'] = prop[2]
        # Delete any existing properties that aren't in config
        ids.update({'prefix':'', 'title':''})
        for prop in self._properties:
            if prop['id'] not in ids:
                self.manage_delProperties(prop['id'])

    security.declarePublic('getSharingConfig')
    def getSharingConfig(self):
        """Return the items end users can use to share with.

        Verify it returns an empty configuration.
        >>> from Products.ShibbolethPermissions.permissions import ShibbolethPermissions
        >>> handler = ShibbolethPermissions()
        >>> handler.getSharingConfig()
        {'http_sharing_tokens': (), 'http_sharing_labels': ()}
        """
        if IS_AUMPAS_INSTALLED:
            autoUserMaker = getToolByName(self, 'AutoUserMakerPASPlugin', None)
            if autoUserMaker is not None:
                return autoUserMaker.getSharingConfig()
        return {httpSharingTokensKey: self.getProperty(httpSharingTokensKey),
                 httpSharingLabelsKey: self.getProperty(httpSharingLabelsKey)}

    security.declarePrivate('getShibValues')
    def getShibValues(self):
        config = self.getSharingConfig()
        request = getattr(self, 'REQUEST')
        req_environ = getattr(request, 'environ', {})
        return dict([(ii, req_environ.get(ii))
                     for ii in config[httpSharingTokensKey]
                     if ii in req_environ])

    security.declarePublic('getLocalRoles')
    def getLocalRoles(self, path=None, **params):
        """Return the self.localRoles as a dictionary or list.

        Return a dictionay if neither path nor params is set (whole dictionary
        of lists of dictionaries). Return a dictionary if params is set, but
        path is not (subset of the whole dictionary of lists of dictionaries).
        Return a list of dictionaries when path is set, which may be an empty
        list if the path is not found or params is given in addition to path
        and none of the params match.

        This simple test returns everything, which at this point is nothing.
        """
        roles = {}
        param_keys = params.keys().sort()
        for ii in self.localRoles.iterkeys():
            roles[ii] = list(self.localRoles[ii])
        if not path and not params:
            return roles        # no select given, so return everything
        if path:
            if not path in roles:
                return []   # path not found, so return nothing
            if not params:  # path found, but no subcriteria so return whole list
                return roles[path]
            return _searchParams(roles[path], param_keys, **params)
        # no path, but params, so return a path keyed dict of lists of dicts
        rval = {}
        for ii in roles.iterkeys():  # each key is a Plone path
            found = _searchParams(roles[ii], param_keys, **params)
            if found:           # Don't save empty lists
                rval[ii] = found
        return rval

    security.declarePublic('addLocalRoles')
    def addLocalRoles(self, path, params, roles):
        """Add a pattern for path of params."""
        params['_roles'] = roles
        if path in self.localRoles:
            self.localRoles[path].append(params)
        else:
            self.localRoles[path] = PersistentList([params,])

    security.declarePublic('delLocalRoles')
    def delLocalRoles(self, path=None, row=None):
        """ Delete the specified roles.
        """
        if path is None and row is None:
            self.localRoles.clear()
        elif path in self.localRoles:
            if row is not None:
                del self.localRoles[path][row]
            else:
                del self.localRoles[path]

    security.declarePublic('updLocalRoles')
    def updLocalRoles(self, path=None, row=None, roles=None):
        """ Update the specified roles.
        """
        if roles is None:
            roles = []
        if path and path in self.localRoles and row is not None:
            try:
                self.localRoles[path][row]['_roles'] = roles
            except (IndexError, TypeError):
                logger.warning("updLocalRoles error updating row %s from %s"
                               % (str(row), str(path)), exc_info=True)

    def _findroles(self, context):
        uservals = self.getShibValues()
        regexs = self.getLocalRoles().get('/'.join(context.getPhysicalPath()), None)
        if regexs is None:
            return []

        for ii in regexs:
            # Make sure the incoming user has all of the
            # needed attributes
            for name in ii.iterkeys():
                if name == '_roles':
                    continue
                if not name in uservals:
                    break
            else:
                for name, pattern in ii.iteritems():
                    if name == '_roles' or uservals[name] is None:
                        continue
                    try:
                        regex = re.compile(pattern)
                        if not regex.search(uservals[name]):
                            break
                    except (ConflictError, KeyboardInterrupt):
                        raise
                    except Exception:
                        break
                else:
                    return list(ii['_roles'])
        return []

    def refreshlocalroles(self, user=None):
        if user is None:
            userid = _getAuthenticatedUser(None).getId()
        else:
            userid = user.getId()
        if not userid:
            return
        for path in self.localRoles.iterkeys():
            obj = self.unrestrictedTraverse(path, None)
            if obj is not None:
                roles = self._findroles(obj)
                reindex = False
                current_localroles = obj.get_local_roles_for_userid(userid)
                if not roles and current_localroles:
                    obj.manage_delLocalRoles((userid,))
                    reindex = True
                elif tuple(roles) != current_localroles:
                    obj.manage_setLocalRoles(userid, roles)
                    reindex = True
                if reindex:
                    obj.reindexObjectSecurity()
class Taxonomy(SimpleItem):
    order = None
    count = None
    version = None

    def __init__(self, name, title, default_language):
        self.data = PersistentDict()
        self.order = PersistentDict()
        self.count = PersistentDict()
        self.version = PersistentDict()

        self.name = name
        self.title = title
        self.default_language = default_language

    @property
    def sm(self):
        return api.portal.get().getSiteManager()

    def __call__(self, context):
        if not self.data:
            return Vocabulary(self.name, {}, {}, {}, 2)

        request = getattr(context, "REQUEST", None)
        language = self.getCurrentLanguage(request)
        return self.makeVocabulary(language)

    @property
    @ram.cache(lambda method, self: (self.name, self.data._p_mtime))
    def inverted_data(self):
        inv_data = {}
        for (language, elements) in self.data.items():
            inv_data[language] = {}
            for (path, identifier) in elements.items():
                inv_data[language][identifier] = path
        return inv_data

    def getShortName(self):
        return self.name.split('.')[-1]

    def getGeneratedName(self):
        return 'collective.taxonomy.generated.' + self.getShortName()

    def getVocabularyName(self):
        return 'collective.taxonomy.' + self.getShortName()

    def makeVocabulary(self, language):
        self._fixup()
        data = self.data.get(language, {})
        order = self.order.get(language)
        version = self.version.get(language, 1)
        inverted_data = self.inverted_data.get(language, {})
        return Vocabulary(self.name, data, inverted_data, order, version)

    def getCurrentLanguage(self, request):
        language = get_lang_code()
        if language in self.data:
            return language
        elif self.default_language in self.data:
            return self.default_language
        else:
            # our best guess!
            return self.data.keys()[0]

    def getLanguages(self):
        return tuple(self.data)

    def iterLanguage(self, language=None):
        if language is None:
            language = self.default_language

        vocabulary = self.makeVocabulary(language)

        for path, identifier in vocabulary.iterEntries():
            parent_path = path.rsplit(PATH_SEPARATOR, 1)[0]
            if parent_path:
                parent = vocabulary.getTermByValue(parent_path)
            else:
                parent = None
            yield path, identifier, parent

    def registerBehavior(self, **kwargs):
        new_args = copy(kwargs)

        new_args['name'] = self.getGeneratedName()
        new_args['title'] = self.title
        new_args['description'] = kwargs.get('field_description', u'')
        new_args['field_description'] = new_args['description']

        behavior = TaxonomyBehavior(**new_args)
        self.sm.registerUtility(behavior, IBehavior,
                                name=self.getGeneratedName())

        behavior.addIndex()
        behavior.activateSearchable()

    def cleanupFTI(self):
        """Cleanup the FTIs"""
        generated_name = self.getGeneratedName()
        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if generated_name in fti.behaviors:
                fti.behaviors = [behavior for behavior in
                                 fti.behaviors
                                 if behavior != generated_name]
            modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def updateBehavior(self, **kwargs):
        behavior_name = self.getGeneratedName()
        short_name = self.getShortName()

        utility = self.sm.queryUtility(IBehavior, name=behavior_name)
        if utility:
            utility.deactivateSearchable()
            utility.activateSearchable()
            if 'field_title' in kwargs:
                utility.title = kwargs.pop('field_title')

            for k, v in kwargs.items():
                setattr(utility, k, v)

        delattr(generated, short_name)

        for (name, fti) in self.sm.getUtilitiesFor(IDexterityFTI):
            if behavior_name in fti.behaviors:
                modified(fti, DexterityFTIModificationDescription("behaviors", ''))

    def unregisterBehavior(self):
        behavior_name = self.getGeneratedName()
        utility = self.sm.queryUtility(IBehavior, name=behavior_name)

        if utility is None:
            return

        self.cleanupFTI()

        utility.removeIndex()
        utility.deactivateSearchable()
        utility.unregisterInterface()

        self.sm.unregisterUtility(utility, IBehavior, name=behavior_name)

    def clean(self):
        self.data.clear()

    def add(self, language, value, key):
        self._fixup()
        tree = self.data.get(language)
        if tree is None:
            tree = self.data[language] = OOBTree()
        else:
            # Make sure we update the modification time.
            self.data[language] = tree

        update = key in tree
        tree[key] = value

        order = self.order.get(language)
        if order is None:
            order = self.order[language] = IOBTree()
            count = self.count[language] = 0
        else:
            if update:
                pop_value(tree, key)

            count = self.count[language] + 1

        self.count[language] = count
        order[count] = key

    def update(self, language, items, clear=False):
        self._fixup()

        tree = self.data.setdefault(language, OOBTree())
        if clear:
            tree.clear()

        # A new tree always uses the newest version.
        if not tree:
            version = self.version[language] = 2
        else:
            version = self.version.get(language, 1)

        order = self.order.setdefault(language, IOBTree())
        count = self.count.get(language, 0)

        if clear:
            order.clear()
            count = 0

        # Always migrate to newest version.
        if version == 1:
            def fix(path):
                return path.replace(LEGACY_PATH_SEPARATOR, PATH_SEPARATOR)

            for i in list(order):
                path = order[i]
                order[i] = fix(path)

            for path in list(tree):
                value = tree.pop(path)
                tree[fix(path)] = value

            version = self.version[language] = 2
            logger.info(
                "Taxonomy '%s' upgraded to version %d for language '%s'." % (
                    self.name, version, language
                )
            )

        # Make sure we update the modification time.
        self.data[language] = tree

        # The following structure is used to expunge updated entries.
        inv = {}
        if not clear:
            for i, key in order.items():
                inv[key] = i

        seen = set()
        for key, value in items:
            if key in seen:
                logger.warning("Duplicate key entry: %r" % (key, ))

            seen.add(key)
            update = key in tree
            tree[key] = value
            order[count] = key
            count += 1

            # If we're updating, then we have to pop out the old ordering
            # information in order to maintain relative ordering of new items.
            if update:
                i = inv.get(key)
                if i is not None:
                    del order[i]

        self.count[language] = count

    def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None, msgid_plural=None,
                  default_plural=None, number=None):

        if target_language is None or \
                target_language not in self.inverted_data:
            target_language = str(self.getCurrentLanguage(
                getattr(context, 'REQUEST')
            ))

        if msgid not in self.inverted_data[target_language]:
            return ''

        if self.version is not None and self.version.get(target_language) != 2:
            path_sep = LEGACY_PATH_SEPARATOR
        else:
            path_sep = PATH_SEPARATOR

        path = self.inverted_data[target_language][msgid]
        pretty_path = path[1:].replace(path_sep, PRETTY_PATH_SEPARATOR)

        if mapping is not None and mapping.get(NODE):
            pretty_path = pretty_path.rsplit(PRETTY_PATH_SEPARATOR, 1)[-1]

        return pretty_path

    def _fixup(self):
        # due to compatibility reasons this method fixes data structure
        # for old Taxonomy instances.
        # XXX: remove this in version 2.0 to prevent write on read
        if self.order is None:
            safeWrite(self, getRequest())
            self.order = PersistentDict()
            self.count = PersistentDict()

        if self.version is None:
            safeWrite(self, getRequest())
            self.version = PersistentDict()