Пример #1
0
class WorklistDefinition(SimpleItem):
    """Worklist definiton"""

    meta_type = 'Worklist'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    description = ''
    var_matches = None  # Compared with catalog when set.
    actbox_name = ''
    actbox_url = ''
    actbox_icon = ''
    actbox_category = 'global'
    guard = None

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

    def __init__(self, id):
        self.id = id

    def getGuard(self):
        if self.guard is not None:
            return self.guard
        else:
            return Guard().__of__(self)  # Create a temporary guard.

    def getGuardSummary(self):
        res = None
        if self.guard is not None:
            res = self.guard.getSummary()
        return res

    def getWorkflow(self):
        return aq_parent(aq_inner(aq_parent(aq_inner(self))))

    def getAvailableCatalogVars(self):
        res = []
        res.append(self.getWorkflow().state_var)
        for id, vdef in self.getWorkflow().variables.items():
            if vdef.for_catalog:
                res.append(id)
        res.sort()
        return res

    def getVarMatchKeys(self):
        if self.var_matches:
            return self.var_matches.keys()
        else:
            return []

    def getVarMatch(self, id):
        if self.var_matches:
            matches = self.var_matches.get(id, ())
            if not isinstance(matches, (tuple, Expression)):
                # Old version, convert it.
                matches = (matches,)
                self.var_matches[id] = matches
            return matches
        else:
            return ()

    def getVarMatchText(self, id):
        values = self.getVarMatch(id)
        if isinstance(values, Expression):
            return values.text
        return '; '.join(values)

    _properties_form = DTMLFile('worklist_properties', _dtmldir)

    def manage_properties(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._properties_form(REQUEST,
                                     management_view='Properties',
                                     manage_tabs_message=manage_tabs_message,
                                     )

    def setProperties(self, description,
                      actbox_name='', actbox_url='', actbox_category='global',
                      actbox_icon='', props=None, REQUEST=None):
        '''
        '''
        if props is None:
            props = REQUEST
        self.description = str(description)
        for key in self.getAvailableCatalogVars():
            # Populate var_matches.
            fieldname = 'var_match_%s' % key
            v = props.get(fieldname, '')
            if v:
                if not self.var_matches:
                    self.var_matches = PersistentMapping()

                if tales_re.match(v).group(1):
                    # Found a TALES prefix
                    self.var_matches[key] = Expression(v)
                else:
                    # Falling back to formatted string
                    v = [ var.strip() for var in v.split(';') ]
                    self.var_matches[key] = tuple(v)

            else:
                if self.var_matches and self.var_matches.has_key(key):
                    del self.var_matches[key]
        self.actbox_name = str(actbox_name)
        self.actbox_url = str(actbox_url)
        self.actbox_category = str(actbox_category)
        self.actbox_icon = str(actbox_icon)
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.guard = g
        else:
            self.guard = None
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')

    def search(self, info=None, **kw):
        """ Perform the search corresponding to this worklist

        Returns sequence of ZCatalog brains
        - info is a mapping for resolving formatted string variable references
        - additional keyword/value pairs may be used to restrict the query
        """
        if not self.var_matches:
            return

        if info is None:
            info = {}

        catalog = getToolByName(self, 'portal_catalog')
        criteria = {}

        for key, values in self.var_matches.items():
            if isinstance(values, Expression):
                wf = self.getWorkflow()
                portal = wf._getPortalRoot()
                context = createExprContext(StateChangeInfo(portal, wf))
                criteria[key] = values(context)
            else:
                criteria[key] = [x % info for x in values]

        criteria.update(kw)

        return catalog.searchResults(**criteria)
Пример #2
0
class ElementSpec(SimpleItem):

    """ Represent all the tool knows about a single metadata element.
    """

    security = ClassSecurityInfo()

    #
    #   Default values.
    #
    is_multi_valued = 0

    def __init__(self, is_multi_valued):
        self.is_multi_valued = is_multi_valued
        self.policies = PersistentMapping()
        self.policies[None] = self._makePolicy()  # set default policy

    security.declarePrivate('_makePolicy')
    def _makePolicy(self):
        return MetadataElementPolicy(self.is_multi_valued)

    security.declareProtected(View, 'isMultiValued')
    def isMultiValued(self):
        """
            Is this element multi-valued?
        """
        return self.is_multi_valued

    security.declareProtected(View, 'getPolicy')
    def getPolicy(self, typ=None):
        """ Find the policy for this element for objects of the given type.

        o Return a default, if none found.
        """
        try:
            return self.policies[typ].__of__(self)
        except KeyError:
            return self.policies[None].__of__(self)

    security.declareProtected(View, 'listPolicies')
    def listPolicies(self):
        """ Return a list of all policies for this element.
        """
        res = []
        for k, v in self.policies.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal, 'addPolicy')
    def addPolicy(self, typ):
        """ Add a policy to this element for objects of the given type.
        """
        if typ is None:
            raise MetadataError("Can't replace default policy.")

        if typ in self.policies:
            raise MetadataError("Existing policy for content type:" + typ)

        self.policies[typ] = self._makePolicy()

    security.declareProtected(ManagePortal, 'removePolicy')
    def removePolicy(self, typ):
        """ Remove the policy from this element for objects of the given type.

        o Do *not* remvoe the default, however.
        """
        if typ is None:
            raise MetadataError("Can't remove default policy.")
        del self.policies[typ]
Пример #3
0
class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
    """Stores messages and their translations...
    """

    meta_type = 'MessageCatalog'
    implements(IMessageCatalog)


    security = ClassSecurityInfo()


    POLICY_ADD_FALSE = 0
    POLICY_ADD_TRUE = 1
    POLICY_ADD_LOG = 2


    def __init__(self, id, title, sourcelang, languages):
        self.id = id

        self.title = title
        self.policy = self.POLICY_ADD_TRUE

        # Language Manager data
        self._languages = tuple(languages)
        self._default_language = sourcelang

        # Here the message translations are stored
        self._messages = PersistentMapping()

        # Data for the PO files headers
        self._po_headers = PersistentMapping()
        for lang in self._languages:
            self._po_headers[lang] = empty_po_header


    #######################################################################
    # ITranslationDomain interface
    # zope.i18n.interfaces.ITranslationDomain
    #######################################################################
    @property
    def domain(self):
        """ """
        return unicode(self.id)


    def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None):
        """ """
        msgstr = self.gettext(msgid, lang=target_language, default=default)
        # BBB support str in mapping by converting to unicode for
        # backward compatibility.
        if mapping:
            mapping = dict([to_unicode(k), to_unicode(v)]
                            for k, v in mapping.iteritems())
        return interpolate(msgstr, mapping)


    #######################################################################
    # Private API
    #######################################################################
    def get_message_key(self, message):
        if message in self._messages:
            return message
        # A message may be stored as unicode or byte string
        encoding = HTTPRequest.default_encoding
        if isinstance(message, unicode):
            message = message.encode(encoding)
        else:
            message = unicode(message, encoding)
        if message in self._messages:
            return message


    def get_translations(self, message):
        message = self.get_message_key(message)
        return self._messages[message]


    def get_tabs_message(self, REQUEST):
        message = REQUEST.get('manage_tabs_message')
        if message is None:
            return None
        return unicode(message, 'utf-8')


    #######################################################################
    # Public API
    #######################################################################
    security.declarePublic('message_exists')
    def message_exists(self, message):
        """ """
        # BBB call get_message_key to support both (old) str key and
        # (new) unicode key.
        return bool(self.get_message_key(message))


    security.declareProtected('Manage messages', 'message_edit')
    def message_edit(self, message, language, translation, note):
        """ """
        # BBB call get_message_key to support both (old) str key and
        # (new) unicode key.
        message = self.get_message_key(message) or message
        self._messages[message][language] = translation
        self._messages[message]['note'] = note


    security.declareProtected('Manage messages', 'message_del')
    def message_del(self, message):
        """ """
        # BBB call get_message_key to support both (old) str key and
        # (new) unicode key.
        message = self.get_message_key(message) or message
        del self._messages[message]


    security.declarePublic('gettext')
    def gettext(self, message, lang=None, add=None, default=None):
        """Returns the message translation from the database if available.

        If add=1, add any unknown message to the database.
        If a default is provided, use it instead of the message id
        as a translation for unknown messages.
        """
        if not isinstance(message, basestring):
            raise TypeError, 'only strings can be translated.'

        if default is None:
            default = message

        message = message.strip()

        # BBB call get_message_key to support both (old) str key and
        # (new) unicode key.
        message = self.get_message_key(message) or to_unicode(message)

        # Add it if it's not in the dictionary
        if add is None:
            add = getattr(self, 'policy', self.POLICY_ADD_TRUE)
        if add != self.POLICY_ADD_FALSE and not self._messages.has_key(message) and message:
            if add == self.POLICY_ADD_LOG:
                LOG('New entry added to message catalog %s :' % self.id,  INFO, '%s\n%s' % (message, ''.join(format_list(extract_stack()[:-1]))))
            self._messages[message] = PersistentMapping()

        # Get the string
        if self._messages.has_key(message):
            m = self._messages[message]

            if lang is None:
                # Builds the list of available languages
                # should the empty translations be filtered?
                available_languages = list(self._languages)

                # Imagine that the default language is 'en'. There is no
                # translation from 'en' to 'en' in the message catalog
                # The user has the preferences 'en' and 'nl' in that order
                # The next two lines make certain 'en' is shown, not 'nl'
                if not self._default_language in available_languages:
                    available_languages.append(self._default_language)

                # Get the language!
                lang = lang_negotiator(available_languages)

                # Is it None? use the default
                if lang is None:
                    lang = self._default_language

            if lang is not None:
                return m.get(lang) or default

        return default


    __call__ = gettext


    #######################################################################
    # Management screens
    #######################################################################
    manage_options = (
        {'label': u'Messages', 'action': 'manage_messages',
         'help': ('Localizer', 'MC_messages.stx')},
        {'label': u'Properties', 'action': 'manage_propertiesForm'},
        {'label': u'Import', 'action': 'manage_Import_form',
         'help': ('Localizer', 'MC_importExport.stx')},
        {'label': u'Export', 'action': 'manage_Export_form',
         'help': ('Localizer', 'MC_importExport.stx')}) \
        + LanguageManager.manage_options \
        + SimpleItem.manage_options


    #######################################################################
    # Management screens -- Messages
    #######################################################################
    security.declareProtected('Manage messages', 'manage_messages')
    manage_messages = LocalDTMLFile('ui/MC_messages', globals())


    security.declarePublic('get_namespace')
    def get_namespace(self, REQUEST):
        """For the management interface, allows to filter the messages to
        show.
        """
        # Check whether there are languages or not
        languages = self.get_languages_mapping()
        if not languages:
            return {}

        # Input
        batch_start = REQUEST.get('batch_start', 0)
        batch_size = REQUEST.get('batch_size', 15)
        empty = REQUEST.get('empty', 0)
        regex = REQUEST.get('regex', '')
        message = REQUEST.get('msg', None)

        # Build the namespace
        namespace = {}
        namespace['batch_size'] = batch_size
        namespace['empty'] = empty
        namespace['regex'] = regex

        # The language
        lang = REQUEST.get('lang', None) or languages[0]['code']
        namespace['language'] = lang

        # Filter the messages
        query = regex.strip()
        try:
            query = compile(query)
        except:
            query = compile('')

        messages = []
        for m, t in self._messages.items():
            if query.search(m) and (not empty or not t.get(lang, '').strip()):
                messages.append(m)
        messages.sort(filter_sort)
        # How many messages
        n = len(messages)
        namespace['n_messages'] = n

        # Calculate the start
        while batch_start >= n:
            batch_start = batch_start - batch_size
        if batch_start < 0:
            batch_start = 0
        namespace['batch_start'] = batch_start
        # Select the batch to show
        batch_end = batch_start + batch_size
        messages = messages[batch_start:batch_end]
        # Batch links
        namespace['previous'] = get_url(REQUEST.URL, batch_start - batch_size,
            batch_size, regex, lang, empty)
        namespace['next'] = get_url(REQUEST.URL, batch_start + batch_size,
            batch_size, regex, lang, empty)

        # Get the message
        message_encoded = None
        translations = {}
        if message is None:
            if messages:
                message = messages[0]
                translations = self.get_translations(message)
                message = to_unicode(message)
                message_encoded = message_encode(message)
        else:
            message_encoded = message
            message = message_decode(message_encoded)
            translations = self.get_translations(message)
            message = to_unicode(message)
        namespace['message'] = message
        namespace['message_encoded'] = message_encoded
        namespace['translations'] = translations
        namespace['translation'] = translations.get(lang, '')
        namespace['note'] = translations.get('note', '')

        # Calculate the current message
        namespace['messages'] = []
        for x in messages:
            x = to_unicode(x)
            x_encoded = message_encode(x)
            url = get_url(
                REQUEST.URL, batch_start, batch_size, regex, lang, empty,
                msg=x_encoded)
            namespace['messages'].append({
                'message': x,
                'message_encoded': x_encoded,
                'current': x == message,
                'url': url})

        # The languages
        for language in languages:
            code = language['code']
            language['name'] = _(language['name'], language=code)
            language['url'] = get_url(REQUEST.URL, batch_start, batch_size,
                regex, code, empty, msg=message_encoded)
        namespace['languages'] = languages

        return namespace


    security.declareProtected('Manage messages', 'manage_editMessage')
    def manage_editMessage(self, message, language, translation, note,
                           REQUEST, RESPONSE):
        """Modifies a message.
        """
        message_encoded = message
        message = message_decode(message_encoded)
        message_key = self.get_message_key(message)
        self.message_edit(message_key, language, translation, note)

        url = get_url(REQUEST.URL1 + '/manage_messages',
                      REQUEST['batch_start'], REQUEST['batch_size'],
                      REQUEST['regex'], REQUEST.get('lang', ''),
                      REQUEST.get('empty', 0),
                      msg=message_encoded,
                      manage_tabs_message=_(u'Saved changes.'))
        RESPONSE.redirect(url)


    security.declareProtected('Manage messages', 'manage_delMessage')
    def manage_delMessage(self, message, REQUEST, RESPONSE):
        """ """
        message = message_decode(message)
        message_key = self.get_message_key(message)
        self.message_del(message_key)

        url = get_url(REQUEST.URL1 + '/manage_messages',
                      REQUEST['batch_start'], REQUEST['batch_size'],
                      REQUEST['regex'], REQUEST.get('lang', ''),
                      REQUEST.get('empty', 0),
                      manage_tabs_message=_(u'Saved changes.'))
        RESPONSE.redirect(url)


    #######################################################################
    # Management screens -- Properties
    # Management screens -- Import/Export
    # FTP access
    #######################################################################
    security.declareProtected('View management screens',
                              'manage_propertiesForm')
    manage_propertiesForm = LocalDTMLFile('ui/MC_properties', globals())


    security.declareProtected('View management screens', 'manage_properties')
    def manage_properties(self, title, policy, REQUEST=None, RESPONSE=None):
        """Change the Message Catalog properties.
        """
        self.title = title
        self.policy = int(policy)

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')


    # Properties management screen
    security.declareProtected('View management screens', 'get_po_header')
    def get_po_header(self, lang):
        """ """
        # For backwards compatibility
        if not hasattr(aq_base(self), '_po_headers'):
            self._po_headers = PersistentMapping()

        return self._po_headers.get(lang, empty_po_header)


    security.declareProtected('View management screens', 'update_po_header')
    def update_po_header(self, lang,
                         last_translator_name=None,
                         last_translator_email=None,
                         language_team=None,
                         charset=None,
                         REQUEST=None, RESPONSE=None):
        """ """
        header = self.get_po_header(lang)

        if last_translator_name is None:
            last_translator_name = header['last_translator_name']

        if last_translator_email is None:
            last_translator_email = header['last_translator_email']

        if language_team is None:
            language_team = header['language_team']

        if charset is None:
            charset = header['charset']

        header = {'last_translator_name': last_translator_name,
                  'last_translator_email': last_translator_email,
                  'language_team': language_team,
                  'charset': charset}

        self._po_headers[lang] = header

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')



    security.declareProtected('View management screens', 'manage_Import_form')
    manage_Import_form = LocalDTMLFile('ui/MC_Import_form', globals())


    security.declarePublic('get_policies')
    def get_policies(self):
        """ """
        if not hasattr(self, 'policy'):
            self.policy = self.POLICY_ADD_TRUE
        policies = [
            [self.POLICY_ADD_FALSE, "Never add new entries automatically"],
            [self.POLICY_ADD_TRUE, "Add new entries automatically if missing"],
            [self.POLICY_ADD_LOG, "Add new entries automatically if missing and log the backtrace"],
        ]
        return policies


    security.declarePublic('get_charsets')
    def get_charsets(self):
        """ """
        return charsets[:]


    security.declarePublic('manage_export')
    def manage_export(self, x, REQUEST=None, RESPONSE=None):
        """Exports the content of the message catalog either to a template
        file (locale.pot) or to an language specific PO file (<x>.po).
        """
        # Get the PO header info
        header = self.get_po_header(x)
        last_translator_name = header['last_translator_name']
        last_translator_email = header['last_translator_email']
        language_team = header['language_team']
        charset = header['charset']

        # PO file header, empty message.
        po_revision_date = strftime('%Y-%m-%d %H:%m+%Z', gmtime(time()))
        pot_creation_date = po_revision_date
        last_translator = '%s <%s>' % (last_translator_name,
                                       last_translator_email)

        if x == 'locale.pot':
            language_team = 'LANGUAGE <*****@*****.**>'
        else:
            language_team = '%s <%s>' % (x, language_team)

        r = ['msgid ""',
             'msgstr "Project-Id-Version: %s\\n"' % self.title,
             '"POT-Creation-Date: %s\\n"' % pot_creation_date,
             '"PO-Revision-Date: %s\\n"' % po_revision_date,
             '"Last-Translator: %s\\n"' % last_translator,
             '"Language-Team: %s\\n"' % language_team,
             '"MIME-Version: 1.0\\n"',
             '"Content-Type: text/plain; charset=%s\\n"' % charset,
             '"Content-Transfer-Encoding: 8bit\\n"',
             '', '']


        # Get the messages, and perhaps its translations.
        # Convert keys to unicode for proper sorting.
        d = {}
        if x == 'locale.pot':
            filename = x
            for k in self._messages.keys():
                d[to_unicode(k, encoding=charset)] = u""
        else:
            filename = '%s.po' % x
            for k, v in self._messages.items():
                k = to_unicode(k, encoding=charset)
                d[k] = to_unicode(v.get(x, ""), encoding=charset)

        # Generate the file
        def backslashescape(x):
            x = to_str(x)
            quote_esc = compile(r'"')
            x = quote_esc.sub('\\"', x)

            trans = [('\n', '\\n'), ('\r', '\\r'), ('\t', '\\t')]
            for a, b in trans:
                x = x.replace(a, b)

            return x

        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        for k in dkeys:
            r.append('msgid "%s"' % backslashescape(k))
            v = d[k]
            r.append('msgstr "%s"' % backslashescape(v))
            r.append('')

        if RESPONSE is not None:
            RESPONSE.setHeader('Content-type','application/data')
            RESPONSE.setHeader('Content-Disposition',
                               'inline;filename=%s' % filename)

        return '\n'.join(r)


    security.declareProtected('Manage messages', 'po_import')
    def po_import(self, lang, data):
        """ """
        messages = self._messages

        # Load the data
        po = polib.pofile(data)
        encoding = to_str(po.encoding)
        for entry in po:
            msgid = to_unicode(entry.msgid, encoding=encoding)
            if msgid:
                msgstr = to_unicode(entry.msgstr or '', encoding=encoding)
                translation_map = messages.get(msgid)
                if translation_map is None:
                    # convert old non-unicode translations if they exist:
                    translation_map = messages.pop(self.get_message_key(msgid),
                                                   None)
                    if translation_map is None:
                        translation_map = PersistentMapping()
                    messages[msgid] = translation_map
                translation_map[lang] = msgstr

        # Set the encoding (the full header should be loaded XXX)
        self.update_po_header(lang, charset=encoding)


    security.declareProtected('Manage messages', 'manage_import')
    def manage_import(self, lang, file, REQUEST=None, RESPONSE=None):
        """ """
        # XXX For backwards compatibility only, use "po_import" instead.
        if isinstance(file, str):
            content = file
        else:
            content = file.read()

        self.po_import(lang, content)

        if RESPONSE is not None:
            RESPONSE.redirect('manage_messages')


    def objectItems(self, spec=None):
        """ """
        for lang in self._languages:
            if not hasattr(aq_base(self), lang):
                self._setObject(lang, POFile(lang))

        r = MessageCatalog.inheritedAttribute('objectItems')(self, spec)
        return r


    #######################################################################
    # TMX support
    security.declareProtected('View management screens', 'manage_Export_form')
    manage_Export_form = LocalDTMLFile('ui/MC_Export_form', globals())


    #######################################################################
    # Backwards compatibility (XXX)
    #######################################################################

    hasmsg = message_exists
    hasLS = message_exists  # CMFLocalizer uses it
Пример #4
0
class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
    """Stores messages and their translations...
    """

    meta_type = 'MessageCatalog'
    implements(IMessageCatalog)


    security = ClassSecurityInfo()


    def __init__(self, id, title, sourcelang, languages):
        self.id = id

        self.title = title

        # Language Manager data
        self._languages = tuple(languages)
        self._default_language = sourcelang

        # Here the message translations are stored
        self._messages = PersistentMapping()

        # Data for the PO files headers
        self._po_headers = PersistentMapping()
        for lang in self._languages:
            self._po_headers[lang] = empty_po_header


    #######################################################################
    # ITranslationDomain interface
    # zope.i18n.interfaces.ITranslationDomain
    #######################################################################
    @property
    def domain(self):
        """ """
        return unicode(self.id)


    def translate(self, msgid, mapping=None, context=None,
                  target_language=None, default=None):
        """ """
        msgstr = self.gettext(msgid, lang=target_language, default=default)
        return interpolate(msgstr, mapping)


    #######################################################################
    # Private API
    #######################################################################
    def get_message_key(self, message):
        if message in self._messages:
            return message
        # A message may be stored as unicode or byte string
        encoding = HTTPRequest.default_encoding
        if isinstance(message, unicode):
            message = message.encode(encoding)
        else:
            message = unicode(message, encoding)
        if message in self._messages:
            return message


    def get_translations(self, message):
        message = self.get_message_key(message)
        return self._messages[message]


    def get_tabs_message(self, REQUEST):
        message = REQUEST.get('manage_tabs_message')
        if message is None:
            return None
        return unicode(message, 'utf-8')


    #######################################################################
    # Public API
    #######################################################################
    security.declarePublic('message_exists')
    def message_exists(self, message):
        """ """
        return self._messages.has_key(message)


    security.declareProtected('Manage messages', 'message_edit')
    def message_edit(self, message, language, translation, note):
        """ """
        self._messages[message][language] = translation
        self._messages[message]['note'] = note


    security.declareProtected('Manage messages', 'message_del')
    def message_del(self, message):
        """ """
        del self._messages[message]


    security.declarePublic('gettext')
    def gettext(self, message, lang=None, add=1, default=None):
        """Returns the message translation from the database if available.

        If add=1, add any unknown message to the database.
        If a default is provided, use it instead of the message id
        as a translation for unknown messages.
        """
        if not isinstance(message, (str, unicode)):
            raise TypeError, 'only strings can be translated.'

        message = message.strip()

        if default is None:
            default = message

        # Add it if it's not in the dictionary
        if add and not self._messages.has_key(message) and message:
            self._messages[message] = PersistentMapping()

        # Get the string
        if self._messages.has_key(message):
            m = self._messages[message]

            if lang is None:
                # Builds the list of available languages
                # should the empty translations be filtered?
                available_languages = list(self._languages)

                # Imagine that the default language is 'en'. There is no
                # translation from 'en' to 'en' in the message catalog
                # The user has the preferences 'en' and 'nl' in that order
                # The next two lines make certain 'en' is shown, not 'nl'
                if not self._default_language in available_languages:
                    available_languages.append(self._default_language)

                # Get the language!
                lang = lang_negotiator(available_languages)

                # Is it None? use the default
                if lang is None:
                    lang = self._default_language

            if lang is not None:
                return m.get(lang) or default

        return default


    __call__ = gettext


    #######################################################################
    # Management screens
    #######################################################################
    manage_options = (
        {'label': u'Messages', 'action': 'manage_messages',
         'help': ('Localizer', 'MC_messages.stx')},
        {'label': u'Properties', 'action': 'manage_propertiesForm'},
        {'label': u'Import', 'action': 'manage_Import_form',
         'help': ('Localizer', 'MC_importExport.stx')},
        {'label': u'Export', 'action': 'manage_Export_form',
         'help': ('Localizer', 'MC_importExport.stx')}) \
        + LanguageManager.manage_options \
        + SimpleItem.manage_options


    #######################################################################
    # Management screens -- Messages
    #######################################################################
    security.declareProtected('Manage messages', 'manage_messages')
    manage_messages = LocalDTMLFile('ui/MC_messages', globals())


    security.declarePublic('get_namespace')
    def get_namespace(self, REQUEST):
        """For the management interface, allows to filter the messages to
        show.
        """
        # Check whether there are languages or not
        languages = self.get_languages_mapping()
        if not languages:
            return {}

        # Input
        batch_start = REQUEST.get('batch_start', 0)
        batch_size = REQUEST.get('batch_size', 15)
        empty = REQUEST.get('empty', 0)
        regex = REQUEST.get('regex', '')
        message = REQUEST.get('msg', None)

        # Build the namespace
        namespace = {}
        namespace['batch_size'] = batch_size
        namespace['empty'] = empty
        namespace['regex'] = regex

        # The language
        lang = REQUEST.get('lang', None) or languages[0]['code']
        namespace['language'] = lang

        # Filter the messages
        query = regex.strip()
        try:
            query = compile(query)
        except:
            query = compile('')

        messages = []
        for m, t in self._messages.items():
            if query.search(m) and (not empty or not t.get(lang, '').strip()):
                messages.append(m)
        messages.sort(filter_sort)
        # How many messages
        n = len(messages)
        namespace['n_messages'] = n

        # Calculate the start
        while batch_start >= n:
            batch_start = batch_start - batch_size
        if batch_start < 0:
            batch_start = 0
        namespace['batch_start'] = batch_start
        # Select the batch to show
        batch_end = batch_start + batch_size
        messages = messages[batch_start:batch_end]
        # Batch links
        namespace['previous'] = get_url(REQUEST.URL, batch_start - batch_size,
            batch_size, regex, lang, empty)
        namespace['next'] = get_url(REQUEST.URL, batch_start + batch_size,
            batch_size, regex, lang, empty)

        # Get the message
        message_encoded = None
        translations = {}
        if message is None:
            if messages:
                message = messages[0]
                translations = self.get_translations(message)
                message = to_unicode(message)
                message_encoded = message_encode(message)
        else:
            message_encoded = message
            message = message_decode(message_encoded)
            translations = self.get_translations(message)
            message = to_unicode(message)
        namespace['message'] = message
        namespace['message_encoded'] = message_encoded
        namespace['translations'] = translations
        namespace['translation'] = translations.get(lang, '')
        namespace['note'] = translations.get('note', '')

        # Calculate the current message
        namespace['messages'] = []
        for x in messages:
            x = to_unicode(x)
            x_encoded = message_encode(x)
            url = get_url(
                REQUEST.URL, batch_start, batch_size, regex, lang, empty,
                msg=x_encoded)
            namespace['messages'].append({
                'message': x,
                'message_encoded': x_encoded,
                'current': x == message,
                'url': url})

        # The languages
        for language in languages:
            code = language['code']
            language['name'] = _(language['name'], language=code)
            language['url'] = get_url(REQUEST.URL, batch_start, batch_size,
                regex, code, empty, msg=message_encoded)
        namespace['languages'] = languages

        return namespace


    security.declareProtected('Manage messages', 'manage_editMessage')
    def manage_editMessage(self, message, language, translation, note,
                           REQUEST, RESPONSE):
        """Modifies a message.
        """
        message_encoded = message
        message = message_decode(message_encoded)
        message_key = self.get_message_key(message)
        self.message_edit(message_key, language, translation, note)

        url = get_url(REQUEST.URL1 + '/manage_messages',
                      REQUEST['batch_start'], REQUEST['batch_size'],
                      REQUEST['regex'], REQUEST.get('lang', ''),
                      REQUEST.get('empty', 0),
                      msg=message_encoded,
                      manage_tabs_message=_(u'Saved changes.'))
        RESPONSE.redirect(url)


    security.declareProtected('Manage messages', 'manage_delMessage')
    def manage_delMessage(self, message, REQUEST, RESPONSE):
        """ """
        message = message_decode(message)
        message_key = self.get_message_key(message)
        self.message_del(message_key)

        url = get_url(REQUEST.URL1 + '/manage_messages',
                      REQUEST['batch_start'], REQUEST['batch_size'],
                      REQUEST['regex'], REQUEST.get('lang', ''),
                      REQUEST.get('empty', 0),
                      manage_tabs_message=_(u'Saved changes.'))
        RESPONSE.redirect(url)


    #######################################################################
    # Management screens -- Properties
    # Management screens -- Import/Export
    # FTP access
    #######################################################################
    security.declareProtected('View management screens',
                              'manage_propertiesForm')
    manage_propertiesForm = LocalDTMLFile('ui/MC_properties', globals())


    security.declareProtected('View management screens', 'manage_properties')
    def manage_properties(self, title, REQUEST=None, RESPONSE=None):
        """Change the Message Catalog properties.
        """
        self.title = title

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')


    # Properties management screen
    security.declareProtected('View management screens', 'get_po_header')
    def get_po_header(self, lang):
        """ """
        # For backwards compatibility
        if not hasattr(aq_base(self), '_po_headers'):
            self._po_headers = PersistentMapping()

        return self._po_headers.get(lang, empty_po_header)


    security.declareProtected('View management screens', 'update_po_header')
    def update_po_header(self, lang,
                         last_translator_name=None,
                         last_translator_email=None,
                         language_team=None,
                         charset=None,
                         REQUEST=None, RESPONSE=None):
        """ """
        header = self.get_po_header(lang)

        if last_translator_name is None:
            last_translator_name = header['last_translator_name']

        if last_translator_email is None:
            last_translator_email = header['last_translator_email']

        if language_team is None:
            language_team = header['language_team']

        if charset is None:
            charset = header['charset']

        header = {'last_translator_name': last_translator_name,
                  'last_translator_email': last_translator_email,
                  'language_team': language_team,
                  'charset': charset}

        self._po_headers[lang] = header

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')



    security.declareProtected('View management screens', 'manage_Import_form')
    manage_Import_form = LocalDTMLFile('ui/MC_Import_form', globals())


    security.declarePublic('get_charsets')
    def get_charsets(self):
        """ """
        return charsets[:]


    security.declarePublic('manage_export')
    def manage_export(self, x, REQUEST=None, RESPONSE=None):
        """Exports the content of the message catalog either to a template
        file (locale.pot) or to an language specific PO file (<x>.po).
        """
        # Get the PO header info
        header = self.get_po_header(x)
        last_translator_name = header['last_translator_name']
        last_translator_email = header['last_translator_email']
        language_team = header['language_team']
        charset = header['charset']

        # PO file header, empty message.
        po_revision_date = strftime('%Y-%m-%d %H:%m+%Z', gmtime(time()))
        pot_creation_date = po_revision_date
        last_translator = '%s <%s>' % (last_translator_name,
                                       last_translator_email)

        if x == 'locale.pot':
            language_team = 'LANGUAGE <*****@*****.**>'
        else:
            language_team = '%s <%s>' % (x, language_team)

        r = ['msgid ""',
             'msgstr "Project-Id-Version: %s\\n"' % self.title,
             '"POT-Creation-Date: %s\\n"' % pot_creation_date,
             '"PO-Revision-Date: %s\\n"' % po_revision_date,
             '"Last-Translator: %s\\n"' % last_translator,
             '"Language-Team: %s\\n"' % language_team,
             '"MIME-Version: 1.0\\n"',
             '"Content-Type: text/plain; charset=%s\\n"' % charset,
             '"Content-Transfer-Encoding: 8bit\\n"',
             '', '']


        # Get the messages, and perhaps its translations.
        d = {}
        if x == 'locale.pot':
            filename = x
            for k in self._messages.keys():
                d[k] = ""
        else:
            filename = '%s.po' % x
            for k, v in self._messages.items():
                try:
                    d[k] = v[x]
                except KeyError:
                    d[k] = ""

        # Generate the file
        def backslashescape(x):
            quote_esc = compile(r'"')
            x = quote_esc.sub('\\"', x)

            trans = [('\n', '\\n'), ('\r', '\\r'), ('\t', '\\t')]
            for a, b in trans:
                x = x.replace(a, b)

            return x

        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        for k in dkeys:
            r.append('msgid "%s"' % backslashescape(k))
            v = d[k]
            r.append('msgstr "%s"' % backslashescape(v))
            r.append('')

        if RESPONSE is not None:
            RESPONSE.setHeader('Content-type','application/data')
            RESPONSE.setHeader('Content-Disposition',
                               'inline;filename=%s' % filename)

        r2 = []
        for x in r:
            if isinstance(x, unicode):
                r2.append(x.encode(charset))
            else:
                r2.append(x)

        return '\n'.join(r2)


    security.declareProtected('Manage messages', 'po_import')
    def po_import(self, lang, data):
        """ """
        messages = self._messages

        # Load the data
        po = itools.gettext.POFile(string=data)
        for msgid in po.get_msgids():
            # TODO Keep the context if any
            _context, msgid = msgid
            if msgid:
                msgstr = po.get_msgstr(msgid) or ''
                if not messages.has_key(msgid):
                    messages[msgid] = PersistentMapping()
                messages[msgid][lang] = msgstr

        # Set the encoding (the full header should be loaded XXX)
        self.update_po_header(lang, charset=po.get_encoding())


    security.declareProtected('Manage messages', 'manage_import')
    def manage_import(self, lang, file, REQUEST=None, RESPONSE=None):
        """ """
        # XXX For backwards compatibility only, use "po_import" instead.
        if isinstance(file, str):
            content = file
        else:
            content = file.read()

        self.po_import(lang, content)

        if RESPONSE is not None:
            RESPONSE.redirect('manage_messages')


    def objectItems(self, spec=None):
        """ """
        for lang in self._languages:
            if not hasattr(aq_base(self), lang):
                self._setObject(lang, POFile(lang))

        r = MessageCatalog.inheritedAttribute('objectItems')(self, spec)
        return r


    #######################################################################
    # TMX support
    security.declareProtected('View management screens', 'manage_Export_form')
    manage_Export_form = LocalDTMLFile('ui/MC_Export_form', globals())


    security.declareProtected('Manage messages', 'tmx_export')
    def tmx_export(self, REQUEST, RESPONSE=None):
        """Exports the content of the message catalog to a TMX file
        """
        src_lang = self._default_language

        # Get the header info
        header = self.get_po_header(src_lang)
        charset = header['charset']

        # Init the TMX handler
        tmx = TMXFile()
        tmx.header['creationtool'] = u'Localizer'
        tmx.header['creationtoolversion'] = u'1.x'
        tmx.header['datatype'] = u'plaintext'
        tmx.header['segtype'] = u'paragraph'
        tmx.header['adminlang'] = src_lang
        tmx.header['srclang'] = src_lang
        tmx.header['o-encoding'] = u'%s' % charset.lower()

        # handle messages
        for msgkey, transunit in self._messages.items():
            unit = TMXUnit({})
            for lang in transunit.keys():
                if lang != 'note':
                    sentence = Sentence({'lang': lang})
                    sentence.text = transunit[lang]
                    unit.msgstr[lang] = sentence

            if src_lang not in transunit.keys():
                sentence = Sentence({'lang': src_lang})
                sentence.text = msgkey
                unit.msgstr[src_lang] = sentence

            if transunit.has_key('note'):
                note = TMXNote(transunit.get('note'))
                unit.notes.append(note)
            tmx.messages[msgkey] = unit

        if RESPONSE is not None:
            RESPONSE.setHeader('Content-type','application/data')
            RESPONSE.setHeader('Content-Disposition',
                               'attachment; filename="%s.tmx"' % self.id)

        return tmx.to_str()



    security.declareProtected('Manage messages', 'tmx_import')
    def tmx_import(self, howmuch, file, REQUEST=None, RESPONSE=None):
        """Imports a TMX level 1 file.
        """
        try:
            data = file.read()
            tmx = TMXFile(string=data)
        except:
            return MessageDialog(title = 'Parse error',
                                 message = _('impossible to parse the file') ,
                                 action = 'manage_Import_form',)

        num_notes = 0
        num_trans = 0

        if howmuch == 'clear':
            # Clear the message catalogue prior to import
            self._messages = {}
            self._languages = ()
            self._default_language = tmx.get_srclang()

        for id, msg in tmx.messages.items():
            if not self._messages.has_key(id) and howmuch == 'existing':
                continue
            msg.msgstr.pop(self._default_language)
            if not self._messages.has_key(id):
                self._messages[id] = {}
            for lang in msg.msgstr.keys():
                # normalize the languageTag and extract the core
                (core, local) = LanguageTag.decode(lang)
                lang = LanguageTag.encode((core, local))
                if lang not in self._languages:
                    self._languages += (lang,)
                if msg.msgstr[lang].text:
                    self._messages[id][lang] = msg.msgstr[lang].text
                    if core != lang and core != self._default_language:
                        if core not in self._languages:
                            self._languages += (core,)
                        if not msg.msgstr.has_key(core):
                            self._messages[id][core] = msg.msgstr[lang].text
            if msg.notes:
                ns = [m.text for m in msg.notes]
                self._messages[id]['note'] = u' '.join(ns)
                num_notes += 1
            num_trans += 1

        if REQUEST is not None:
            message = _(u'Imported %d messages and %d notes')
            return MessageDialog(
                title = _(u'Messages imported'),
                message = message % (num_trans, num_notes),
                action = 'manage_messages')



    #######################################################################
    # Backwards compatibility (XXX)
    #######################################################################

    hasmsg = message_exists
    hasLS = message_exists  # CMFLocalizer uses it

    security.declareProtected('Manage messages', 'xliff_export')
    def xliff_export(self, dst_lang, export_all=1, REQUEST=None,
                     RESPONSE=None):
        """Exports the content of the message catalog to an XLIFF file
        """
        from DateTime import DateTime

        src_lang = self._default_language
        export_all = int(export_all)

        # Init the XLIFF handler
        xliff = XLFFile()
        # Add the translation units
        original = '/%s' % self.absolute_url(1)
        for msgkey, transunit in self._messages.items():
            target = transunit.get(dst_lang, '')
            # If 'export_all' is true export all messages, otherwise export
            # only untranslated messages
            if export_all or not target:
                unit = xliff.add_unit(original, msgkey, None)
                unit.attributes['id'] = md5text(msgkey)
                if target:
                    unit.target = target
                # Add note
                note = transunit.get('note')
                if note:
                    unit.notes.append(XLFNote(note))

        # build the data-stucture for the File tag
        file = xliff.files[original]
        attributes = file.attributes
        attributes['original'] = original
        attributes['product-name'] = u'Localizer'
        attributes['product-version'] = u'1.1.x'
        attributes['data-type'] = u'plaintext'
        attributes['source-language'] = src_lang
        attributes['target-language'] = dst_lang
        attributes['date'] = DateTime().HTML4()

        # Serialize
        xliff = xliff.to_str()
        # Set response headers
        RESPONSE.setHeader('Content-Type', 'text/xml; charset=UTF-8')
        filename = '%s_%s_%s.xlf' % (self.id, src_lang, dst_lang)
        RESPONSE.setHeader('Content-Disposition',
           'attachment; filename="%s"' % filename)
        # Ok
        return xliff


    security.declareProtected('Manage messages', 'xliff_import')
    def xliff_import(self, howmuch, file, REQUEST=None):
        """XLIFF is the XML Localization Interchange File Format designed by a
        group of software providers.  It is specified by www.oasis-open.org
        """
        try:
            data = file.read()
            xliff = XLFFile(string=data)
        except:
            return MessageDialog(title = 'Parse error',
                                 message = _('impossible to parse the file') ,
                                 action = 'manage_Import_form',)

        num_notes = 0
        num_trans = 0
        (file_ids, sources, targets) = xliff.get_languages()

        if howmuch == 'clear':
            # Clear the message catalogue prior to import
            self._messages = {}
            self._languages = ()
            self._default_language = sources[0]

        # update languages
        if len(sources) > 1 or sources[0] != self._default_language:
            return MessageDialog(title = 'Language error',
                                 message = _('incompatible language sources') ,
                                 action = 'manage_Import_form',)
        for lang in targets:
            if lang != self._default_language and lang not in self._languages:
                self._languages += (lang,)

        # get messages
        for file in xliff.files:
            cur_target = file.attributes.get('target-language', '')
            for msg in file.body.keys():
                if not self._messages.has_key(msg) and howmuch == 'existing':
                    pass
                else:
                    if not self._messages.has_key(msg):
                        self._messages[msg] = {}

                    if cur_target and file.body[msg].target:
                        self._messages[msg][cur_target] = file.body[msg].target
                        num_trans += 1
                    if file.body[msg].notes:
                        ns = [n.text for n in file.body[msg].notes]
                        comment = ' '.join(ns)
                        self._messages[msg]['note'] = comment
                        num_notes += 1

        if REQUEST is not None:
            return MessageDialog(
                title = _(u'Messages imported'),
                message = (_(u'Imported %d messages and %d notes to %s') % \
                           (num_trans, num_notes, ' '.join(targets))),
                action = 'manage_messages')
Пример #5
0
class ArchetypeTool(UniqueObject, ActionProviderBase,
                    SQLStorageConfig, Folder):
    """Archetypes tool, manage aspects of Archetype instances.
    """
    id = TOOL_NAME
    meta_type = TOOL_NAME.title().replace('_', ' ')

    implements(IArchetypeTool)

    isPrincipiaFolderish = True  # Show up in the ZMI

    security = ClassSecurityInfo()

    meta_types = all_meta_types = ()

    manage_options = (
        (
            {'label': 'Types',
             'action': 'manage_debugForm',
             },

            {'label': 'Catalogs',
             'action': 'manage_catalogs',
             },

            {'label': 'Templates',
             'action': 'manage_templateForm',
             },

            {'label': 'UIDs',
             'action': 'manage_uids',
             },

            {'label': 'Update Schema',
             'action': 'manage_updateSchemaForm',
             },

            {'label': 'Migration',
             'action': 'manage_migrationForm',
             },

        ) + SQLStorageConfig.manage_options
    )

    security.declareProtected(permissions.ManagePortal,
                              'manage_uids')
    manage_uids = PageTemplateFile('viewContents', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_templateForm')
    manage_templateForm = PageTemplateFile('manageTemplates', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_debugForm')
    manage_debugForm = PageTemplateFile('generateDebug', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_updateSchemaForm')
    manage_updateSchemaForm = PageTemplateFile('updateSchemaForm', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_migrationForm')
    manage_migrationForm = PageTemplateFile('migrationForm', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_dumpSchemaForm')
    manage_dumpSchemaForm = PageTemplateFile('schema', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_catalogs')
    manage_catalogs = PageTemplateFile('manage_catalogs', _www)

    def __init__(self):
        self._schemas = PersistentMapping()
        self._templates = PersistentMapping()
        self._registeredTemplates = PersistentMapping()
        # meta_type -> [names of CatalogTools]
        self.catalog_map = PersistentMapping()
        self.catalog_map['Reference'] = []  # References not in portal_catalog
        # DM (avoid persistency bug): "_types" now maps known schemas to
        # signatures
        self._types = {}

    security.declareProtected(permissions.ManagePortal,
                              'manage_dumpSchema')

    def manage_dumpSchema(self, REQUEST=None):
        """XML Dump Schema of passed in class.
        """
        from Products.Archetypes.Schema import getSchemata
        package = REQUEST.get('package', '')
        type_name = REQUEST.get('type_name', '')
        spec = self.getTypeSpec(package, type_name)
        type = self.lookupType(package, type_name)
        options = {}
        options['classname'] = spec
        options['schematas'] = getSchemata(type['klass'])
        REQUEST.RESPONSE.setHeader('Content-Type', 'text/xml')
        return self.manage_dumpSchemaForm(**options)

    # Template Management
    # Views can be pretty generic by iterating the schema so we don't
    # register by type anymore, we just create per site selection
    # lists
    #
    # We keep two lists, all register templates and their
    # names/titles and the mapping of type to template bindings both
    # are persistent
    security.declareProtected(permissions.ManagePortal,
                              'registerTemplate')

    def registerTemplate(self, template, name=None):
        # Lookup the template by name
        if not name:
            obj = self.unrestrictedTraverse(template, None)
            try:
                name = obj.title_or_id()
            except:
                name = template

        self._registeredTemplates[template] = name

    security.declareProtected(permissions.View, 'lookupTemplates')

    def lookupTemplates(self, instance_or_portaltype=None):
        """Lookup templates by giving an instance or a portal_type.

        Returns a DisplayList.
        """
        results = []
        if not isinstance(instance_or_portaltype, basestring):
            portal_type = instance_or_portaltype.getTypeInfo().getId()
        else:
            portal_type = instance_or_portaltype
        try:
            templates = self._templates[portal_type]
        except KeyError:
            return DisplayList()
            # XXX Look this up in the types tool later
            # self._templates[instance] = ['base_view',]
            # templates = self._templates[instance]
        for t in templates:
            results.append((t, self._registeredTemplates[t]))

        return DisplayList(results).sortedByValue()

    security.declareProtected(permissions.View, 'listTemplates')

    def listTemplates(self):
        """Lists all the templates.
        """
        return DisplayList(self._registeredTemplates.items()).sortedByValue()

    security.declareProtected(permissions.ManagePortal, 'bindTemplate')

    def bindTemplate(self, portal_type, templateList):
        """Creates binding between a type and its associated views.
        """
        self._templates[portal_type] = templateList

    security.declareProtected(permissions.ManagePortal,
                              'manage_templates')

    def manage_templates(self, REQUEST=None):
        """Sets all the template/type mappings.
        """
        prefix = 'template_names_'
        for key in REQUEST.form.keys():
            if key.startswith(prefix):
                k = key[len(prefix):]
                v = REQUEST.form.get(key)
                self.bindTemplate(k, v)

        add = REQUEST.get('addTemplate')
        name = REQUEST.get('newTemplate')
        if add and name:
            self.registerTemplate(name)

        return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_templateForm')

    security.declareProtected(permissions.View, 'typeImplementsInterfaces')

    def typeImplementsInterfaces(self, type, interfaces):
        """Checks if an type uses one of the given interfaces.
        """
        if isinstance(type, dict) and 'klass' in type:
            type = type['klass']
        for iface in interfaces:
            res = iface.implementedBy(type)
            if res:
                return True
        return False

    security.declareProtected(permissions.View, 'isTemplateEnabled')

    def isTemplateEnabled(self, type):
        """Checks if an type uses ITemplateMixin.
        """
        return self.typeImplementsInterfaces(type, [ITemplateMixin])

    security.declareProtected(
        permissions.View, 'listTemplateEnabledPortalTypes')

    def listTemplateEnabledPortalTypes(self):
        """Return a list of portal_types with ITemplateMixin
        """
        return self.listPortalTypesWithInterfaces([ITemplateMixin])

    security.declareProtected(
        permissions.View, 'listPortalTypesWithInterfaces')

    def listPortalTypesWithInterfaces(self, ifaces):
        """Returns a list of ftis of which the types implement one of
        the given interfaces.  Only returns AT types.

        Get a list of FTIs of types implementing IReferenceable:
        >>> portal = layer['portal']
        >>> from Products.Archetypes.config import TOOL_NAME
        >>> tool = getToolByName(portal, TOOL_NAME)
        >>> ftis = tool.listPortalTypesWithInterfaces([IReferenceable])

        Sort the type ids and print them:
        >>> type_ids = [fti.getId() for fti in ftis]
        >>> type_ids.sort()
        >>> type_ids
        ['ATBIFolder', 'Collection', 'ComplexType', ...]
        """
        pt = getToolByName(self, 'portal_types')
        value = []
        for data in listTypes():
            klass = data['klass']
            for iface in ifaces:
                if iface.implementedBy(klass):
                    ti = pt.getTypeInfo(data['portal_type'])
                    if ti is not None:
                        value.append(ti)
        return value

    # Type/Schema Management
    security.declareProtected(permissions.View, 'listRegisteredTypes')

    def listRegisteredTypes(self, inProject=False, portalTypes=False):
        """Return the list of sorted types.
        """

        def type_sort(a, b):
            v = cmp(a['package'], b['package'])
            if v != False:
                return v

            c = cmp(a['klass'].__class__.__name__,
                    b['klass'].__class__.__name__)

            if c == False:
                return cmp(a['package'], b['package'])
            return c

        values = listTypes()
        values.sort(type_sort)

        if inProject:
            # portal_type can change (as it does after ATCT-migration), so we
            # need to check against the content_meta_type of each type-info
            ttool = getToolByName(self, 'portal_types')
            types = [ti.Metatype() for ti in ttool.listTypeInfo()]
            if portalTypes:
                values = [v for v in values if v['portal_type'] in types]
            else:
                values = [v for v in values if v['meta_type'] in types]

        return values

    security.declareProtected(permissions.View, 'getTypeSpec')

    def getTypeSpec(self, package, type):
        t = self.lookupType(package, type)
        module = t['klass'].__module__
        klass = t['name']
        return '%s.%s' % (module, klass)

    security.declareProtected(permissions.View, 'listTypes')

    def listTypes(self, package=None, type=None):
        """Just the class.
        """
        if type is None:
            return [t['klass'] for t in listTypes(package)]
        else:
            return [getType(type, package)['klass']]

    security.declareProtected(permissions.View, 'lookupType')

    def lookupType(self, package, type):
        types = self.listRegisteredTypes()
        for t in types:
            if t['package'] != package:
                continue
            if t['meta_type'] == type:
                # We have to return the schema wrapped into the acquisition of
                # something to allow access. Otherwise we will end up with:
                # Your user account is defined outside the context of the object
                # being accessed.
                t['schema'] = ImplicitAcquisitionWrapper(t['schema'], self)
                return t
        return None

    security.declareProtected(permissions.ManagePortal,
                              'manage_installType')

    def manage_installType(self, typeName, package=None,
                           uninstall=None, REQUEST=None):
        """Un/Install a type TTW.
        """
        typesTool = getToolByName(self, 'portal_types')
        try:
            typesTool._delObject(typeName)
        except (ConflictError, KeyboardInterrupt):
            raise
        except:  # XXX bare exception
            pass
        if uninstall is not None:
            if REQUEST:
                return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                                 '/manage_debugForm')
            return

        typeinfo_name = '%s: %s' % (package, typeName)

        # We want to run the process/modify_fti code which might not
        # have been called
        typeDesc = getType(typeName, package)
        process_types([typeDesc], package)
        klass = typeDesc['klass']

        # get the meta type of the FTI from the class, use the default FTI as
        # default
        fti_meta_type = getattr(klass, '_at_fti_meta_type', None)
        if fti_meta_type in (None, 'simple item'):
            fti_meta_type = FactoryTypeInformation.meta_type

        typesTool.manage_addTypeInformation(fti_meta_type,
                                            id=typeName,
                                            typeinfo_name=typeinfo_name)
        t = getattr(typesTool, typeName, None)
        if t:
            t.title = getattr(klass, 'archetype_name',
                              typeDesc['portal_type'])

        # and update the actions as needed
        fixActionsForType(klass, typesTool)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                             '/manage_debugForm')

    security.declarePublic('getSearchWidgets')

    def getSearchWidgets(self, package=None, type=None,
                         context=None, nosort=None):
        """Empty widgets for searching.
        """
        return self.getWidgets(package=package, type=type,
                               context=context, mode='search', nosort=nosort)

    security.declarePublic('getWidgets')

    def getWidgets(self, instance=None,
                   package=None, type=None,
                   context=None, mode='edit',
                   fields=None, schemata=None, nosort=None):
        """Empty widgets for standalone rendering.
        """
        widgets = []
        w_keys = {}
        context = context is not None and context or self
        instances = instance is not None and [instance] or []
        f_names = fields
        if not instances:
            for t in self.listTypes(package, type):
                instance = t('fake_instance')
                instance._at_is_fake_instance = True
                wrapped = instance.__of__(context)
                wrapped.initializeArchetype()
                instances.append(wrapped)
        for instance in instances:
            if schemata is not None:
                schema = instance.Schemata()[schemata].copy()
            else:
                schema = instance.Schema().copy()
            fields = schema.fields()
            if mode == 'search':
                # Include only fields which have an index
                # XXX duplicate fieldnames may break this,
                # as different widgets with the same name
                # on different schemas means only the first
                # one found will be used
                indexes = self.portal_catalog.indexes()
                fields = [f for f in fields
                          if (f.accessor and
                              not f.accessor in w_keys
                              and f.accessor in indexes)]
            if f_names is not None:
                fields = filter(lambda f: f.getName() in f_names, fields)
            for field in fields:
                widget = field.widget
                field_name = field.getName()
                accessor = field.getAccessor(instance)
                if mode == 'search':
                    field.required = False
                    field.addable = False  # for ReferenceField
                    if not isinstance(field.vocabulary, DisplayList):
                        field.vocabulary = field.Vocabulary(instance)
                    if '' not in field.vocabulary.keys():
                        field.vocabulary = DisplayList([('', _(u'at_search_any', default=u'<any>'))]) + \
                            field.vocabulary
                    widget.populate = False
                    field_name = field.accessor
                    # accessor must be a method which doesn't take an argument
                    # this lambda is facking an accessor
                    accessor = lambda: field.getDefault(instance)

                w_keys[field_name] = None
                widgets.append((field_name, WidgetWrapper(
                    field_name=field_name,
                    mode=mode,
                    widget=widget,
                    instance=instance,
                    field=field,
                    accessor=accessor)))
        if mode == 'search' and nosort == None:
            widgets.sort()
        return [widget for name, widget in widgets]

    security.declarePrivate('_rawEnum')

    def _rawEnum(self, callback, *args, **kwargs):
        """Finds all object to check if they are 'referenceable'.
        """
        catalog = getToolByName(self, 'portal_catalog')
        brains = catalog(dict(id=[]))
        for b in brains:
            o = b.getObject()
            if o is not None:
                if IBaseObject.providedBy(o):
                    callback(o, *args, **kwargs)
            else:
                log('no object for brain: %s:%s' % (b, b.getURL()))

    security.declareProtected(permissions.View, 'enum')

    def enum(self, callback, *args, **kwargs):
        catalog = getToolByName(self, UID_CATALOG)
        keys = catalog.uniqueValuesFor('UID')
        for uid in keys:
            o = self.getObject(uid)
            if o:
                callback(o, *args, **kwargs)
            else:
                log('No object for %s' % uid)

    security.declareProtected(permissions.View, 'Content')

    def Content(self):
        """Return a list of all the content ids.
        """
        catalog = getToolByName(self, UID_CATALOG)
        keys = catalog.uniqueValuesFor('UID')
        results = catalog(dict(UID=keys))
        return results

    # Management Forms
    security.declareProtected(permissions.ManagePortal,
                              'manage_doGenerate')

    def manage_doGenerate(self, sids=(), REQUEST=None):
        """(Re)generate types.
        """
        schemas = []
        for sid in sids:
            schemas.append(self.getSchema(sid))

        for s in schemas:
            s.generate()

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                             '/manage_workspace')

    security.declareProtected(permissions.ManagePortal,
                              'manage_inspect')

    def manage_inspect(self, UID, REQUEST=None):
        """Dump some things about an object hook in the debugger for now.
        """
        object = self.getObject(UID)
        log("uid: %s, schema: %s" % (object, object.Schema()))

        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_uids')

    security.declareProtected(permissions.ManagePortal,
                              'manage_reindex')

    def manage_reindex(self, REQUEST=None):
        """Assign UIDs to all basecontent objects.
        """

        def _index(object, archetype_tool):
            archetype_tool.registerContent(object)

        self._rawEnum(_index, self)

        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_uids')

    security.declareProtected(permissions.ManagePortal, 'index')
    index = manage_reindex

    def _listAllTypes(self):
        """List all types -- either currently known or known to us.
        """
        allTypes = _types.copy()
        allTypes.update(self._types)
        return allTypes.keys()

    security.declareProtected(permissions.ManagePortal,
                              'getChangedSchema')

    def getChangedSchema(self):
        """Returns a list of tuples indicating which schema have changed.

        Tuples have the form (schema, changed).
        """
        list = []
        currentTypes = _types
        ourTypes = self._types
        modified = False
        keys = self._listAllTypes()
        keys.sort()
        for t in keys:
            if t not in ourTypes:
                # Add it
                ourTypes[t] = currentTypes[t]['signature']
                modified = True
                list.append((t, 0))
            elif t not in currentTypes:
                # Huh: what shall we do? We remove it -- this might be wrong!
                del ourTypes[t]
                modified = True
                # We do not add an entry because we cannot update
                # these objects (having no longer type information for them)
            else:
                list.append((t, ourTypes[t] != currentTypes[t]['signature']))
        if modified:
            self._p_changed = True
        return list

    security.declareProtected(permissions.ManagePortal,
                              'manage_updateSchema')

    def manage_updateSchema(self, REQUEST=None, update_all=None,
                            remove_instance_schemas=None):
        """Make sure all objects' schema are up to date.
        """
        out = StringIO()
        print >> out, 'Updating schema...'

        update_types = []
        if REQUEST is None:
            # DM (avoid persistency bug): avoid code duplication
            update_types = [ti[0] for ti in self.getChangedSchema() if ti[1]]
        else:
            # DM (avoid persistency bug):
            for t in self._listAllTypes():
                if REQUEST.form.get(t, False):
                    update_types.append(t)
            update_all = REQUEST.form.get('update_all', False)
            remove_instance_schemas = REQUEST.form.get(
                'remove_instance_schemas', False)

        # XXX: Enter this block only when there are types to update!
        if update_types:
            # Use the catalog's ZopeFindAndApply method to walk through
            # all objects in the portal.  This works much better than
            # relying on the catalog to find objects, because an object
            # may be uncatalogable because of its schema, and then you
            # can't update it if you require that it be in the catalog.
            catalog = getToolByName(self, 'portal_catalog')
            portal = getToolByName(self, 'portal_url').getPortalObject()
            meta_types = [_types[t]['meta_type'] for t in update_types]
            if remove_instance_schemas:
                func_update_changed = self._removeSchemaAndUpdateChangedObject
                func_update_all = self._removeSchemaAndUpdateObject
            else:
                func_update_changed = self._updateChangedObject
                func_update_all = self._updateObject
            if update_all:
                catalog.ZopeFindAndApply(portal, obj_metatypes=meta_types,
                                         search_sub=True, apply_func=func_update_all)
            else:
                catalog.ZopeFindAndApply(portal, obj_metatypes=meta_types,
                                         search_sub=True, apply_func=func_update_changed)
            for t in update_types:
                self._types[t] = _types[t]['signature']
            self._p_changed = True

        print >> out, 'Done.'
        return out.getvalue()

    # A counter to ensure that in a given interval a subtransaction
    # commit is done.
    subtransactioncounter = 0

    def _updateObject(self, o, path, remove_instance_schemas=None):
        o._updateSchema(remove_instance_schemas=remove_instance_schemas)
        # Subtransactions to avoid eating up RAM when used inside a
        # 'ZopeFindAndApply' like in manage_updateSchema
        self.subtransactioncounter += 1
        # Only every 250 objects a sub-commit, otherwise it eats up all
        # diskspace
        if not self.subtransactioncounter % 250:
            transaction.savepoint(optimistic=True)

    def _updateChangedObject(self, o, path):
        if not o._isSchemaCurrent():
            self._updateObject(o, path)

    def _removeSchemaAndUpdateObject(self, o, path):
        self._updateObject(o, path, remove_instance_schemas=True)

    def _removeSchemaAndUpdateChangedObject(self, o, path):
        if not o._isSchemaCurrent():
            self._removeSchemaAndUpdateObject(o, path)

    security.declareProtected(permissions.ManagePortal,
                              'manage_updateSchema')

    def manage_migrate(self, REQUEST=None):
        """Run Extensions.migrations.migrate.
        """
        from Products.Archetypes.Extensions.migrations import migrate
        out = migrate(self)
        self.manage_updateSchema()
        return out

    # Catalog management
    security.declareProtected(permissions.View,
                              'listCatalogs')

    def listCatalogs(self):
        """Show the catalog mapping.
        """
        return self.catalog_map

    security.declareProtected(permissions.ManagePortal,
                              'manage_updateCatalogs')

    def manage_updateCatalogs(self, REQUEST=None):
        """Set the catalog map for meta_type to include the list
        catalog_names.
        """
        prefix = 'catalog_names_'
        for key in REQUEST.form.keys():
            if key.startswith(prefix):
                k = key[len(prefix):]
                v = REQUEST.form.get(key)
                self.setCatalogsByType(k, v)

        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_catalogs')

    security.declareProtected(permissions.ManagePortal,
                              'setCatalogsByType')

    def setCatalogsByType(self, portal_type, catalogList):
        """ associate catalogList with meta_type. (unfortunally not portal_type).

            catalogList is a list of strings with the ids of the catalogs.
            Each catalog is has to be a tool, means unique in site root.
        """
        self.catalog_map[portal_type] = catalogList

    security.declareProtected(permissions.View, 'getCatalogsByType')

    def getCatalogsByType(self, portal_type):
        """Return the catalog objects assoicated with a given type.
        """
        catalogs = []
        catalog_map = getattr(self, 'catalog_map', None)
        if catalog_map is not None:
            names = self.catalog_map.get(portal_type, ['portal_catalog'])
        else:
            names = ['portal_catalog']
        portal = getToolByName(self, 'portal_url').getPortalObject()
        for name in names:
            try:
                catalogs.append(getToolByName(portal, name))
            except (ConflictError, KeyboardInterrupt):
                raise
            except Exception, E:
                log('No tool %s' % name, E)
                pass
        return catalogs
class Resource(Persistent):
    security = ClassSecurityInfo()

    def __init__(self, id, **kwargs):
        self._data = PersistentMapping()
        extres = id.startswith('http://') or id.startswith(
            'https://') or id.startswith('//')
        if not extres and (id.startswith('/') or id.endswith('/') or
                           ('//' in id)):
            raise ValueError("Invalid Resource ID: %s" % id)
        self._data['id'] = id
        expression = kwargs.get('expression', '')
        self.setExpression(expression)
        self._data['authenticated'] = kwargs.get('authenticated', False)
        self._data['enabled'] = kwargs.get('enabled', True)
        self._data['cookable'] = kwargs.get('cookable', True)
        self._data['cacheable'] = kwargs.get('cacheable', True)
        self._data['conditionalcomment'] = kwargs.get('conditionalcomment', '')
        self._data['bundle'] = kwargs.get('bundle', 'default')
        self.isExternal = extres
        if extres:
            self._data[
                'cacheable'] = False  #External resources are NOT cacheable
            self._data[
                'cookable'] = False  #External resources are NOT mergable

    def copy(self):
        result = self.__class__(self.getId())
        for key, value in self._data.items():
            if key != 'id':
                result._data[key] = value
        return result

    security.declarePublic('getId')

    def getId(self):
        return self._data['id']

    security.declarePublic('getQuotedId')

    def getQuotedId(self):
        return quote_plus(self._data['id'])

    security.declareProtected(permissions.ManagePortal, '_setId')

    def _setId(self, id):
        if id.startswith('/') or id.endswith('/') or (
            ('//' in id) and not self.isExternalResource()):
            raise ValueError("Invalid Resource ID: %s" % id)
        self._data['id'] = id

    security.declarePublic('getCookedExpression')

    def getCookedExpression(self):
        # Automatic inline migration of expressions
        if 'cooked_expression' not in self._data:
            expr = Expression(self._data['expression'])
            self._data['cooked_expression'] = expr
        return self._data['cooked_expression']

    security.declarePublic('getExpression')

    def getExpression(self):
        return self._data['expression']

    security.declareProtected(permissions.ManagePortal, 'setExpression')

    def setExpression(self, expression):
        # Update the cooked expression
        self._data['cooked_expression'] = Expression(expression)
        self._data['expression'] = expression

    security.declarePublic('getAuthenticated')

    def getAuthenticated(self):
        # Automatic inline migration
        if 'authenticated' not in self._data:
            self._data['authenticated'] = False
        return bool(self._data['authenticated'])

    security.declareProtected(permissions.ManagePortal, 'setAuthenticated')

    def setAuthenticated(self, authenticated):
        self._data['authenticated'] = authenticated

    security.declarePublic('getEnabled')

    def getEnabled(self):
        return bool(self._data['enabled'])

    security.declareProtected(permissions.ManagePortal, 'setEnabled')

    def setEnabled(self, enabled):
        self._data['enabled'] = enabled

    security.declarePublic('getCookable')

    def getCookable(self):
        return self._data['cookable']

    security.declareProtected(permissions.ManagePortal, 'setCookable')

    def setCookable(self, cookable):
        if self.isExternalResource() and cookable:
            raise ValueError("External Resources cannot be merged")
        self._data['cookable'] = cookable

    security.declarePublic('getCacheable')

    def getCacheable(self):
        # as this is a new property, old instance might not have that value, so
        # return True as default
        return self._data.get('cacheable', True)

    security.declareProtected(permissions.ManagePortal, 'setCacheable')

    def setCacheable(self, cacheable):
        if self.isExternalResource() and cacheable:
            raise ValueError("External Resources are not cacheable")
        self._data['cacheable'] = cacheable

    security.declarePublic('getConditionalcomment')

    def getConditionalcomment(self):
        # New property, return blank if the old instance doesn't have that value
        return self._data.get('conditionalcomment', '')

    security.declareProtected(permissions.ManagePortal,
                              'setConditionalcomment')

    def setConditionalcomment(self, conditionalcomment):
        self._data['conditionalcomment'] = conditionalcomment

    security.declarePublic('isExternalResource')

    def isExternalResource(self):
        return getattr(self, 'isExternal', False)

    security.declarePublic('getBundle')

    def getBundle(self):
        return self._data.get('bundle', None) or 'default'

    security.declareProtected(permissions.ManagePortal, 'setBundle')

    def setBundle(self, bundle):
        self._data['bundle'] = bundle
Пример #7
0
class ElementSpec(SimpleItem):
    """ Represent all the tool knows about a single metadata element.
    """

    security = ClassSecurityInfo()

    #
    #   Default values.
    #
    is_multi_valued = 0

    def __init__(self, is_multi_valued):
        self.is_multi_valued = is_multi_valued
        self.policies = PersistentMapping()
        self.policies[None] = self._makePolicy()  # set default policy

    @security.private
    def _makePolicy(self):
        return MetadataElementPolicy(self.is_multi_valued)

    @security.protected(View)
    def isMultiValued(self):
        """
            Is this element multi-valued?
        """
        return self.is_multi_valued

    @security.protected(View)
    def getPolicy(self, typ=None):
        """ Find the policy for this element for objects of the given type.

        o Return a default, if none found.
        """
        try:
            return self.policies[typ].__of__(self)
        except KeyError:
            return self.policies[None].__of__(self)

    @security.protected(View)
    def listPolicies(self):
        """ Return a list of all policies for this element.
        """
        res = []
        for k, v in self.policies.items():
            res.append((k, v.__of__(self)))
        return res

    @security.protected(ManagePortal)
    def addPolicy(self, typ):
        """ Add a policy to this element for objects of the given type.
        """
        if typ is None:
            raise MetadataError("Can't replace default policy.")

        if typ in self.policies:
            raise MetadataError("Existing policy for content type:" + typ)

        self.policies[typ] = self._makePolicy()

    @security.protected(ManagePortal)
    def removePolicy(self, typ):
        """ Remove the policy from this element for objects of the given type.

        o Do *not* remvoe the default, however.
        """
        if typ is None:
            raise MetadataError("Can't remove default policy.")
        del self.policies[typ]
Пример #8
0
class PluginRegistry(SimpleItem):

    """ Implement IPluginRegistry as an independent, ZMI-manageable object.

    o Each plugin type holds an ordered list of (id, wrapper) tuples.
    """
    implements(IPluginRegistry, IWriteLock)

    security = ClassSecurityInfo()

    meta_type = 'Plugin Registry'

    _plugins = None

    def __init__(self, plugin_type_info=()):

        if isinstance(plugin_type_info, basestring):
            # some tool is passing us our ID.
            raise ValueError('Must pass a sequence of plugin info dicts!')

        self._plugin_types = [x[0] for x in plugin_type_info]
        self._plugin_type_info = PersistentMapping()
        for interface in plugin_type_info:
            self._plugin_type_info[interface[0]] = { 
                  'id': interface[1]
                , 'title': interface[2]
                , 'description': interface[3]
                }

    #
    #   IPluginRegistry implementation
    #
    security.declareProtected(ManageUsers, 'listPluginTypeInfo')
    def listPluginTypeInfo(self):

        """ See IPluginRegistry.
        """
        result = []

        for ptype in self._plugin_types:

            info = self._plugin_type_info[ptype].copy()
            info['interface'] = ptype
            info['methods'] = ptype.names()

            result.append(info)

        return result

    security.declareProtected(ManageUsers, 'listPlugins')
    def listPlugins(self, plugin_type):

        """ See IPluginRegistry.
        """
        result = []

        parent = aq_parent(aq_inner(self))

        for plugin_id in self._getPlugins(plugin_type):

            plugin = parent._getOb(plugin_id)
            if not _satisfies(plugin, plugin_type):
                logger.debug('Active plugin %s no longer implements %s'
                                % (plugin_id, plugin_type)
                           )
            else:
                result.append((plugin_id, plugin))

        return result

    security.declareProtected(ManageUsers, 'getPluginInfo')
    def getPluginInfo(self, plugin_type):

        """ See IPluginRegistry.
        """
        plugin_type = self._getInterfaceFromName(plugin_type)
        return self._plugin_type_info[plugin_type]

    security.declareProtected(ManageUsers, 'listPluginIds')
    def listPluginIds(self, plugin_type):

        """ See IPluginRegistry.
        """

        return self._getPlugins(plugin_type)

    security.declareProtected(ManageUsers, 'activatePlugin')
    def activatePlugin(self, plugin_type, plugin_id):

        """ See IPluginRegistry.
        """
        plugins = list(self._getPlugins(plugin_type))

        if plugin_id in plugins:
            raise KeyError, 'Duplicate plugin id: %s' % plugin_id

        parent = aq_parent(aq_inner(self))
        plugin = parent._getOb(plugin_id) 

        if not _satisfies(plugin, plugin_type):
            raise ValueError, 'Plugin does not implement %s' % plugin_type 
        
        plugins.append(plugin_id)
        self._plugins[plugin_type] = tuple(plugins)

    security.declareProtected(ManageUsers, 'deactivatePlugin')
    def deactivatePlugin(self, plugin_type, plugin_id):

        """ See IPluginRegistry.
        """
        plugins = list(self._getPlugins(plugin_type))

        if not plugin_id in plugins:
            raise KeyError, 'Invalid plugin id: %s' % plugin_id

        plugins = [x for x in plugins if x != plugin_id]
        self._plugins[plugin_type] = tuple(plugins)

    security.declareProtected(ManageUsers, 'movePluginsUp')
    def movePluginsUp(self, plugin_type, ids_to_move):

        """ See IPluginRegistry.
        """
        ids = list(self._getPlugins(plugin_type))
        count = len(ids)

        indexes = list(map(ids.index, ids_to_move))
        indexes.sort()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 - 1
            if i2 < 0:
                # i1 is already on top
                continue

            ids[i2], ids[i1] = ids[i1], ids[i2]

        self._plugins[plugin_type] = tuple(ids)

    security.declareProtected(ManageUsers, 'movePluginsDown')
    def movePluginsDown(self, plugin_type, ids_to_move):

        """ See IPluginRegistry.
        """
        ids = list(self._getPlugins(plugin_type))
        count = len(ids)

        indexes = list(map(ids.index, ids_to_move))
        indexes.sort()
        indexes.reverse()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 + 1
            if i2 == len(ids):
                # i1 is already on the bottom
                continue

            ids[i2], ids[i1] = ids[i1], ids[i2]

        self._plugins[plugin_type] = tuple(ids)

    #
    #   ZMI
    #
    arrow_right_gif = ImageFile('www/arrow-right.gif', globals())
    arrow_left_gif = ImageFile('www/arrow-left.gif', globals())
    arrow_up_gif = ImageFile('www/arrow-up.gif', globals())
    arrow_down_gif = ImageFile('www/arrow-down.gif', globals())

    security.declareProtected(ManageUsers, 'manage_activatePlugins')
    def manage_activatePlugins(self
                             , plugin_type
                             , plugin_ids
                             , RESPONSE
                            ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        for id in plugin_ids:
            self.activatePlugin(interface, id)
        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s'
                         % (self.absolute_url(), plugin_type)
                        )

    security.declareProtected(ManageUsers, 'manage_deactivatePlugins')
    def manage_deactivatePlugins(self
                                , plugin_type
                                , plugin_ids
                                , RESPONSE
                               ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        for id in plugin_ids:
            self.deactivatePlugin(interface, id)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s'
                         % (self.absolute_url(), plugin_type)
                        )

    security.declareProtected(ManageUsers, 'manage_movePluginsUp')
    def manage_movePluginsUp(self
                            , plugin_type
                            , plugin_ids
                            , RESPONSE
                           ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        self.movePluginsUp(interface, plugin_ids)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s'
                         % (self.absolute_url(), plugin_type)
                        )

    security.declareProtected(ManageUsers, 'manage_movePluginsDown')
    def manage_movePluginsDown(self
                              , plugin_type
                              , plugin_ids
                              , RESPONSE
                             ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        self.movePluginsDown(interface, plugin_ids)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s'
                         % (self.absolute_url(), plugin_type)
                        )

    security.declareProtected(ManageUsers, 'getAllPlugins')
    def getAllPlugins(self, plugin_type):

        """ Return a mapping segregating active / available plugins.

        'plugin_type' is the __name__ of the interface.
        """
        interface = self._getInterfaceFromName(plugin_type)

        active = self._getPlugins(interface)
        available = []

        for id, value in aq_parent(aq_inner(self)).objectItems():
            if _satisfies(value, interface):
                if id not in active:
                    available.append(id)

        return { 'active' : active, 'available' : available }


    security.declareProtected(ManageUsers, 'removePluginById')
    def removePluginById(self, plugin_id):

        """ Remove a plugin from any plugin types which have it configured.
        """
        for plugin_type in self._plugin_types:

            if plugin_id in self._getPlugins(plugin_type):
                self.deactivatePlugin(plugin_type, plugin_id)

    security.declareProtected(ManageUsers, 'manage_plugins')
    manage_plugins = PageTemplateFile('plugins', _wwwdir)
    security.declareProtected(ManageUsers, 'manage_active')
    manage_active = PageTemplateFile('active_plugins', _wwwdir)
    manage_twoLists = PageTemplateFile('two_lists', _wwwdir)

    manage_options=(({ 'label'  : 'Plugins'
                       , 'action' : 'manage_plugins'
                     # , 'help'   : ('PluggableAuthService'
                     #              , 'plugins.stx')
                       }
                     , { 'label'  : 'Active'
                       , 'action' : 'manage_active'
                       }
                    )
                   + SimpleItem.manage_options
                  )

    if _HAS_GENERIC_SETUP:
        security.declareProtected(ManageUsers, 'manage_exportImportForm')
        manage_exportImportForm = PageTemplateFile('export_import', _wwwdir)

        security.declareProtected(ManageUsers, 'getConfigAsXML')
        def getConfigAsXML(self):
            """ Return XML representing the registry's configuration.
            """
            from exportimport import PluginRegistryExporter
            pre = PluginRegistryExporter(self).__of__(self)
            return pre.generateXML()

        security.declareProtected(ManageUsers, 'manage_exportImport')
        def manage_exportImport(self, updated_xml, should_purge, RESPONSE):
            """ Parse XML and update the registry.
            """
            #XXX encoding?
            _updatePluginRegistry(self, updated_xml, should_purge)
            RESPONSE.redirect('%s/manage_exportImportForm'
                              '?manage_tabs_message=Registry+updated.'
                                % self.absolute_url())

        security.declareProtected(ManageUsers, 'manage_FTPget')
        def manage_FTPget(self, REQUEST, RESPONSE):
            """
            """
            return self.getConfigAsXML()

        security.declareProtected(ManageUsers, 'PUT')
        def PUT(self, REQUEST, RESPONSE):
            """
            """
            xml = REQUEST['BODYFILE'].read()
            _updatePluginRegistry(self, xml, True)

        manage_options = (manage_options[:2]
                         + ({ 'label' : 'Export / Import'
                             , 'action' : 'manage_exportImportForm'
                             },)
                         + manage_options[2:]
                        )

    #
    #   Helper methods
    #
    security.declarePrivate('_getPlugins')
    def _getPlugins(self, plugin_type):

        parent = aq_parent(aq_inner(self))

        if plugin_type not in self._plugin_types:
            raise KeyError, plugin_type

        if self._plugins is None:
            self._plugins = PersistentMapping()

        return self._plugins.setdefault(plugin_type, ())

    security.declarePrivate('_getInterfaceFromName')
    def _getInterfaceFromName(self, plugin_type_name):

        """ Convert the string name to an interface.

        o Raise KeyError is no such interface is known.
        """
        found = [x[0] for x in self._plugin_type_info.items()
                                if x[1]['id'] == plugin_type_name]
        if not found:
            raise KeyError, plugin_type_name

        if len(found) > 1:
            raise KeyError, 'Waaa!:  %s' % plugin_type_name

        return found[0]
Пример #9
0
class WorklistDefinition(SimpleItem):
    """Worklist definiton"""

    meta_type = 'Worklist'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    description = ''
    var_matches = None  # Compared with catalog when set.
    actbox_name = ''
    actbox_url = ''
    actbox_icon = ''
    actbox_category = 'global'
    guard = None

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

    def __init__(self, id):
        self.id = id

    def getGuard(self):
        if self.guard is not None:
            return self.guard
        else:
            return Guard().__of__(self)  # Create a temporary guard.

    def getGuardSummary(self):
        res = None
        if self.guard is not None:
            res = self.guard.getSummary()
        return res

    def getWorkflow(self):
        return aq_parent(aq_inner(aq_parent(aq_inner(self))))

    def getAvailableCatalogVars(self):
        res = []
        res.append(self.getWorkflow().state_var)
        for id, vdef in self.getWorkflow().variables.items():
            if vdef.for_catalog:
                res.append(id)
        res.sort()
        return res

    def getVarMatchKeys(self):
        if self.var_matches:
            return self.var_matches.keys()
        else:
            return []

    def getVarMatch(self, id):
        if self.var_matches:
            matches = self.var_matches.get(id, ())
            if not isinstance(matches, (tuple, Expression)):
                # Old version, convert it.
                matches = (matches,)
                self.var_matches[id] = matches
            return matches
        else:
            return ()

    def getVarMatchText(self, id):
        values = self.getVarMatch(id)
        if isinstance(values, Expression):
            return values.text
        return '; '.join(values)

    _properties_form = DTMLFile('worklist_properties', _dtmldir)

    def manage_properties(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._properties_form(REQUEST,
                                     management_view='Properties',
                                     manage_tabs_message=manage_tabs_message,
                                     )

    def setProperties(self, description,
                      actbox_name='', actbox_url='', actbox_category='global',
                      actbox_icon='', props=None, REQUEST=None):
        '''
        '''
        if props is None:
            props = REQUEST
        self.description = str(description)
        for key in self.getAvailableCatalogVars():
            # Populate var_matches.
            fieldname = 'var_match_%s' % key
            v = props.get(fieldname, '')
            if v:
                if not self.var_matches:
                    self.var_matches = PersistentMapping()

                if tales_re.match(v).group(1):
                    # Found a TALES prefix
                    self.var_matches[key] = Expression(v)
                else:
                    # Falling back to formatted string
                    v = [ var.strip() for var in v.split(';') ]
                    self.var_matches[key] = tuple(v)

            else:
                if self.var_matches and self.var_matches.has_key(key):
                    del self.var_matches[key]
        self.actbox_name = str(actbox_name)
        self.actbox_url = str(actbox_url)
        self.actbox_category = str(actbox_category)
        self.actbox_icon = str(actbox_icon)
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.guard = g
        else:
            self.guard = None
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')

    def search(self, info=None, **kw):
        """ Perform the search corresponding to this worklist

        Returns sequence of ZCatalog brains
        - info is a mapping for resolving formatted string variable references
        - additional keyword/value pairs may be used to restrict the query
        """
        if not self.var_matches:
            return

        if info is None:
            info = {}

        criteria = {}

        for key, values in self.var_matches.items():
            if isinstance(values, Expression):
                wf = self.getWorkflow()
                portal = wf._getPortalRoot()
                context = createExprContext(StateChangeInfo(portal, wf))
                criteria[key] = values(context)
            else:
                criteria[key] = [x % info for x in values]

        criteria.update(kw)

        ctool = getUtility(ICatalogTool)
        return ctool.searchResults(**criteria)
Пример #10
0
class ArchetypeTool(UniqueObject, ActionProviderBase, \
                    SQLStorageConfig, Folder):
    """Archetypes tool, manage aspects of Archetype instances.
    """
    id = TOOL_NAME
    meta_type = TOOL_NAME.title().replace('_', ' ')

    implements(IArchetypeTool)

    isPrincipiaFolderish = True  # Show up in the ZMI

    security = ClassSecurityInfo()

    meta_types = all_meta_types = ()

    manage_options = ((
        {
            'label': 'Types',
            'action': 'manage_debugForm',
        },
        {
            'label': 'Catalogs',
            'action': 'manage_catalogs',
        },
        {
            'label': 'Templates',
            'action': 'manage_templateForm',
        },
        {
            'label': 'UIDs',
            'action': 'manage_uids',
        },
        {
            'label': 'Update Schema',
            'action': 'manage_updateSchemaForm',
        },
        {
            'label': 'Migration',
            'action': 'manage_migrationForm',
        },
    ) + SQLStorageConfig.manage_options)

    security.declareProtected(permissions.ManagePortal, 'manage_uids')
    manage_uids = PageTemplateFile('viewContents', _www)
    security.declareProtected(permissions.ManagePortal, 'manage_templateForm')
    manage_templateForm = PageTemplateFile('manageTemplates', _www)
    security.declareProtected(permissions.ManagePortal, 'manage_debugForm')
    manage_debugForm = PageTemplateFile('generateDebug', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_updateSchemaForm')
    manage_updateSchemaForm = PageTemplateFile('updateSchemaForm', _www)
    security.declareProtected(permissions.ManagePortal, 'manage_migrationForm')
    manage_migrationForm = PageTemplateFile('migrationForm', _www)
    security.declareProtected(permissions.ManagePortal,
                              'manage_dumpSchemaForm')
    manage_dumpSchemaForm = PageTemplateFile('schema', _www)
    security.declareProtected(permissions.ManagePortal, 'manage_catalogs')
    manage_catalogs = PageTemplateFile('manage_catalogs', _www)

    def __init__(self):
        self._schemas = PersistentMapping()
        self._templates = PersistentMapping()
        self._registeredTemplates = PersistentMapping()
        # meta_type -> [names of CatalogTools]
        self.catalog_map = PersistentMapping()
        self.catalog_map['Reference'] = []  # References not in portal_catalog
        # DM (avoid persistency bug): "_types" now maps known schemas to signatures
        self._types = {}

    security.declareProtected(permissions.ManagePortal, 'manage_dumpSchema')

    def manage_dumpSchema(self, REQUEST=None):
        """XML Dump Schema of passed in class.
        """
        from Products.Archetypes.Schema import getSchemata
        package = REQUEST.get('package', '')
        type_name = REQUEST.get('type_name', '')
        spec = self.getTypeSpec(package, type_name)
        type = self.lookupType(package, type_name)
        options = {}
        options['classname'] = spec
        options['schematas'] = getSchemata(type['klass'])
        REQUEST.RESPONSE.setHeader('Content-Type', 'text/xml')
        return self.manage_dumpSchemaForm(**options)

    # Template Management
    # Views can be pretty generic by iterating the schema so we don't
    # register by type anymore, we just create per site selection
    # lists
    #
    # We keep two lists, all register templates and their
    # names/titles and the mapping of type to template bindings both
    # are persistent
    security.declareProtected(permissions.ManagePortal, 'registerTemplate')

    def registerTemplate(self, template, name=None):
        # Lookup the template by name
        if not name:
            obj = self.unrestrictedTraverse(template, None)
            try:
                name = obj.title_or_id()
            except:
                name = template

        self._registeredTemplates[template] = name

    security.declareProtected(permissions.View, 'lookupTemplates')

    def lookupTemplates(self, instance_or_portaltype=None):
        """Lookup templates by giving an instance or a portal_type.

        Returns a DisplayList.
        """
        results = []
        if not isinstance(instance_or_portaltype, basestring):
            portal_type = instance_or_portaltype.getTypeInfo().getId()
        else:
            portal_type = instance_or_portaltype
        try:
            templates = self._templates[portal_type]
        except KeyError:
            return DisplayList()
            # XXX Look this up in the types tool later
            # self._templates[instance] = ['base_view',]
            # templates = self._templates[instance]
        for t in templates:
            results.append((t, self._registeredTemplates[t]))

        return DisplayList(results).sortedByValue()

    security.declareProtected(permissions.View, 'listTemplates')

    def listTemplates(self):
        """Lists all the templates.
        """
        return DisplayList(self._registeredTemplates.items()).sortedByValue()

    security.declareProtected(permissions.ManagePortal, 'bindTemplate')

    def bindTemplate(self, portal_type, templateList):
        """Creates binding between a type and its associated views.
        """
        self._templates[portal_type] = templateList

    security.declareProtected(permissions.ManagePortal, 'manage_templates')

    def manage_templates(self, REQUEST=None):
        """Sets all the template/type mappings.
        """
        prefix = 'template_names_'
        for key in REQUEST.form.keys():
            if key.startswith(prefix):
                k = key[len(prefix):]
                v = REQUEST.form.get(key)
                self.bindTemplate(k, v)

        add = REQUEST.get('addTemplate')
        name = REQUEST.get('newTemplate')
        if add and name:
            self.registerTemplate(name)

        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_templateForm')

    security.declareProtected(permissions.View, 'typeImplementsInterfaces')

    def typeImplementsInterfaces(self, type, interfaces):
        """Checks if an type uses one of the given interfaces.
        """
        if isinstance(type, dict) and 'klass' in type:
            type = type['klass']
        for iface in interfaces:
            res = iface.implementedBy(type)
            if res:
                return True
        return False

    security.declareProtected(permissions.View, 'isTemplateEnabled')

    def isTemplateEnabled(self, type):
        """Checks if an type uses ITemplateMixin.
        """
        return self.typeImplementsInterfaces(type, [ITemplateMixin])

    security.declareProtected(permissions.View,
                              'listTemplateEnabledPortalTypes')

    def listTemplateEnabledPortalTypes(self):
        """Return a list of portal_types with ITemplateMixin
        """
        return self.listPortalTypesWithInterfaces([ITemplateMixin])

    security.declareProtected(permissions.View,
                              'listPortalTypesWithInterfaces')

    def listPortalTypesWithInterfaces(self, ifaces):
        """Returns a list of ftis of which the types implement one of
        the given interfaces.  Only returns AT types.

        Get a list of FTIs of types implementing IReferenceable:
        >>> tool = getToolByName(self.portal, TOOL_NAME)
        >>> meth = tool.listPortalTypesWithInterfaces
        >>> ftis = tool.listPortalTypesWithInterfaces([IReferenceable])

        Sort the type ids and print them:
        >>> type_ids = [fti.getId() for fti in ftis]
        >>> type_ids.sort()
        >>> type_ids
        ['ATBIFolder', 'ComplexType', ...]
        """
        pt = getToolByName(self, 'portal_types')
        value = []
        for data in listTypes():
            klass = data['klass']
            for iface in ifaces:
                if iface.implementedBy(klass):
                    ti = pt.getTypeInfo(data['portal_type'])
                    if ti is not None:
                        value.append(ti)
        return value

    # Type/Schema Management
    security.declareProtected(permissions.View, 'listRegisteredTypes')

    def listRegisteredTypes(self, inProject=False, portalTypes=False):
        """Return the list of sorted types.
        """
        def type_sort(a, b):
            v = cmp(a['package'], b['package'])
            if v != False:
                return v

            c = cmp(a['klass'].__class__.__name__,
                    b['klass'].__class__.__name__)

            if c == False:
                return cmp(a['package'], b['package'])
            return c

        values = listTypes()
        values.sort(type_sort)

        if inProject:
            # portal_type can change (as it does after ATCT-migration), so we
            # need to check against the content_meta_type of each type-info
            ttool = getToolByName(self, 'portal_types')
            types = [ti.Metatype() for ti in ttool.listTypeInfo()]
            if portalTypes:
                values = [v for v in values if v['portal_type'] in types]
            else:
                values = [v for v in values if v['meta_type'] in types]

        return values

    security.declareProtected(permissions.View, 'getTypeSpec')

    def getTypeSpec(self, package, type):
        t = self.lookupType(package, type)
        module = t['klass'].__module__
        klass = t['name']
        return '%s.%s' % (module, klass)

    security.declareProtected(permissions.View, 'listTypes')

    def listTypes(self, package=None, type=None):
        """Just the class.
        """
        if type is None:
            return [t['klass'] for t in listTypes(package)]
        else:
            return [getType(type, package)['klass']]

    security.declareProtected(permissions.View, 'lookupType')

    def lookupType(self, package, type):
        types = self.listRegisteredTypes()
        for t in types:
            if t['package'] != package:
                continue
            if t['meta_type'] == type:
                # We have to return the schema wrapped into the acquisition of
                # something to allow access. Otherwise we will end up with:
                # Your user account is defined outside the context of the object
                # being accessed.
                t['schema'] = ImplicitAcquisitionWrapper(t['schema'], self)
                return t
        return None

    security.declareProtected(permissions.ManagePortal, 'manage_installType')

    def manage_installType(self,
                           typeName,
                           package=None,
                           uninstall=None,
                           REQUEST=None):
        """Un/Install a type TTW.
        """
        typesTool = getToolByName(self, 'portal_types')
        try:
            typesTool._delObject(typeName)
        except (ConflictError, KeyboardInterrupt):
            raise
        except:  # XXX bare exception
            pass
        if uninstall is not None:
            if REQUEST:
                return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                                 '/manage_debugForm')
            return

        typeinfo_name = '%s: %s' % (package, typeName)

        # We want to run the process/modify_fti code which might not
        # have been called
        typeDesc = getType(typeName, package)
        process_types([typeDesc], package)
        klass = typeDesc['klass']

        # get the meta type of the FTI from the class, use the default FTI as default
        fti_meta_type = getattr(klass, '_at_fti_meta_type', None)
        if fti_meta_type in (None, 'simple item'):
            fti_meta_type = FactoryTypeInformation.meta_type

        typesTool.manage_addTypeInformation(fti_meta_type,
                                            id=typeName,
                                            typeinfo_name=typeinfo_name)
        t = getattr(typesTool, typeName, None)
        if t:
            t.title = getattr(klass, 'archetype_name', typeDesc['portal_type'])

        # and update the actions as needed
        fixActionsForType(klass, typesTool)

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                             '/manage_debugForm')

    security.declarePublic('getSearchWidgets')

    def getSearchWidgets(self,
                         package=None,
                         type=None,
                         context=None,
                         nosort=None):
        """Empty widgets for searching.
        """
        return self.getWidgets(package=package,
                               type=type,
                               context=context,
                               mode='search',
                               nosort=nosort)

    security.declarePublic('getWidgets')

    def getWidgets(self,
                   instance=None,
                   package=None,
                   type=None,
                   context=None,
                   mode='edit',
                   fields=None,
                   schemata=None,
                   nosort=None):
        """Empty widgets for standalone rendering.
        """
        widgets = []
        w_keys = {}
        context = context is not None and context or self
        instances = instance is not None and [instance] or []
        f_names = fields
        if not instances:
            for t in self.listTypes(package, type):
                instance = t('fake_instance')
                instance._at_is_fake_instance = True
                wrapped = instance.__of__(context)
                wrapped.initializeArchetype()
                instances.append(wrapped)
        for instance in instances:
            if schemata is not None:
                schema = instance.Schemata()[schemata].copy()
            else:
                schema = instance.Schema().copy()
            fields = schema.fields()
            if mode == 'search':
                # Include only fields which have an index
                # XXX duplicate fieldnames may break this,
                # as different widgets with the same name
                # on different schemas means only the first
                # one found will be used
                indexes = self.portal_catalog.indexes()
                fields = [
                    f for f in fields
                    if (f.accessor and not f.accessor in w_keys
                        and f.accessor in indexes)
                ]
            if f_names is not None:
                fields = filter(lambda f: f.getName() in f_names, fields)
            for field in fields:
                widget = field.widget
                field_name = field.getName()
                accessor = field.getAccessor(instance)
                if mode == 'search':
                    field.required = False
                    field.addable = False  # for ReferenceField
                    if not isinstance(field.vocabulary, DisplayList):
                        field.vocabulary = field.Vocabulary(instance)
                    if '' not in field.vocabulary.keys():
                        field.vocabulary = DisplayList([('', _(u'at_search_any', default=u'<any>'))]) + \
                                           field.vocabulary
                    widget.populate = False
                    field_name = field.accessor
                    # accessor must be a method which doesn't take an argument
                    # this lambda is facking an accessor
                    accessor = lambda: field.getDefault(instance)

                w_keys[field_name] = None
                widgets.append((field_name,
                                WidgetWrapper(field_name=field_name,
                                              mode=mode,
                                              widget=widget,
                                              instance=instance,
                                              field=field,
                                              accessor=accessor)))
        if mode == 'search' and nosort == None:
            widgets.sort()
        return [widget for name, widget in widgets]

    security.declarePrivate('_rawEnum')

    def _rawEnum(self, callback, *args, **kwargs):
        """Finds all object to check if they are 'referenceable'.
        """
        catalog = getToolByName(self, 'portal_catalog')
        brains = catalog(dict(id=[]))
        for b in brains:
            o = b.getObject()
            if o is not None:
                if IBaseObject.providedBy(o):
                    callback(o, *args, **kwargs)
            else:
                log('no object for brain: %s:%s' % (b, b.getURL()))

    security.declareProtected(permissions.View, 'enum')

    def enum(self, callback, *args, **kwargs):
        catalog = getToolByName(self, UID_CATALOG)
        keys = catalog.uniqueValuesFor('UID')
        for uid in keys:
            o = self.getObject(uid)
            if o:
                callback(o, *args, **kwargs)
            else:
                log('No object for %s' % uid)

    security.declareProtected(permissions.View, 'Content')

    def Content(self):
        """Return a list of all the content ids.
        """
        catalog = getToolByName(self, UID_CATALOG)
        keys = catalog.uniqueValuesFor('UID')
        results = catalog(dict(UID=keys))
        return results

    # Management Forms
    security.declareProtected(permissions.ManagePortal, 'manage_doGenerate')

    def manage_doGenerate(self, sids=(), REQUEST=None):
        """(Re)generate types.
        """
        schemas = []
        for sid in sids:
            schemas.append(self.getSchema(sid))

        for s in schemas:
            s.generate()

        if REQUEST:
            return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                             '/manage_workspace')

    security.declareProtected(permissions.ManagePortal, 'manage_inspect')

    def manage_inspect(self, UID, REQUEST=None):
        """Dump some things about an object hook in the debugger for now.
        """
        object = self.getObject(UID)
        log("uid: %s, schema: %s" % (object, object.Schema()))

        return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_uids')

    security.declareProtected(permissions.ManagePortal, 'manage_reindex')

    def manage_reindex(self, REQUEST=None):
        """Assign UIDs to all basecontent objects.
        """
        def _index(object, archetype_tool):
            archetype_tool.registerContent(object)

        self._rawEnum(_index, self)

        return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_uids')

    security.declareProtected(permissions.ManagePortal, 'index')
    index = manage_reindex

    def _listAllTypes(self):
        """List all types -- either currently known or known to us.
        """
        allTypes = _types.copy()
        allTypes.update(self._types)
        return allTypes.keys()

    security.declareProtected(permissions.ManagePortal, 'getChangedSchema')

    def getChangedSchema(self):
        """Returns a list of tuples indicating which schema have changed.

        Tuples have the form (schema, changed).
        """
        list = []
        currentTypes = _types
        ourTypes = self._types
        modified = False
        keys = self._listAllTypes()
        keys.sort()
        for t in keys:
            if t not in ourTypes:
                # Add it
                ourTypes[t] = currentTypes[t]['signature']
                modified = True
                list.append((t, 0))
            elif t not in currentTypes:
                # Huh: what shall we do? We remove it -- this might be wrong!
                del ourTypes[t]
                modified = True
                # We do not add an entry because we cannot update
                # these objects (having no longer type information for them)
            else:
                list.append((t, ourTypes[t] != currentTypes[t]['signature']))
        if modified:
            self._p_changed = True
        return list

    security.declareProtected(permissions.ManagePortal, 'manage_updateSchema')

    def manage_updateSchema(self,
                            REQUEST=None,
                            update_all=None,
                            remove_instance_schemas=None):
        """Make sure all objects' schema are up to date.
        """
        out = StringIO()
        print >> out, 'Updating schema...'

        update_types = []
        if REQUEST is None:
            # DM (avoid persistency bug): avoid code duplication
            update_types = [ti[0] for ti in self.getChangedSchema() if ti[1]]
        else:
            # DM (avoid persistency bug):
            for t in self._listAllTypes():
                if REQUEST.form.get(t, False):
                    update_types.append(t)
            update_all = REQUEST.form.get('update_all', False)
            remove_instance_schemas = REQUEST.form.get(
                'remove_instance_schemas', False)

        # XXX: Enter this block only when there are types to update!
        if update_types:
            # Use the catalog's ZopeFindAndApply method to walk through
            # all objects in the portal.  This works much better than
            # relying on the catalog to find objects, because an object
            # may be uncatalogable because of its schema, and then you
            # can't update it if you require that it be in the catalog.
            catalog = getToolByName(self, 'portal_catalog')
            portal = getToolByName(self, 'portal_url').getPortalObject()
            meta_types = [_types[t]['meta_type'] for t in update_types]
            if remove_instance_schemas:
                func_update_changed = self._removeSchemaAndUpdateChangedObject
                func_update_all = self._removeSchemaAndUpdateObject
            else:
                func_update_changed = self._updateChangedObject
                func_update_all = self._updateObject
            if update_all:
                catalog.ZopeFindAndApply(portal,
                                         obj_metatypes=meta_types,
                                         search_sub=True,
                                         apply_func=func_update_all)
            else:
                catalog.ZopeFindAndApply(portal,
                                         obj_metatypes=meta_types,
                                         search_sub=True,
                                         apply_func=func_update_changed)
            for t in update_types:
                self._types[t] = _types[t]['signature']
            self._p_changed = True

        print >> out, 'Done.'
        return out.getvalue()

    # A counter to ensure that in a given interval a subtransaction
    # commit is done.
    subtransactioncounter = 0

    def _updateObject(self, o, path, remove_instance_schemas=None):
        o._updateSchema(remove_instance_schemas=remove_instance_schemas)
        # Subtransactions to avoid eating up RAM when used inside a
        # 'ZopeFindAndApply' like in manage_updateSchema
        self.subtransactioncounter += 1
        # Only every 250 objects a sub-commit, otherwise it eats up all diskspace
        if not self.subtransactioncounter % 250:
            transaction.savepoint(optimistic=True)

    def _updateChangedObject(self, o, path):
        if not o._isSchemaCurrent():
            self._updateObject(o, path)

    def _removeSchemaAndUpdateObject(self, o, path):
        self._updateObject(o, path, remove_instance_schemas=True)

    def _removeSchemaAndUpdateChangedObject(self, o, path):
        if not o._isSchemaCurrent():
            self._removeSchemaAndUpdateObject(o, path)

    security.declareProtected(permissions.ManagePortal, 'manage_updateSchema')

    def manage_migrate(self, REQUEST=None):
        """Run Extensions.migrations.migrate.
        """
        from Products.Archetypes.Extensions.migrations import migrate
        out = migrate(self)
        self.manage_updateSchema()
        return out

    # Catalog management
    security.declareProtected(permissions.View, 'listCatalogs')

    def listCatalogs(self):
        """Show the catalog mapping.
        """
        return self.catalog_map

    security.declareProtected(permissions.ManagePortal,
                              'manage_updateCatalogs')

    def manage_updateCatalogs(self, REQUEST=None):
        """Set the catalog map for meta_type to include the list
        catalog_names.
        """
        prefix = 'catalog_names_'
        for key in REQUEST.form.keys():
            if key.startswith(prefix):
                k = key[len(prefix):]
                v = REQUEST.form.get(key)
                self.setCatalogsByType(k, v)

        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_catalogs')

    security.declareProtected(permissions.ManagePortal, 'setCatalogsByType')

    def setCatalogsByType(self, portal_type, catalogList):
        """ associate catalogList with meta_type. (unfortunally not portal_type).

            catalogList is a list of strings with the ids of the catalogs.
            Each catalog is has to be a tool, means unique in site root.
        """
        self.catalog_map[portal_type] = catalogList

    security.declareProtected(permissions.View, 'getCatalogsByType')

    def getCatalogsByType(self, portal_type):
        """Return the catalog objects assoicated with a given type.
        """
        catalogs = []
        catalog_map = getattr(self, 'catalog_map', None)
        if catalog_map is not None:
            names = self.catalog_map.get(portal_type, ['portal_catalog'])
        else:
            names = ['portal_catalog']
        portal = getToolByName(self, 'portal_url').getPortalObject()
        for name in names:
            try:
                catalogs.append(getToolByName(portal, name))
            except (ConflictError, KeyboardInterrupt):
                raise
            except Exception, E:
                log('No tool %s' % name, E)
                pass
        return catalogs
Пример #11
0
class MetadataSchema(SimpleItem):
    """ Describe a metadata schema.
    """

    security = ClassSecurityInfo()

    meta_type = 'Metadata Schema'
    publisher = ''

    def __init__(self, id, element_specs=()):
        self._setId(id)
        self.element_specs = PersistentMapping()
        for name, is_multi_valued in element_specs:
            self.element_specs[name] = ElementSpec(is_multi_valued)

    #
    #   ZMI methods
    #
    manage_options = (({
        'label': 'Elements',
        'action': 'elementPoliciesForm'
    }, ) + SimpleItem.manage_options)

    security.declareProtected(ManagePortal, 'elementPoliciesForm')
    elementPoliciesForm = DTMLFile('metadataElementPolicies', WWW_DIR)

    @security.protected(ManagePortal)
    def addElementPolicy(self,
                         element,
                         content_type,
                         is_required,
                         supply_default,
                         default_value,
                         enforce_vocabulary,
                         allowed_vocabulary,
                         REQUEST=None):
        # Add a type-specific policy for one of our elements.
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.addPolicy(content_type)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required, supply_default, default_value,
                    enforce_vocabulary, allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/elementPoliciesForm?element=' +
                                         element +
                                         '&manage_tabs_message=Policy+added.')

    @security.protected(ManagePortal)
    def removeElementPolicy(self, element, content_type, REQUEST=None):
        # Remove a type-specific policy for one of our elements.
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.removePolicy(content_type)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/elementPoliciesForm?element=' +
                element + '&manage_tabs_message=Policy+removed.')

    @security.protected(ManagePortal)
    def updateElementPolicy(self,
                            element,
                            content_type,
                            is_required,
                            supply_default,
                            default_value,
                            enforce_vocabulary,
                            allowed_vocabulary,
                            REQUEST=None):
        # Update a policy for one of our elements.
        # 'content_type' will be '<default>' when we edit the default.
        if content_type == '<default>':
            content_type = None
        spec = self.getElementSpec(element)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required, supply_default, default_value,
                    enforce_vocabulary, allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/elementPoliciesForm?element=' +
                element + '&manage_tabs_message=Policy+updated.')

    #
    #   Element spec manipulation.
    #
    @security.protected(ManagePortal)
    def listElementSpecs(self):
        # Return a list of ElementSpecs representing the elements we manage.
        res = []
        for k, v in self.element_specs.items():
            res.append((k, v.__of__(self)))
        return res

    @security.protected(ManagePortal)
    def getElementSpec(self, element):
        # Return an ElementSpec for the given 'element'.
        return self.element_specs[element].__of__(self)

    @security.protected(ManagePortal)
    def addElementSpec(self, element, is_multi_valued, REQUEST=None):
        # Add 'element' to our list of managed elements.
        # Don't replace.
        if element in self.element_specs:
            return

        self.element_specs[element] = ElementSpec(is_multi_valued)

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() +
                '/propertiesForm?manage_tabs_message=Element+' + element +
                '+added.')

    @security.protected(ManagePortal)
    def removeElementSpec(self, element, REQUEST=None):
        # Remove 'element' from our list of managed elements.
        del self.element_specs[element]

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() +
                '/propertiesForm?manage_tabs_message=Element+' + element +
                '+removed.')

    @security.protected(ManagePortal)
    def listPolicies(self, typ=None):
        # Show all policies for a given content type.
        # If 'typ' is none, return the list of default policies.
        result = []
        for element, spec in self.listElementSpecs():
            result.append((element, spec.getPolicy(typ)))
        return result
Пример #12
0
class PluginRegistry(SimpleItem):
    """ Implement IPluginRegistry as an independent, ZMI-manageable object.

    o Each plugin type holds an ordered list of ( id, wrapper ) tuples.
    """
    if _HAS_Z3_INTERFACES:
        if _HAS_Z3_DAV_INTERFACES:
            implements(IPluginRegistry, IWriteLock)
        else:
            implements(IPluginRegistry)
            __implements__ = (WriteLockInterface, )
    else:
        __implements__ = (
            IPluginRegistry,
            WriteLockInterface,
        )

    security = ClassSecurityInfo()

    meta_type = 'Plugin Registry'

    _plugins = None

    def __init__(self, plugin_type_info):

        self._plugin_types = [x[0] for x in plugin_type_info]
        self._plugin_type_info = PersistentMapping()
        for interface in plugin_type_info:
            self._plugin_type_info[interface[0]] = {
                'id': interface[1],
                'title': interface[2],
                'description': interface[3]
            }

    #
    #   IPluginRegistry implementation
    #
    security.declareProtected(ManageUsers, 'listPluginTypeInfo')

    def listPluginTypeInfo(self):
        """ See IPluginRegistry.
        """
        result = []

        for ptype in self._plugin_types:

            info = self._plugin_type_info[ptype].copy()
            info['interface'] = ptype
            info['methods'] = ptype.names()

            result.append(info)

        return result

    security.declareProtected(ManageUsers, 'listPlugins')

    def listPlugins(self, plugin_type):
        """ See IPluginRegistry.
        """
        result = []

        parent = aq_parent(aq_inner(self))

        for plugin_id in self._getPlugins(plugin_type):

            plugin = parent._getOb(plugin_id)
            result.append((plugin_id, plugin))

        return result

    security.declareProtected(ManageUsers, 'getPluginInfo')

    def getPluginInfo(self, plugin_type):
        """ See IPluginRegistry.
        """
        plugin_type = self._getInterfaceFromName(plugin_type)
        return self._plugin_type_info[plugin_type]

    security.declareProtected(ManageUsers, 'listPluginIds')

    def listPluginIds(self, plugin_type):
        """ See IPluginRegistry.
        """

        return self._getPlugins(plugin_type)

    security.declareProtected(ManageUsers, 'activatePlugin')

    def activatePlugin(self, plugin_type, plugin_id):
        """ See IPluginRegistry.
        """
        plugins = list(self._getPlugins(plugin_type))

        if plugin_id in plugins:
            raise KeyError, 'Duplicate plugin id: %s' % plugin_id

        parent = aq_parent(aq_inner(self))
        plugin = parent._getOb(plugin_id)

        satisfies = getattr(plugin_type, 'providedBy', None)
        if satisfies is None:
            satisfies = plugin_type.isImplementedBy

        if not satisfies(plugin):
            raise ValueError, 'Plugin does not implement %s' % plugin_type

        plugins.append(plugin_id)
        self._plugins[plugin_type] = tuple(plugins)

    security.declareProtected(ManageUsers, 'deactivatePlugin')

    def deactivatePlugin(self, plugin_type, plugin_id):
        """ See IPluginRegistry.
        """
        plugins = list(self._getPlugins(plugin_type))

        if not plugin_id in plugins:
            raise KeyError, 'Invalid plugin id: %s' % plugin_id

        plugins = [x for x in plugins if x != plugin_id]
        self._plugins[plugin_type] = tuple(plugins)

    security.declareProtected(ManageUsers, 'movePluginsUp')

    def movePluginsUp(self, plugin_type, ids_to_move):
        """ See IPluginRegistry.
        """
        ids = list(self._getPlugins(plugin_type))
        count = len(ids)

        indexes = list(map(ids.index, ids_to_move))
        indexes.sort()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 - 1
            if i2 < 0:  # wrap to bottom
                i2 = len(ids) - 1

            ids[i2], ids[i1] = ids[i1], ids[i2]

        self._plugins[plugin_type] = tuple(ids)

    security.declareProtected(ManageUsers, 'movePluginsDown')

    def movePluginsDown(self, plugin_type, ids_to_move):
        """ See IPluginRegistry.
        """
        ids = list(self._getPlugins(plugin_type))
        count = len(ids)

        indexes = list(map(ids.index, ids_to_move))
        indexes.sort()
        indexes.reverse()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 + 1
            if i2 == len(ids):  # wrap to top
                i2 = 0

            ids[i2], ids[i1] = ids[i1], ids[i2]

        self._plugins[plugin_type] = tuple(ids)

    #
    #   ZMI
    #
    arrow_right_gif = ImageFile('www/arrow-right.gif', globals())
    arrow_left_gif = ImageFile('www/arrow-left.gif', globals())
    arrow_up_gif = ImageFile('www/arrow-up.gif', globals())
    arrow_down_gif = ImageFile('www/arrow-down.gif', globals())

    security.declareProtected(ManageUsers, 'manage_activatePlugins')

    def manage_activatePlugins(self, plugin_type, plugin_ids, RESPONSE):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        for id in plugin_ids:
            self.activatePlugin(interface, id)
        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s' %
                          (self.absolute_url(), plugin_type))

    security.declareProtected(ManageUsers, 'manage_deactivatePlugins')

    def manage_deactivatePlugins(self, plugin_type, plugin_ids, RESPONSE):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        for id in plugin_ids:
            self.deactivatePlugin(interface, id)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s' %
                          (self.absolute_url(), plugin_type))

    security.declareProtected(ManageUsers, 'manage_movePluginsUp')

    def manage_movePluginsUp(self, plugin_type, plugin_ids, RESPONSE):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        self.movePluginsUp(interface, plugin_ids)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s' %
                          (self.absolute_url(), plugin_type))

    security.declareProtected(ManageUsers, 'manage_movePluginsDown')

    def manage_movePluginsDown(self, plugin_type, plugin_ids, RESPONSE):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName(plugin_type)
        self.movePluginsDown(interface, plugin_ids)

        RESPONSE.redirect('%s/manage_plugins?plugin_type=%s' %
                          (self.absolute_url(), plugin_type))

    security.declareProtected(ManageUsers, 'getAllPlugins')

    def getAllPlugins(self, plugin_type):
        """ Return a mapping segregating active / available plugins.

        'plugin_type' is the __name__ of the interface.
        """
        interface = self._getInterfaceFromName(plugin_type)

        active = self._getPlugins(interface)
        available = []

        satisfies = getattr(interface, 'providedBy', None)
        if satisfies is None:
            satisfies = interface.isImplementedBy

        for id, value in aq_parent(aq_inner(self)).objectItems():
            if satisfies(value):
                if id not in active:
                    available.append(id)

        return {'active': active, 'available': available}

    security.declareProtected(ManageUsers, 'removePluginById')

    def removePluginById(self, plugin_id):
        """ Remove a plugin from any plugin types which have it configured.
        """
        for plugin_type in self._plugin_types:

            if plugin_id in self._getPlugins(plugin_type):
                self.deactivatePlugin(plugin_type, plugin_id)

    security.declareProtected(ManageUsers, 'manage_plugins')
    manage_plugins = PageTemplateFile('plugins', _wwwdir)
    security.declareProtected(ManageUsers, 'manage_active')
    manage_active = PageTemplateFile('active_plugins', _wwwdir)
    manage_twoLists = PageTemplateFile('two_lists', _wwwdir)

    manage_options = ((
        {
            'label': 'Plugins',
            'action': 'manage_plugins'
            # , 'help'   : ( 'PluggableAuthService'
            #              , 'plugins.stx')
        },
        {
            'label': 'Active',
            'action': 'manage_active'
        }) + SimpleItem.manage_options)

    if _HAS_GENERIC_SETUP:
        security.declareProtected(ManageUsers, 'manage_exportImportForm')
        manage_exportImportForm = PageTemplateFile('export_import', _wwwdir)

        security.declareProtected(ManageUsers, 'getConfigAsXML')

        def getConfigAsXML(self):
            """ Return XML representing the registry's configuration.
            """
            from exportimport import PluginRegistryExporter
            pre = PluginRegistryExporter(self).__of__(self)
            return pre.generateXML()

        security.declareProtected(ManageUsers, 'manage_exportImport')

        def manage_exportImport(self, updated_xml, should_purge, RESPONSE):
            """ Parse XML and update the registry.
            """
            #XXX encoding?
            _updatePluginRegistry(self, updated_xml, should_purge)
            RESPONSE.redirect('%s/manage_exportImportForm'
                              '?manage_tabs_message=Registry+updated.' %
                              self.absolute_url())

        security.declareProtected(ManageUsers, 'manage_FTPget')

        def manage_FTPget(self, REQUEST, RESPONSE):
            """
            """
            return self.getConfigAsXML()

        security.declareProtected(ManageUsers, 'PUT')

        def PUT(self, REQUEST, RESPONSE):
            """
            """
            xml = REQUEST['BODYFILE'].read()
            _updatePluginRegistry(self, xml, True)

        manage_options = (manage_options[:2] +
                          ({
                              'label': 'Export / Import',
                              'action': 'manage_exportImportForm'
                          }, ) + manage_options[2:])

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

    def _getPlugins(self, plugin_type):

        parent = aq_parent(aq_inner(self))

        if plugin_type not in self._plugin_types:
            raise KeyError, plugin_type

        if self._plugins is None:
            self._plugins = PersistentMapping()

        return self._plugins.setdefault(plugin_type, ())

    security.declarePrivate('_getInterfaceFromName')

    def _getInterfaceFromName(self, plugin_type_name):
        """ Convert the string name to an interface.

        o Raise KeyError is no such interface is known.
        """
        found = [
            x[0] for x in self._plugin_type_info.items()
            if x[1]['id'] == plugin_type_name
        ]
        if not found:
            raise KeyError, plugin_type_name

        if len(found) > 1:
            raise KeyError, 'Waaa!:  %s' % plugin_type_name

        return found[0]
Пример #13
0
class PluginRegistry( SimpleItem ):

    """ Implement IPluginRegistry as an independent, ZMI-manageable object.

    o Each plugin type holds an ordered list of ( id, wrapper ) tuples.
    """
    __implements__ = ( IPluginRegistry, )

    security = ClassSecurityInfo()

    meta_type = 'Plugin Registry'

    _plugins = None

    def __init__( self, plugin_type_info ):

        self._plugin_types = [x[0] for x in plugin_type_info]
        self._plugin_type_info = PersistentMapping()
        for interface in plugin_type_info:
            self._plugin_type_info[interface[0]] = { 
                  'id': interface[1]
                , 'title': interface[2]
                , 'description': interface[3]
                }

    #
    #   IPluginRegistry implementation
    #
    security.declareProtected( ManageUsers, 'listPluginTypeInfo' )
    def listPluginTypeInfo( self ):

        """ See IPluginRegistry.
        """
        result = []

        for ptype in self._plugin_types:

            info = self._plugin_type_info[ptype].copy()
            info['interface'] = ptype
            info['methods'] = ptype.names()

            result.append( info )

        return result

    security.declareProtected( ManageUsers, 'listPlugins' )
    def listPlugins( self, plugin_type ):

        """ See IPluginRegistry.
        """
        result = []

        parent = aq_parent( aq_inner( self ) )

        for plugin_id in self._getPlugins( plugin_type ):

            plugin = parent._getOb( plugin_id )
            result.append( ( plugin_id, plugin ) )

        return result

    security.declareProtected( ManageUsers, 'getPluginInfo' )
    def getPluginInfo( self, plugin_type ):

        """ See IPluginRegistry.
        """
        plugin_type = self._getInterfaceFromName( plugin_type )
        return self._plugin_type_info[plugin_type]

    security.declareProtected( ManageUsers, 'listPluginInfo' )
    def listPluginIds( self, plugin_type ):

        """ See IPluginRegistry.
        """

        return self._getPlugins( plugin_type )

    security.declareProtected( ManageUsers, 'activatePlugin' )
    def activatePlugin( self, plugin_type, plugin_id ):

        """ See IPluginRegistry.
        """
        plugins = list( self._getPlugins( plugin_type ) )

        if plugin_id in plugins:
            raise KeyError, 'Duplicate plugin id: %s' % plugin_id

        parent = aq_parent( aq_inner( self ) )
        plugin = parent._getOb( plugin_id ) 

        if not plugin_type.isImplementedBy(plugin):
            raise ValueError, 'Plugin does not implement %s' % plugin_type 
        
        plugins.append( plugin_id )
        self._plugins[ plugin_type ] = tuple( plugins )

    security.declareProtected( ManageUsers, 'deactivatePlugin' )
    def deactivatePlugin( self, plugin_type, plugin_id ):

        """ See IPluginRegistry.
        """
        plugins = list( self._getPlugins( plugin_type ) )

        if not plugin_id in plugins:
            raise KeyError, 'Invalid plugin id: %s' % plugin_id

        plugins = [ x for x in plugins if x != plugin_id ]
        self._plugins[ plugin_type ] = tuple( plugins )

    security.declareProtected( ManageUsers, 'movePluginsUp' )
    def movePluginsUp( self, plugin_type, ids_to_move ):

        """ See IPluginRegistry.
        """
        ids = list( self._getPlugins( plugin_type ) )
        count = len( ids )

        indexes = list( map( ids.index, ids_to_move ) )
        indexes.sort()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 - 1
            if i2 < 0:      # wrap to bottom
                i2 = len( ids ) - 1

            ids[ i2 ], ids[ i1 ] = ids[ i1 ], ids[ i2 ]

        self._plugins[ plugin_type ] = tuple( ids )

    security.declareProtected( ManageUsers, 'movePluginsDown' )
    def movePluginsDown( self, plugin_type, ids_to_move ):

        """ See IPluginRegistry.
        """
        ids = list( self._getPlugins( plugin_type ) )
        count = len( ids )

        indexes = list( map( ids.index, ids_to_move ) )
        indexes.sort()
        indexes.reverse()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 + 1
            if i2 == len( ids ):      # wrap to top
                i2 = 0

            ids[ i2 ], ids[ i1 ] = ids[ i1 ], ids[ i2 ]

        self._plugins[ plugin_type ] = tuple( ids )

    #
    #   ZMI
    #
    arrow_right_gif = ImageFile( 'www/arrow-right.gif', globals() )
    arrow_left_gif = ImageFile( 'www/arrow-left.gif', globals() )
    arrow_up_gif = ImageFile( 'www/arrow-up.gif', globals() )
    arrow_down_gif = ImageFile( 'www/arrow-down.gif', globals() )

    security.declareProtected( ManageUsers, 'manage_activatePlugins' )
    def manage_activatePlugins( self
                             , plugin_type
                             , plugin_ids
                             , RESPONSE
                             ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        for id in plugin_ids:
            self.activatePlugin( interface, id )
        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_deactivatePlugins' )
    def manage_deactivatePlugins( self
                                , plugin_type
                                , plugin_ids
                                , RESPONSE
                                ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        for id in plugin_ids:
            self.deactivatePlugin( interface, id )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_movePluginsUp' )
    def manage_movePluginsUp( self
                            , plugin_type
                            , plugin_ids
                            , RESPONSE
                            ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        self.movePluginsUp( interface, plugin_ids )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_movePluginsDown' )
    def manage_movePluginsDown( self
                              , plugin_type
                              , plugin_ids
                              , RESPONSE
                              ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        self.movePluginsDown( interface, plugin_ids )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'getAllPlugins' )
    def getAllPlugins( self, plugin_type ):

        """ Return a mapping segregating active / available plugins.

        'plugin_type' is the __name__ of the interface.
        """
        interface = self._getInterfaceFromName( plugin_type )

        active = self._getPlugins( interface )
        available = []

        for id, value in aq_parent( aq_inner( self ) ).objectItems():
            if interface.isImplementedBy( value ):
                if id not in active:
                    available.append( id )

        return { 'active' : active, 'available' : available }


    security.declareProtected( ManageUsers, 'removePluginById' )
    def removePluginById( self, plugin_id ):

        """ Remove a plugin from any plugin types which have it configured.
        """
        for plugin_type in self._plugin_types:

            if plugin_id in self._getPlugins( plugin_type ):
                self.deactivatePlugin( plugin_type, plugin_id )

    security.declareProtected( ManageUsers, 'manage_plugins' )
    manage_plugins = PageTemplateFile( 'plugins', _wwwdir )
    manage_twoLists = PageTemplateFile( 'two_lists', _wwwdir )

    manage_options=( ( { 'label'        : 'Plugins'
                       , 'action'       : 'manage_plugins'
                     # , 'help'         : ( 'PluggableAuthService'
                     #                    , 'plugins.stx')
                       }
                     ,
                     )
                   + SimpleItem.manage_options
                   )

    #
    #   Helper methods
    #
    security.declarePrivate( '_getPlugins' )
    def _getPlugins( self, plugin_type ):

        parent = aq_parent( aq_inner( self ) )

        if plugin_type not in self._plugin_types:
            raise KeyError, plugin_type

        if self._plugins is None:
            self._plugins = PersistentMapping()

        return self._plugins.setdefault( plugin_type, () )

    security.declarePrivate( '_getInterfaceFromName' )
    def _getInterfaceFromName( self, plugin_type_name ):

        """ Convert the string name to an interface.

        o Raise KeyError is no such interface is known.
        """
        found = [ x[0] for x in self._plugin_type_info.items()
                                if x[1][ 'id' ] == plugin_type_name ]
        if not found:
            raise KeyError, plugin_type_name

        if len( found ) > 1:
            raise KeyError, 'Waaa!:  %s' % plugin_type_name

        return found[ 0 ]
Пример #14
0
class MetadataSchema(SimpleItem):

    """ Describe a metadata schema.
    """

    security = ClassSecurityInfo()

    meta_type = 'Metadata Schema'
    publisher = ''

    def __init__(self, id, element_specs=()):
        self._setId(id)
        self.element_specs = PersistentMapping()
        for name, is_multi_valued in element_specs:
            self.element_specs[name] = ElementSpec(is_multi_valued)

    #
    #   ZMI methods
    #
    manage_options = (
        ({'label': 'Elements', 'action': 'elementPoliciesForm'},) +
        SimpleItem.manage_options)

    security.declareProtected(ManagePortal, 'elementPoliciesForm')
    elementPoliciesForm = DTMLFile('metadataElementPolicies', _dtmldir)

    security.declareProtected(ManagePortal, 'addElementPolicy')
    def addElementPolicy(self,
                         element,
                         content_type,
                         is_required,
                         supply_default,
                         default_value,
                         enforce_vocabulary,
                         allowed_vocabulary,
                         REQUEST=None):
        """ Add a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.addPolicy(content_type)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required,
                    supply_default,
                    default_value,
                    enforce_vocabulary,
                    allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()
                + '/elementPoliciesForm'
                + '?element=' + element
                + '&manage_tabs_message=Policy+added.')

    security.declareProtected(ManagePortal, 'removeElementPolicy')
    def removeElementPolicy(self, element, content_type, REQUEST=None):
        """ Remvoe a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.removePolicy(content_type)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()
                + '/elementPoliciesForm'
                + '?element=' + element
                + '&manage_tabs_message=Policy+removed.')

    security.declareProtected(ManagePortal, 'updateElementPolicy')
    def updateElementPolicy(self,
                            element,
                            content_type,
                            is_required,
                            supply_default,
                            default_value,
                            enforce_vocabulary,
                            allowed_vocabulary,
                            REQUEST=None):
        """ Update a policy for one of our elements

        o 'content_type' will be '<default>' when we edit the default.
        """
        if content_type == '<default>':
            content_type = None
        spec = self.getElementSpec(element)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required,
                    supply_default,
                    default_value,
                    enforce_vocabulary,
                    allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()
                + '/elementPoliciesForm'
                + '?element=' + element
                + '&manage_tabs_message=Policy+updated.')

    #
    #   Element spec manipulation.
    #
    security.declareProtected(ManagePortal, 'listElementSpecs')
    def listElementSpecs(self):
        """ Return a list of ElementSpecs representing the elements we manage.
        """
        res = []
        for k, v in self.element_specs.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal, 'getElementSpec')
    def getElementSpec(self, element):
        """ Return an ElementSpec for the given 'element'.
        """
        return self.element_specs[element].__of__(self)

    security.declareProtected(ManagePortal, 'addElementSpec')
    def addElementSpec(self, element, is_multi_valued, REQUEST=None):
        """ Add 'element' to our list of managed elements.
        """
        # Don't replace.
        if element in self.element_specs:
            return

        self.element_specs[element] = ElementSpec(is_multi_valued)

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()
                + '/propertiesForm'
                + '?manage_tabs_message=Element+' + element + '+added.')

    security.declareProtected(ManagePortal, 'removeElementSpec')
    def removeElementSpec(self, element, REQUEST=None):
        """ Remove 'element' from our list of managed elements.
        """
        del self.element_specs[element]

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()
                + '/propertiesForm'
                + '?manage_tabs_message=Element+' + element + '+removed.')

    security.declareProtected(ManagePortal, 'listPolicies')
    def listPolicies(self, typ=None):
        """ Show all policies for a given content type

        o If 'typ' is none, return the list of default policies.
        """
        result = []
        for element, spec in self.listElementSpecs():
            result.append((element, spec.getPolicy(typ)))
        return result
Пример #15
0
class PluginRegistry( SimpleItem ):

    """ Implement IPluginRegistry as an independent, ZMI-manageable object.

    o Each plugin type holds an ordered list of ( id, wrapper ) tuples.
    """
    __implements__ = ( IPluginRegistry, )

    security = ClassSecurityInfo()

    meta_type = 'Plugin Registry'

    _plugins = None

    def __init__( self, plugin_type_info ):

        self._plugin_types = [x[0] for x in plugin_type_info]
        self._plugin_type_info = PersistentMapping()
        for interface in plugin_type_info:
            self._plugin_type_info[interface[0]] = { 
                  'id': interface[1]
                , 'title': interface[2]
                , 'description': interface[3]
                }

    #
    #   IPluginRegistry implementation
    #
    security.declareProtected( ManageUsers, 'listPluginTypeInfo' )
    def listPluginTypeInfo( self ):

        """ See IPluginRegistry.
        """
        result = []

        for ptype in self._plugin_types:

            info = self._plugin_type_info[ptype].copy()
            info['interface'] = ptype
            info['methods'] = ptype.names()

            result.append( info )

        return result

    security.declareProtected( ManageUsers, 'listPlugins' )
    def listPlugins( self, plugin_type ):

        """ See IPluginRegistry.
        """
        result = []

        parent = aq_parent( aq_inner( self ) )

        for plugin_id in self._getPlugins( plugin_type ):

            plugin = parent._getOb( plugin_id )
            result.append( ( plugin_id, plugin ) )

        return result

    security.declareProtected( ManageUsers, 'getPluginInfo' )
    def getPluginInfo( self, plugin_type ):

        """ See IPluginRegistry.
        """
        plugin_type = self._getInterfaceFromName( plugin_type )
        return self._plugin_type_info[plugin_type]

    security.declareProtected( ManageUsers, 'listPluginInfo' )
    def listPluginIds( self, plugin_type ):

        """ See IPluginRegistry.
        """

        return self._getPlugins( plugin_type )

    security.declareProtected( ManageUsers, 'activatePlugin' )
    def activatePlugin( self, plugin_type, plugin_id ):

        """ See IPluginRegistry.
        """
        plugins = list( self._getPlugins( plugin_type ) )

        if plugin_id in plugins:
            raise KeyError, 'Duplicate plugin id: %s' % plugin_id

        parent = aq_parent( aq_inner( self ) )
        plugin = parent._getOb( plugin_id ) 

        if not plugin_type.isImplementedBy(plugin):
            raise ValueError, 'Plugin does not implement %s' % plugin_type 
        
        plugins.append( plugin_id )
        self._plugins[ plugin_type ] = tuple( plugins )

    security.declareProtected( ManageUsers, 'deactivatePlugin' )
    def deactivatePlugin( self, plugin_type, plugin_id ):

        """ See IPluginRegistry.
        """
        plugins = list( self._getPlugins( plugin_type ) )

        if not plugin_id in plugins:
            raise KeyError, 'Invalid plugin id: %s' % plugin_id

        plugins = [ x for x in plugins if x != plugin_id ]
        self._plugins[ plugin_type ] = tuple( plugins )

    security.declareProtected( ManageUsers, 'movePluginsUp' )
    def movePluginsUp( self, plugin_type, ids_to_move ):

        """ See IPluginRegistry.
        """
        ids = list( self._getPlugins( plugin_type ) )
        count = len( ids )

        indexes = list( map( ids.index, ids_to_move ) )
        indexes.sort()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 - 1
            if i2 < 0:      # wrap to bottom
                i2 = len( ids ) - 1

            ids[ i2 ], ids[ i1 ] = ids[ i1 ], ids[ i2 ]

        self._plugins[ plugin_type ] = tuple( ids )

    security.declareProtected( ManageUsers, 'movePluginsDown' )
    def movePluginsDown( self, plugin_type, ids_to_move ):

        """ See IPluginRegistry.
        """
        ids = list( self._getPlugins( plugin_type ) )
        count = len( ids )

        indexes = list( map( ids.index, ids_to_move ) )
        indexes.sort()
        indexes.reverse()

        for i1 in indexes:

            if i1 < 0 or i1 >= count:
                raise IndexError, i1

            i2 = i1 + 1
            if i2 == len( ids ):      # wrap to top
                i2 = 0

            ids[ i2 ], ids[ i1 ] = ids[ i1 ], ids[ i2 ]

        self._plugins[ plugin_type ] = tuple( ids )

    #
    #   ZMI
    #
    arrow_right_gif = ImageFile( 'www/arrow-right.gif', globals() )
    arrow_left_gif = ImageFile( 'www/arrow-left.gif', globals() )
    arrow_up_gif = ImageFile( 'www/arrow-up.gif', globals() )
    arrow_down_gif = ImageFile( 'www/arrow-down.gif', globals() )

    security.declareProtected( ManageUsers, 'manage_activatePlugins' )
    def manage_activatePlugins( self
                             , plugin_type
                             , plugin_ids
                             , RESPONSE
                             ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        for id in plugin_ids:
            self.activatePlugin( interface, id )
        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_deactivatePlugins' )
    def manage_deactivatePlugins( self
                                , plugin_type
                                , plugin_ids
                                , RESPONSE
                                ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        for id in plugin_ids:
            self.deactivatePlugin( interface, id )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_movePluginsUp' )
    def manage_movePluginsUp( self
                            , plugin_type
                            , plugin_ids
                            , RESPONSE
                            ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        self.movePluginsUp( interface, plugin_ids )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'manage_movePluginsDown' )
    def manage_movePluginsDown( self
                              , plugin_type
                              , plugin_ids
                              , RESPONSE
                              ):
        """ Shim into ZMI.
        """
        interface = self._getInterfaceFromName( plugin_type )
        self.movePluginsDown( interface, plugin_ids )

        RESPONSE.redirect( '%s/manage_plugins?plugin_type=%s'
                         % ( self.absolute_url(), plugin_type )
                         )

    security.declareProtected( ManageUsers, 'getAllPlugins' )
    def getAllPlugins( self, plugin_type ):

        """ Return a mapping segregating active / available plugins.

        'plugin_type' is the __name__ of the interface.
        """
        interface = self._getInterfaceFromName( plugin_type )

        active = self._getPlugins( interface )
        available = []

        for id, value in aq_parent( aq_inner( self ) ).objectItems():
            if interface.isImplementedBy( value ):
                if id not in active:
                    available.append( id )

        return { 'active' : active, 'available' : available }


    security.declareProtected( ManageUsers, 'removePluginById' )
    def removePluginById( self, plugin_id ):

        """ Remove a plugin from any plugin types which have it configured.
        """
        for plugin_type in self._plugin_types:

            if plugin_id in self._getPlugins( plugin_type ):
                self.deactivatePlugin( plugin_type, plugin_id )

    security.declareProtected( ManageUsers, 'manage_plugins' )
    manage_plugins = PageTemplateFile( 'plugins', _wwwdir )
    manage_twoLists = PageTemplateFile( 'two_lists', _wwwdir )

    manage_options=( ( { 'label'        : 'Plugins'
                       , 'action'       : 'manage_plugins'
                     # , 'help'         : ( 'PluggableAuthService'
                     #                    , 'plugins.stx')
                       }
                     ,
                     )
                   + SimpleItem.manage_options
                   )

    #
    #   Helper methods
    #
    security.declarePrivate( '_getPlugins' )
    def _getPlugins( self, plugin_type ):

        parent = aq_parent( aq_inner( self ) )

        if plugin_type not in self._plugin_types:
            raise KeyError, plugin_type

        if self._plugins is None:
            self._plugins = PersistentMapping()

        return self._plugins.setdefault( plugin_type, () )

    security.declarePrivate( '_getInterfaceFromName' )
    def _getInterfaceFromName( self, plugin_type_name ):

        """ Convert the string name to an interface.

        o Raise KeyError is no such interface is known.
        """
        found = [ x[0] for x in self._plugin_type_info.items()
                                if x[1][ 'id' ] == plugin_type_name ]
        if not found:
            raise KeyError, plugin_type_name

        if len( found ) > 1:
            raise KeyError, 'Waaa!:  %s' % plugin_type_name

        return found[ 0 ]
class DiscussionItemContainer(Persistent, Implicit, Traversable):

    """
        Store DiscussionItem objects. Discussable content that
        has DiscussionItems associated with it will have an
        instance of DiscussionItemContainer injected into it to
        hold the discussion threads.
    """

    implements(IDiscussable, ICallableOpaqueItemEvents)

    # for the security machinery to allow traversal
    #__roles__ = None

    security = ClassSecurityInfo()

    def __init__(self):
        self.id = 'talkback'
        self._container = PersistentMapping()

    security.declareProtected(View, 'getId')
    def getId(self):
        return self.id

    security.declareProtected(View, 'getReply')
    def getReply(self, reply_id):
        """
            Return a discussion item, given its ID;  raise KeyError
            if not found.
        """
        return self._container.get(reply_id).__of__(self)

    # Is this right?
    security.declareProtected(View, '__bobo_traverse__')
    def __bobo_traverse__(self, REQUEST, name):
        """
        This will make this container traversable
        """
        target = getattr(self, name, None)
        if target is not None:
            return target

        else:
            try:
                return self.getReply(name)
            except:
                parent = aq_parent(aq_inner(self))
                if parent.getId() == name:
                    return parent
                else:
                    REQUEST.RESPONSE.notFoundError("%s\n%s" % (name, ''))

    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        """
            We have juste been added or moved.
            Add the contained items to the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__(self).indexObject()

    security.declarePrivate('manage_afterClone')
    def manage_afterClone(self, item):
        """
            We have just been cloned.
            Notify the workflow about the contained items.
        """
        for obj in self.objectValues():
            obj.__of__(self).notifyWorkflowCreated()

    security.declarePrivate('manage_beforeDelete')
    def manage_beforeDelete(self, item, container):
        """
            Remove the contained items from the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__(self).unindexObject()

    #
    #   OFS.ObjectManager query interface.
    #
    security.declareProtected(AccessContentsInformation, 'objectIds')
    def objectIds(self, spec=None):
        """
            Return a list of the ids of our DiscussionItems.
        """
        if spec and spec is not DiscussionItem.meta_type:
            return []
        return self._container.keys()

    security.declareProtected(AccessContentsInformation, 'objectItems')
    def objectItems(self, spec=None):
        """
            Return a list of (id, subobject) tuples for our DiscussionItems.
        """
        r = []
        a = r.append
        g = self._container.get
        for id in self.objectIds(spec):
            a((id, g(id)))
        return r

    security.declareProtected(AccessContentsInformation, 'objectValues')
    def objectValues(self):
        """
            Return a list of our DiscussionItems.
        """
        return self._container.values()

    #
    #   IDiscussable interface
    #
    security.declareProtected(ReplyToItem, 'createReply')
    def createReply(self, title, text, Creator=None,
                    text_format='structured-text'):
        """
            Create a reply in the proper place
        """
        id = int(DateTime().timeTime())
        while self._container.get(str(id), None) is not None:
            id = id + 1
        id = str(id)

        item = DiscussionItem(id, title=title, description=title)
        self._container[id] = item
        item = item.__of__(self)

        item.setFormat(text_format)
        item._edit(text)
        item.addCreator(Creator)
        item.setReplyTo(self._getDiscussable())

        item.indexObject()
        item.notifyWorkflowCreated()

        return id

    security.declareProtected(ManagePortal, 'deleteReply')
    def deleteReply(self, reply_id):
        """ Remove a reply from this container """
        if reply_id in self._container:
            reply = self._container.get(reply_id).__of__(self)
            my_replies = reply.talkback.getReplies()
            for my_reply in my_replies:
                my_reply_id = my_reply.getId()
                self.deleteReply(my_reply_id)

            if hasattr(reply, 'unindexObject'):
                reply.unindexObject()

            del self._container[reply_id]

    security.declareProtected(View, 'hasReplies')
    def hasReplies(self, content_obj):
        """
            Test to see if there are any dicussion items
        """
        outer = self._getDiscussable(outer=1)
        if content_obj == outer:
            return bool(len(self._container))
        else:
            return bool(len(content_obj.talkback._getReplyResults()))

    security.declareProtected(View, 'replyCount')
    def replyCount(self, content_obj):
        """ How many replies do i have? """
        outer = self._getDiscussable(outer=1)
        if content_obj == outer:
            return len(self._container)
        else:
            replies = content_obj.talkback.getReplies()
            return self._repcount(replies)

    security.declarePrivate('_repcount')
    def _repcount(self, replies):
        """counts the total number of replies

        by recursing thru the various levels
        """
        count = 0

        for reply in replies:
            count = count + 1

            #if there is at least one reply to this reply
            replies = reply.talkback.getReplies()
            if replies:
                count = count + self._repcount(replies)

        return count

    security.declareProtected(View, 'getReplies')
    def getReplies(self):
        """ Return a sequence of the IDiscussionResponse objects which are
            associated with this Discussable
        """
        objects = []
        a = objects.append
        result_ids = self._getReplyResults()

        for id in result_ids:
            a(self._container.get(id).__of__(self))

        return objects

    security.declareProtected(View, 'quotedContents')
    def quotedContents(self):
        """
            Return this object's contents in a form suitable for inclusion
            as a quote in a response.
        """

        return ""

    #
    #   Utility methods
    #
    security.declarePrivate('_getReplyParent')
    def _getReplyParent(self, in_reply_to):
        """
            Return the object indicated by the 'in_reply_to', where
            'None' represents the "outer" content object.
        """
        outer = self._getDiscussable(outer=1)
        if not in_reply_to:
            return outer
        parent = self._container[in_reply_to].__of__(aq_inner(self))
        return parent.__of__(outer)

    security.declarePrivate('_getDiscussable')
    def _getDiscussable(self, outer=0):
        """
        """
        tb = outer and aq_inner(self) or self
        return getattr(tb, 'aq_parent', None)

    security.declarePrivate('_getReplyResults')
    def _getReplyResults(self):
        """
           Get a list of ids of DiscussionItems which are replies to
           our Discussable.
        """
        discussable = self._getDiscussable()
        outer = self._getDiscussable(outer=1)

        if discussable == outer:
            in_reply_to = ''
        else:
            in_reply_to = discussable.getId()

        result = []
        a = result.append
        for key, value in self._container.items():
            if value.in_reply_to == in_reply_to:
                a((key, value))

        result.sort(lambda a, b: cmp(a[1].creation_date, b[1].creation_date))

        return [ x[0] for x in result ]
class NyMessageCatalog(Persistent):
    """Stores messages and their translations"""

    implements(INyTranslationCatalog)


    def __init__(self, id, title, languages=('en', )):

        self.id = id
        self.title = title

        # Language Manager data
        self._languages = tuple(languages)

        # We suppose all zope/portal products are written in English
        # therefore we consider all new messages in English
        # Default language in Catalog is always 'en', thus it can be different
        # from the default language of the portal
        self._default_language = 'en' # self._languages[0]

        # Here the message translations are stored
        self._messages = PersistentMapping()
        self._po_headers = PersistentMapping()

    ### INyTranslationCatalog

    def edit_message(self, msgid, lang, translation):
        # language existance test **not present in Localizer**:
        if lang not in self.get_languages():
            return
        # Add-by-edit functionality **not present in Localizer**:
        if not self._message_exists(msgid):
            self.gettext(msgid, lang)
        self._messages[msgid][lang] = translation

    def del_message(self, msgid):
        """ """
        if self._messages.has_key(msgid):
            del self._messages[msgid]

    def gettext(self, msgid, lang=None, default=None):
        """Returns the corresponding translation of msgid in Catalog.
        """
        msgstr = None
        if not isinstance(msgid, basestring):
            raise TypeError('Only strings can be translated.')
        # saving everything unicode, utf-8
        elif isinstance(msgid, str):
            msgstr = msgid
            msgid = force_to_unicode(msgid)
        if not lang:
            raise ValueError("No language provided for gettext")
        msgid = msgid.strip()
        # empty message is translated as empty message, regardless of lang
        if not msgid:
            return msgid
        # default `default translation` is the msgid itself
        if default is None:
            default = msgid

        if lang not in self.get_languages():
            # we don't have that lang, thus we can't translate and won't add msg
            return default

        # Add it if it's not in the dictionary
        if not self._message_exists(msgid):
            if msgstr is not None:
                import logging
                logger = logging.getLogger(__name__)
                logger.warn('Got str "%s" in gettext, expecting unicode'
                            % msgstr)
            self._messages[msgid] = PersistentMapping()
            update_transaction_note()

        if not self._messages[msgid].has_key(self._default_language):
            self._messages[msgid][self._default_language] = default

        # translation may be blank (supposition), then-> default (usually msgid)
        in_catalog = self._messages[msgid].get(lang, '')
        return in_catalog or default

    def get_languages(self):
        """Get available languages"""
        return self._languages

    def add_language(self, lang):
        """Add language"""
        if lang not in self._languages:
            self._languages = self._languages + (lang, )

    def del_language(self, lang):
        """Delete language with corresponding messages"""
        if lang not in self.get_languages():
            return
        langlist = list(self._languages)
        langlist.pop(langlist.index(lang))
        self._languages = tuple(langlist)

    def clear(self):
        """Erase all messages"""
        self._messages.clear()

    def messages(self):
        """
        Returns a generator used for catalog entries iteration.
        """
        for (msgid, translations_dict) in self._messages.items():
            yield (msgid, translations_dict)


    def _message_exists(self, message):
        return self._messages.has_key(message)

    ### Dictionary-like API

    def __getitem__(self, key):
        return self._messages[key]

    def __delitem__(self, key):
        del self._messages[key]
class Resource(Persistent):
    security = ClassSecurityInfo()

    def __init__(self, id, **kwargs):
        self._data = PersistentMapping()
        extres = id.startswith("http://") or id.startswith("https://")
        if id.startswith("/") or id.endswith("/") or ("//" in id and not extres):
            raise ValueError("Invalid Resource ID: %s" % id)
        self._data["id"] = id
        expression = kwargs.get("expression", "")
        self.setExpression(expression)
        self._data["authenticated"] = kwargs.get("authenticated", False)
        self._data["enabled"] = kwargs.get("enabled", True)
        self._data["cookable"] = kwargs.get("cookable", True)
        self._data["cacheable"] = kwargs.get("cacheable", True)
        self._data["conditionalcomment"] = kwargs.get("conditionalcomment", "")
        self._data["bundle"] = kwargs.get("bundle", "default")
        self.isExternal = extres
        if extres:
            self._data["cacheable"] = False  # External resources are NOT cacheable
            self._data["cookable"] = False  # External resources are NOT mergable

    def copy(self):
        result = self.__class__(self.getId())
        for key, value in self._data.items():
            if key != "id":
                result._data[key] = value
        return result

    security.declarePublic("getId")

    def getId(self):
        return self._data["id"]

    security.declarePublic("getQuotedId")

    def getQuotedId(self):
        return quote_plus(self._data["id"])

    security.declareProtected(permissions.ManagePortal, "_setId")

    def _setId(self, id):
        if id.startswith("/") or id.endswith("/") or (("//" in id) and not self.isExternalResource()):
            raise ValueError("Invalid Resource ID: %s" % id)
        self._data["id"] = id

    security.declarePublic("getCookedExpression")

    def getCookedExpression(self):
        # Automatic inline migration of expressions
        if "cooked_expression" not in self._data:
            expr = Expression(self._data["expression"])
            self._data["cooked_expression"] = expr
        return self._data["cooked_expression"]

    security.declarePublic("getExpression")

    def getExpression(self):
        return self._data["expression"]

    security.declareProtected(permissions.ManagePortal, "setExpression")

    def setExpression(self, expression):
        # Update the cooked expression
        self._data["cooked_expression"] = Expression(expression)
        self._data["expression"] = expression

    security.declarePublic("getAuthenticated")

    def getAuthenticated(self):
        # Automatic inline migration
        if "authenticated" not in self._data:
            self._data["authenticated"] = False
        return bool(self._data["authenticated"])

    security.declareProtected(permissions.ManagePortal, "setAuthenticated")

    def setAuthenticated(self, authenticated):
        self._data["authenticated"] = authenticated

    security.declarePublic("getEnabled")

    def getEnabled(self):
        return bool(self._data["enabled"])

    security.declareProtected(permissions.ManagePortal, "setEnabled")

    def setEnabled(self, enabled):
        self._data["enabled"] = enabled

    security.declarePublic("getCookable")

    def getCookable(self):
        return self._data["cookable"]

    security.declareProtected(permissions.ManagePortal, "setCookable")

    def setCookable(self, cookable):
        if self.isExternalResource() and cookable:
            raise ValueError("External Resources cannot be merged")
        self._data["cookable"] = cookable

    security.declarePublic("getCacheable")

    def getCacheable(self):
        # as this is a new property, old instance might not have that value, so
        # return True as default
        return self._data.get("cacheable", True)

    security.declareProtected(permissions.ManagePortal, "setCacheable")

    def setCacheable(self, cacheable):
        if self.isExternalResource() and cacheable:
            raise ValueError("External Resources are not cacheable")
        self._data["cacheable"] = cacheable

    security.declarePublic("getConditionalcomment")

    def getConditionalcomment(self):
        # New property, return blank if the old instance doesn't have that value
        return self._data.get("conditionalcomment", "")

    security.declareProtected(permissions.ManagePortal, "setConditionalcomment")

    def setConditionalcomment(self, conditionalcomment):
        self._data["conditionalcomment"] = conditionalcomment

    security.declarePublic("isExternalResource")

    def isExternalResource(self):
        return getattr(self, "isExternal", False)

    security.declarePublic("getBundle")

    def getBundle(self):
        return self._data.get("bundle", None) or "default"

    security.declareProtected(permissions.ManagePortal, "setBundle")

    def setBundle(self, bundle):
        self._data["bundle"] = bundle
Пример #19
0
class TransformTool(UniqueObject, ActionProviderBase, Folder):

    id = 'portal_transforms'
    meta_type = id.title().replace('_', ' ')
    isPrincipiaFolderish = 1  # Show up in the ZMI

    meta_types = all_meta_types = (
        {'name': 'Transform', 'action': 'manage_addTransformForm'},
        {'name': 'TransformsChain', 'action': 'manage_addTransformsChainForm'},
    )

    manage_addTransformForm = PageTemplateFile('addTransform', _www)
    manage_addTransformsChainForm = PageTemplateFile(
        'addTransformsChain', _www)
    manage_cacheForm = PageTemplateFile('setCacheTime', _www)
    manage_editTransformationPolicyForm = PageTemplateFile(
        'editTransformationPolicy', _www)
    manage_reloadAllTransforms = PageTemplateFile('reloadAllTransforms', _www)

    manage_options = (
        (Folder.manage_options[0], ) + Folder.manage_options[2:] +
        ({'label': 'Caches', 'action': 'manage_cacheForm'},
         {'label': 'Policy', 'action': 'manage_editTransformationPolicyForm'},
         {'label': 'Reload transforms',
          'action': 'manage_reloadAllTransforms'},
         ))

    security = ClassSecurityInfo()

    def __init__(self, policies=None, max_sec_in_cache=3600):
        self._mtmap = PersistentMapping()
        self._policies = policies or PersistentMapping()
        self.max_sec_in_cache = max_sec_in_cache
        self._new_style_pt = 1

    # mimetype oriented conversions (iengine interface)

    @security.private
    def unregisterTransform(self, name):
        """ unregister a transform
        name is the name of a registered transform
        """
        self._unmapTransform(getattr(self, name))
        if name in self.objectIds():
            self._delObject(name)

    @security.public
    def convertTo(self, target_mimetype, orig, data=None, object=None,
                  usedby=None, context=None, **kwargs):
        """Convert orig to a given mimetype

        * orig is an encoded string

        * data an optional IDataStream object. If None a new datastream will be
        created and returned

        * optional object argument is the object on which is bound the data.
        If present that object will be used by the engine to bound cached data.

        * additional arguments (kwargs) will be passed to the transformations.
        Some usual arguments are : filename, mimetype, encoding

        return an object implementing IDataStream or None if no path has been
        found.
        """
        target_mimetype = str(target_mimetype)

        if object is not None:
            cache = Cache(object, context=context)
            data = cache.getCache(target_mimetype)
            if data is not None:
                time, data = data
                if self.max_sec_in_cache == 0 or time < self.max_sec_in_cache:
                    return data

        if data is None:
            data = self._wrap(target_mimetype)

        registry = getToolByName(self, 'mimetypes_registry')

        if not getattr(aq_base(registry), 'classify', None):
            # avoid problems when importing a site with an old mimetype
            # registry
            return None

        orig_mt = registry.classify(orig,
                                    mimetype=kwargs.get('mimetype'),
                                    filename=kwargs.get('filename'))
        orig_mt = str(orig_mt)
        if not orig_mt:
            log('Unable to guess input mime type (filename=%s, mimetype=%s)' %
                (kwargs.get('mimetype'), kwargs.get('filename')),
                severity=DEBUG)
            return None

        target_mt = registry.lookup(target_mimetype)
        if target_mt:
            target_mt = target_mt[0]
        else:
            log('Unable to match target mime type %s' % str(target_mimetype),
                severity=DEBUG)
            return None

        # fastpath
        # If orig_mt and target_mt are the same, we only allow
        # a one-hop transform, a.k.a. filter.
        # XXX disabled filtering for now
        if orig_mt == str(target_mt):
            data.setData(orig)
            md = data.getMetadata()
            md['mimetype'] = str(orig_mt)
            if object is not None:
                cache.setCache(str(target_mimetype), data)
            return data

        # get a path to output mime type
        requirements = self._policies.get(str(target_mt), [])
        path = self._findPath(orig_mt, target_mt, list(requirements))
        if not path and requirements:
            log('Unable to satisfy requirements %s' % ', '.join(requirements),
                severity=DEBUG)
            path = self._findPath(orig_mt, target_mt)

        if not path:
            log('NO PATH FROM %s TO %s : %s' %
                (orig_mt, target_mimetype, path), severity=DEBUG)
            return None

        if len(path) > 1:
            # create a chain on the fly (sly)
            transform = chain()
            for t in path:
                transform.registerTransform(t)
        else:
            transform = path[0]

        result = transform.convert(orig, data, context=context,
                                   usedby=usedby, **kwargs)
        self._setMetaData(result, transform)

        # set cache if possible
        if object is not None and result.isCacheable():
            cache.setCache(str(target_mimetype), result)

        # return IDataStream object
        return result
    # make sure it's not publishable (XSS risk)
    del convertTo.__doc__

    @security.public
    def convertToData(self, target_mimetype, orig, data=None, object=None,
                      usedby=None, context=None, **kwargs):
        # Convert to a given mimetype and return the raw data
        # ignoring subobjects. see convertTo for more information
        data = self.convertTo(target_mimetype, orig, data, object, usedby,
                              context, **kwargs)
        if data:
            return data.getData()
        return None

    @security.public
    def convert(self, name, orig, data=None, context=None, **kwargs):
        # run a tranform of a given name on data

        # * name is the name of a registered transform

        # see convertTo docstring for more info

        if not data:
            data = self._wrap(name)
        try:
            transform = getattr(self, name)
        except AttributeError:
            raise Exception('No such transform "%s"' % name)
        data = transform.convert(orig, data, context=context, **kwargs)
        self._setMetaData(data, transform)
        return data

    def __call__(self, name, orig, data=None, context=None, **kwargs):
        # run a transform by its name, returning the raw data product

        # * name is the name of a registered transform.

        # return an encoded string.
        # see convert docstring for more info on additional arguments.

        data = self.convert(name, orig, data, context, **kwargs)
        return data.getData()

    # utilities ###############################################################

    def _setMetaData(self, datastream, transform):
        """set metadata on datastream according to the given transform
        (mime type and optionaly encoding)
        """
        md = datastream.getMetadata()
        if hasattr(transform, 'output_encoding'):
            md['encoding'] = transform.output_encoding
        md['mimetype'] = transform.output

    def _wrap(self, name):
        """wrap a data object in an icache"""
        return datastream(name)

    def _unwrap(self, data):
        """unwrap data from an icache"""
        if IDataStream.providedBy(data):
            data = data.getData()
        return data

    def _mapTransform(self, transform):
        """map transform to internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        inputs = getattr(transform, 'inputs', None)
        if not inputs:
            raise TransformException('Bad transform %s : no input MIME type' %
                                     (transform))
        for i in inputs:
            mts = registry.lookup(i)
            if not mts:
                msg = 'Input MIME type %r for transform %s is not registered '\
                      'in the MIME types registry' % (i, transform.name())
                raise TransformException(msg)
            for mti in mts:
                for mt in mti.mimetypes:
                    mt_in = self._mtmap.setdefault(mt, PersistentMapping())
                    output = getattr(transform, 'output', None)
                    if not output:
                        msg = 'Bad transform %s : no output MIME type'
                        raise TransformException(msg % transform.name())
                    mto = registry.lookup(output)
                    if not mto:
                        msg = 'Output MIME type %r for transform %s is not '\
                              'registered in the MIME types registry' % \
                              (output, transform.name())
                        raise TransformException(msg)
                    if len(mto) > 1:
                        msg = ("Wildcarding not allowed in transform's output "
                               "MIME type")
                        raise TransformException(msg)

                    for mt2 in mto[0].mimetypes:
                        try:
                            if transform not in mt_in[mt2]:
                                mt_in[mt2].append(transform)
                        except KeyError:
                            mt_in[mt2] = PersistentList([transform])

    def _unmapTransform(self, transform):
        """unmap transform from internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        for i in transform.inputs:
            for mti in registry.lookup(i):
                for mt in mti.mimetypes:
                    mt_in = self._mtmap.get(mt, {})
                    output = transform.output
                    mto = registry.lookup(output)
                    for mt2 in mto[0].mimetypes:
                        l = mt_in[mt2]
                        for i in range(len(l)):
                            if transform.name() == l[i].name():
                                l.pop(i)
                                break
                        else:
                            log('Can\'t find transform %s from %s to %s' % (
                                transform.name(), mti, mt),
                                severity=DEBUG)

    def _findPath(self, orig, target, required_transforms=()):
        """return the shortest path for transformation from orig mimetype to
        target mimetype
        """
        if not self._mtmap:
            return None

        orig = str(orig)
        target = str(target)
        # First, let's deal with required transforms.
        if required_transforms:
            # Let's decompose paths, then.
            required_transform = required_transforms.pop(0)
            # The first path must lead to one of the inputs supported
            # by this first required transform.
            # Which input types are supported by this transform ?
            supportedInputs = {}
            for input, outputs in self._mtmap.items():
                for output, transforms in outputs.items():
                    for transform in transforms:
                        if transform.name() == required_transform:
                            supportedInputs[input] = 'ok'
                            # BTW, let's remember the output type
                            transformOutput = output
                            # and remember the transform, it is
                            # useful later
                            requiredTransform = transform
            # Which of these inputs will be reachable with the
            # shortest path ?
            shortest = 9999  # big enough, I guess
            shortestFirstPath = None
            for supportedInput in supportedInputs.keys():
                # We start from orig
                firstOrig = orig
                # And want to reach supportedInput
                firstTarget = supportedInput
                # What's the shortest path ?
                firstPath = self._findPath(firstOrig, firstTarget)
                if firstPath is not None:
                    if len(firstPath) < shortest:
                        # Here is a path which is shorter than others
                        # which also reach the required transform.
                        shortest = len(firstPath)
                        shortestFirstPath = firstPath
            if shortestFirstPath is None:
                return None  # there is no path leading to this transform
            # Then we have to take this transform.
            secondPath = [requiredTransform]
            # From the output of this transform, we then have to
            # reach our target, possible through other required
            # transforms.
            thirdOrig = transformOutput
            thirdTarget = target
            thirdPath = self._findPath(thirdOrig, thirdTarget,
                                       required_transforms)
            if thirdPath is None:
                return None  # no path
            # Final result is the concatenation of these 3 parts
            return shortestFirstPath + secondPath + thirdPath

        if orig == target:
            return []

        # Now let's efficiently find the shortest path from orig
        # to target (without required transforms).
        # The overall idea is that we build all possible paths
        # starting from orig and of given length. And we increment
        # this length until one of these paths reaches our target or
        # until all reachable types have been reached.
        currentPathLength = 0
        pathToType = {orig: []}  # all paths we know, by end of path.

        def typesWithPathOfLength(length):
            '''Returns the lists of known paths of a given length'''
            result = []
            for type_, path in pathToType.items():
                if len(path) == length:
                    result.append(type_)
            return result

        # We will start exploring paths which start from types
        # reachable in zero steps. That is paths which start from
        # orig.
        typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # Explore paths while there are new paths to be explored
        while len(typesToStartFrom) > 0:
            for startingType in typesToStartFrom:
                # Where can we go in one step starting from here ?
                outputs = self._mtmap.get(startingType)
                if outputs:
                    for reachedType, transforms in outputs.items():
                        # Does this lead to a type we never reached before ?
                        if reachedType not in pathToType.keys() and transforms:
                            # Yes, we did not know any path reaching this type
                            # Let's remember the path to here
                            pathToType[reachedType] = (
                                pathToType[startingType] + [transforms[0]])
                            if reachedType == target:
                                # This is the first time we reach our target.
                                # We have our shortest path to target.
                                return pathToType[target]
            # We explored all possible paths of length currentPathLength
            # Let's increment that length.
            currentPathLength += 1
            # What are the next types to start from ?
            typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # We are done exploring paths starting from orig
        # and this exploration did not reach our target.
        # Hence there is no path from orig to target.
        return None

    def _getPaths(self, orig, target, requirements, path=None, result=None):
        """return some of the paths for transformation from orig mimetype to
        target mimetype with the guarantee that the shortest path is included.
        If target is the same as orig, then returns an empty path.
        """

        shortest = 9999
        if result:
            for okPath in result:
                shortest = min(shortest, len(okPath))

        if orig == target:
            return [[]]
        if path is None:
            result = []
            path = []
            requirements = list(requirements)
        outputs = self._mtmap.get(orig)
        if outputs is None:
            return result

        registry = getToolByName(self, 'mimetypes_registry')
        mto = registry.lookup(target)
        # target mimetype aliases
        target_aliases = mto[0].mimetypes

        path.append(None)
        for o_mt, transforms in outputs.items():
            for transform in transforms:
                required = 0
                name = transform.name()
                if name in requirements:
                    requirements.remove(name)
                    required = 1
                if transform in path:
                    # avoid infinite loop...
                    continue
                path[-1] = transform
                if o_mt in target_aliases:
                    if not requirements:
                        result.append(path[:])
                        if len(path[:]) < shortest:
                            # here is a shorter one !
                            shortest = len(path)
                else:
                    if len(path) < shortest:
                        # keep exploring this path, it is still short enough
                        self._getPaths(o_mt, target, requirements,
                                       path, result)
                if required:
                    requirements.append(name)
        path.pop()

        return result

    @security.private
    def manage_afterAdd(self, item, container):
        """ overload manage_afterAdd to finish initialization when the
        transform tool is added
        """
        Folder.manage_afterAdd(self, item, container)
        try:
            initialize(self)
        except TransformException:
            # may fail on copy or zexp import
            pass

    @security.protected(ManagePortal)
    def manage_addTransform(self, id, module, REQUEST=None):
        """ add a new transform to the tool """
        transform = Transform(id, module)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def manage_addTransformsChain(self, id, description, REQUEST=None):
        """ add a new transform to the tool """
        transform = TransformsChain(id, description)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def manage_setCacheValidityTime(self, seconds, REQUEST=None):
        """set  the lifetime of cached data in seconds"""
        self.max_sec_in_cache = int(seconds)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def reloadTransforms(self, ids=()):
        """ reload transforms with the given ids
        if no ids, reload all registered transforms

        return a list of (transform_id, transform_module) describing reloaded
        transforms
        """
        if not ids:
            ids = self.objectIds()
        reloaded = []
        for id in ids:
            o = getattr(self, id)
            o.reload()
            reloaded.append((id, o.module))
        return reloaded

    # Policy handling methods

    def manage_addPolicy(self, output_mimetype, required_transforms,
                         REQUEST=None):
        """ add a policy for a given output mime types"""
        registry = getToolByName(self, 'mimetypes_registry')
        if not registry.lookup(output_mimetype):
            raise TransformException('Unknown MIME type')
        if output_mimetype in self._policies:
            msg = 'A policy for output %s is yet defined' % output_mimetype
            raise TransformException(msg)

        required_transforms = tuple(required_transforms)
        self._policies[output_mimetype] = required_transforms
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/manage_editTransformationPolicyForm')

    def manage_delPolicies(self, outputs, REQUEST=None):
        """ remove policies for given output mime types"""
        for mimetype in outputs:
            del self._policies[mimetype]
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/manage_editTransformationPolicyForm')

    def listPolicies(self):
        """ return the list of defined policies

        a policy is a 2-uple (output_mime_type, [list of required transforms])
        """
        # XXXFIXME: backward compat, should be removed latter
        if not hasattr(self, '_policies'):
            self._policies = PersistentMapping()
        return self._policies.items()

    # mimetype oriented conversions (iengine interface)

    @security.private
    def registerTransform(self, transform):
        """register a new transform

        transform isn't a Zope Transform (the wrapper) but the wrapped
        transform the persistence wrapper will be created here
        """
        # needed when call from transform.transforms.initialize which
        # register non zope transform
        module = str(transform.__module__)
        transform = Transform(transform.name(), module, transform)
        if not ITransform.providedBy(transform):
            raise TransformException('%s does not implement ITransform' %
                                     transform)
        name = transform.name()
        __traceback_info__ = (name, transform)
        if name not in self.objectIds():
            self._setObject(name, transform)
            self._mapTransform(transform)

    @security.protected(ManagePortal)
    def ZopeFind(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.ZopeFind(self, *args, **kwargs)
        except MissingBinary:
            log('ZopeFind: catched MissingBinary exception')

    @security.protected(View)
    def objectItems(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.objectItems(self, *args, **kwargs)
        except MissingBinary:
            log('objectItems: catched MissingBinary exception')
            return []

    # available mimetypes ####################################################
    def listAvailableTextInputs(self):
        """Returns a list of mimetypes that can be used as input for textfields
        by building a list of the inputs beginning with "text/" of all
        transforms.
        """
        available_types = []
        candidate_transforms = [object[1] for object in self.objectItems()]
        for candidate in candidate_transforms:
            for input in candidate.inputs:
                if input.startswith("text/") and input not in available_types:
                    available_types.append(input)
        return available_types
class Resource(Persistent):
    security = ClassSecurityInfo()

    def __init__(self, id, **kwargs):
        self._data = PersistentMapping()
        extres = id.startswith('http://') or id.startswith('https://')
        if id.startswith('/') or id.endswith('/') or ('//' in id and not extres):
            raise ValueError("Invalid Resource ID: %s" % id)
        self._data['id'] = id
        expression = kwargs.get('expression', '')
        self.setExpression(expression)
        self._data['authenticated'] = kwargs.get('authenticated', False)
        self._data['enabled'] = kwargs.get('enabled', True)
        self._data['cookable'] = kwargs.get('cookable', True)
        self._data['cacheable'] = kwargs.get('cacheable', True)
        self._data['conditionalcomment'] = kwargs.get('conditionalcomment','')
        self._data['bundle'] = kwargs.get('bundle', 'default')
        self.isExternal = extres
        if extres:
            self._data['cacheable'] = False #External resources are NOT cacheable
            self._data['cookable'] = False #External resources are NOT mergable

    def copy(self):
        result = self.__class__(self.getId())
        for key, value in self._data.items():
            if key != 'id':
                result._data[key] = value
        return result

    security.declarePublic('getId')
    def getId(self):
        return self._data['id']

    security.declarePublic('getQuotedId')
    def getQuotedId(self):
        return quote_plus(self._data['id'])

    security.declareProtected(permissions.ManagePortal, '_setId')
    def _setId(self, id):
        if id.startswith('/') or id.endswith('/') or (
                ('//' in id) and not self.isExternalResource()):
            raise ValueError("Invalid Resource ID: %s" %id)
        self._data['id'] = id

    security.declarePublic('getCookedExpression')
    def getCookedExpression(self):
        # Automatic inline migration of expressions
        if 'cooked_expression' not in self._data:
            expr = Expression(self._data['expression'])
            self._data['cooked_expression'] = expr
        return self._data['cooked_expression']

    security.declarePublic('getExpression')
    def getExpression(self):
        return self._data['expression']

    security.declareProtected(permissions.ManagePortal, 'setExpression')
    def setExpression(self, expression):
        # Update the cooked expression
        self._data['cooked_expression'] = Expression( expression )
        self._data['expression'] = expression

    security.declarePublic('getAuthenticated')
    def getAuthenticated(self):
        # Automatic inline migration
        if 'authenticated' not in self._data:
            self._data['authenticated'] = False
        return bool(self._data['authenticated'])

    security.declareProtected(permissions.ManagePortal, 'setAuthenticated')
    def setAuthenticated(self, authenticated):
        self._data['authenticated'] = authenticated

    security.declarePublic('getEnabled')
    def getEnabled(self):
        return bool(self._data['enabled'])

    security.declareProtected(permissions.ManagePortal, 'setEnabled')
    def setEnabled(self, enabled):
        self._data['enabled'] = enabled

    security.declarePublic('getCookable')
    def getCookable(self):
        return self._data['cookable']

    security.declareProtected(permissions.ManagePortal, 'setCookable')
    def setCookable(self, cookable):
        if self.isExternalResource() and cookable:
            raise ValueError("External Resources cannot be merged")
        self._data['cookable'] = cookable

    security.declarePublic('getCacheable')
    def getCacheable(self):
        # as this is a new property, old instance might not have that value, so
        # return True as default
        return self._data.get('cacheable', True)

    security.declareProtected(permissions.ManagePortal, 'setCacheable')
    def setCacheable(self, cacheable):
        if self.isExternalResource() and cacheable:
            raise ValueError("External Resources are not cacheable")
        self._data['cacheable'] = cacheable

    security.declarePublic('getConditionalcomment')
    def getConditionalcomment(self):
        # New property, return blank if the old instance doesn't have that value
        return self._data.get('conditionalcomment','')

    security.declareProtected(permissions.ManagePortal, 'setConditionalcomment')
    def setConditionalcomment(self, conditionalcomment):
        self._data['conditionalcomment'] = conditionalcomment

    security.declarePublic('isExternalResource')
    def isExternalResource(self):
        return getattr(self, 'isExternal', False)

    security.declarePublic('getBundle')
    def getBundle(self):
        return self._data.get('bundle', None) or 'default'
    
    security.declareProtected(permissions.ManagePortal, 'setBundle')
    def setBundle(self, bundle):
        self._data['bundle'] = bundle
Пример #21
0
class TransformTool(UniqueObject, ActionProviderBase, Folder):

    id = 'portal_transforms'
    meta_type = id.title().replace('_', ' ')
    isPrincipiaFolderish = 1  # Show up in the ZMI

    meta_types = all_meta_types = (
        {
            'name': 'Transform',
            'action': 'manage_addTransformForm'
        },
        {
            'name': 'TransformsChain',
            'action': 'manage_addTransformsChainForm'
        },
    )

    manage_addTransformForm = PageTemplateFile('addTransform', _www)
    manage_addTransformsChainForm = PageTemplateFile('addTransformsChain',
                                                     _www)
    manage_cacheForm = PageTemplateFile('setCacheTime', _www)
    manage_editTransformationPolicyForm = PageTemplateFile(
        'editTransformationPolicy', _www)
    manage_reloadAllTransforms = PageTemplateFile('reloadAllTransforms', _www)

    manage_options = ((Folder.manage_options[0], ) +
                      Folder.manage_options[2:] + (
                          {
                              'label': 'Caches',
                              'action': 'manage_cacheForm'
                          },
                          {
                              'label': 'Policy',
                              'action': 'manage_editTransformationPolicyForm'
                          },
                          {
                              'label': 'Reload transforms',
                              'action': 'manage_reloadAllTransforms'
                          },
                      ))

    security = ClassSecurityInfo()

    def __init__(self, policies=None, max_sec_in_cache=3600):
        self._mtmap = PersistentMapping()
        self._policies = policies or PersistentMapping()
        self.max_sec_in_cache = max_sec_in_cache
        self._new_style_pt = 1

    # mimetype oriented conversions (iengine interface)

    @security.private
    def unregisterTransform(self, name):
        """ unregister a transform
        name is the name of a registered transform
        """
        self._unmapTransform(getattr(self, name))
        if name in self.objectIds():
            self._delObject(name)

    @security.public
    def convertTo(self,
                  target_mimetype,
                  orig,
                  data=None,
                  object=None,
                  usedby=None,
                  context=None,
                  **kwargs):
        """Convert orig to a given mimetype

        * orig is a native string

        * data an optional IDataStream object. If None a new datastream will be
        created and returned

        * optional object argument is the object on which is bound the data.
        If present that object will be used by the engine to bound cached data.

        * additional arguments (kwargs) will be passed to the transformations.
        Some usual arguments are : filename, mimetype, encoding

        return an object implementing IDataStream or None if no path has been
        found.
        """
        target_mimetype = str(target_mimetype)

        if object is not None:
            cache = Cache(object, context=context)
            data = cache.getCache(target_mimetype)
            if data is not None:
                time, data = data
                if self.max_sec_in_cache == 0 or time < self.max_sec_in_cache:
                    return data

        if data is None:
            data = self._wrap(target_mimetype)

        registry = getToolByName(self, 'mimetypes_registry')

        if not getattr(aq_base(registry), 'classify', None):
            # avoid problems when importing a site with an old mimetype
            # registry
            return None

        orig_mt = registry.classify(orig,
                                    mimetype=kwargs.get('mimetype'),
                                    filename=kwargs.get('filename'))
        orig_mt = str(orig_mt)
        if not orig_mt:
            log('Unable to guess input mime type (filename=%s, mimetype=%s)' %
                (kwargs.get('mimetype'), kwargs.get('filename')),
                severity=DEBUG)
            return None

        target_mt = registry.lookup(target_mimetype)
        if target_mt:
            target_mt = target_mt[0]
        else:
            log('Unable to match target mime type %s' % str(target_mimetype),
                severity=DEBUG)
            return None

        # fastpath
        # If orig_mt and target_mt are the same, we only allow
        # a one-hop transform, a.k.a. filter.
        # XXX disabled filtering for now
        if orig_mt == str(target_mt):
            data.setData(orig)
            md = data.getMetadata()
            md['mimetype'] = str(orig_mt)
            if object is not None:
                cache.setCache(str(target_mimetype), data)
            return data

        # get a path to output mime type
        requirements = self._policies.get(str(target_mt), [])
        path = self._findPath(orig_mt, target_mt, list(requirements))
        if not path and requirements:
            log('Unable to satisfy requirements %s' % ', '.join(requirements),
                severity=DEBUG)
            path = self._findPath(orig_mt, target_mt)

        if not path:
            log('NO PATH FROM %s TO %s : %s' %
                (orig_mt, target_mimetype, path),
                severity=DEBUG)
            return None

        if len(path) > 1:
            # create a chain on the fly (sly)
            transform = chain()
            for t in path:
                transform.registerTransform(t)
        else:
            transform = path[0]

        result = transform.convert(orig,
                                   data,
                                   context=context,
                                   usedby=usedby,
                                   **kwargs)
        self._setMetaData(result, transform)

        # set cache if possible
        if object is not None and result.isCacheable():
            cache.setCache(str(target_mimetype), result)

        # return IDataStream object
        return result

    # make sure it's not publishable (XSS risk)
    del convertTo.__doc__

    @security.public
    def convertToData(self,
                      target_mimetype,
                      orig,
                      data=None,
                      object=None,
                      usedby=None,
                      context=None,
                      **kwargs):
        # Convert to a given mimetype and return the raw data
        # ignoring subobjects. see convertTo for more information
        data = self.convertTo(target_mimetype, orig, data, object, usedby,
                              context, **kwargs)
        if data:
            return data.getData()
        return None

    @security.public
    def convert(self, name, orig, data=None, context=None, **kwargs):
        # run a tranform of a given name on data

        # * name is the name of a registered transform

        # see convertTo docstring for more info

        if not data:
            data = self._wrap(name)
        try:
            transform = getattr(self, name)
        except AttributeError:
            raise Exception('No such transform "%s"' % name)
        data = transform.convert(orig, data, context=context, **kwargs)
        self._setMetaData(data, transform)
        return data

    def __call__(self, name, orig, data=None, context=None, **kwargs):
        # run a transform by its name, returning the raw data product

        # * name is the name of a registered transform.

        # return an encoded string.
        # see convert docstring for more info on additional arguments.

        data = self.convert(name, orig, data, context, **kwargs)
        return data.getData()

    # utilities ###############################################################

    def _setMetaData(self, datastream, transform):
        """set metadata on datastream according to the given transform
        (mime type and optionaly encoding)
        """
        md = datastream.getMetadata()
        if hasattr(transform, 'output_encoding'):
            md['encoding'] = transform.output_encoding
        md['mimetype'] = transform.output

    def _wrap(self, name):
        """wrap a data object in an icache"""
        return datastream(name)

    def _unwrap(self, data):
        """unwrap data from an icache"""
        if IDataStream.providedBy(data):
            data = data.getData()
        return data

    def _mapTransform(self, transform):
        """map transform to internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        inputs = getattr(transform, 'inputs', None)
        if not inputs:
            raise TransformException('Bad transform %s : no input MIME type' %
                                     (transform))
        for i in inputs:
            mts = registry.lookup(i)
            if not mts:
                msg = 'Input MIME type %r for transform %s is not registered '\
                      'in the MIME types registry' % (i, transform.name())
                raise TransformException(msg)
            for mti in mts:
                for mt in mti.mimetypes:
                    mt_in = self._mtmap.setdefault(mt, PersistentMapping())
                    output = getattr(transform, 'output', None)
                    if not output:
                        msg = 'Bad transform %s : no output MIME type'
                        raise TransformException(msg % transform.name())
                    mto = registry.lookup(output)
                    if not mto:
                        msg = 'Output MIME type %r for transform %s is not '\
                              'registered in the MIME types registry' % \
                              (output, transform.name())
                        raise TransformException(msg)
                    if len(mto) > 1:
                        msg = ("Wildcarding not allowed in transform's output "
                               "MIME type")
                        raise TransformException(msg)

                    for mt2 in mto[0].mimetypes:
                        try:
                            if transform not in mt_in[mt2]:
                                mt_in[mt2].append(transform)
                        except KeyError:
                            mt_in[mt2] = PersistentList([transform])

    def _unmapTransform(self, transform):
        """unmap transform from internal structures"""
        registry = getToolByName(self, 'mimetypes_registry')
        for i in transform.inputs:
            for mti in registry.lookup(i):
                for mt in mti.mimetypes:
                    mt_in = self._mtmap.get(mt, {})
                    output = transform.output
                    mto = registry.lookup(output)
                    for mt2 in mto[0].mimetypes:
                        l = mt_in[mt2]
                        for i in range(len(l)):
                            if transform.name() == l[i].name():
                                l.pop(i)
                                break
                        else:
                            log('Can\'t find transform %s from %s to %s' %
                                (transform.name(), mti, mt),
                                severity=DEBUG)

    def _findPath(self, orig, target, required_transforms=()):
        """return the shortest path for transformation from orig mimetype to
        target mimetype
        """
        if not self._mtmap:
            return None

        orig = str(orig)
        target = str(target)
        # First, let's deal with required transforms.
        if required_transforms:
            # Let's decompose paths, then.
            required_transform = required_transforms.pop(0)
            # The first path must lead to one of the inputs supported
            # by this first required transform.
            # Which input types are supported by this transform ?
            supportedInputs = {}
            for input, outputs in self._mtmap.items():
                for output, transforms in outputs.items():
                    for transform in transforms:
                        if transform.name() == required_transform:
                            supportedInputs[input] = 'ok'
                            # BTW, let's remember the output type
                            transformOutput = output
                            # and remember the transform, it is
                            # useful later
                            requiredTransform = transform
            # Which of these inputs will be reachable with the
            # shortest path ?
            shortest = 9999  # big enough, I guess
            shortestFirstPath = None
            for supportedInput in supportedInputs.keys():
                # We start from orig
                firstOrig = orig
                # And want to reach supportedInput
                firstTarget = supportedInput
                # What's the shortest path ?
                firstPath = self._findPath(firstOrig, firstTarget)
                if firstPath is not None:
                    if len(firstPath) < shortest:
                        # Here is a path which is shorter than others
                        # which also reach the required transform.
                        shortest = len(firstPath)
                        shortestFirstPath = firstPath
            if shortestFirstPath is None:
                return None  # there is no path leading to this transform
            # Then we have to take this transform.
            secondPath = [requiredTransform]
            # From the output of this transform, we then have to
            # reach our target, possible through other required
            # transforms.
            thirdOrig = transformOutput
            thirdTarget = target
            thirdPath = self._findPath(thirdOrig, thirdTarget,
                                       required_transforms)
            if thirdPath is None:
                return None  # no path
            # Final result is the concatenation of these 3 parts
            return shortestFirstPath + secondPath + thirdPath

        if orig == target:
            return []

        # Now let's efficiently find the shortest path from orig
        # to target (without required transforms).
        # The overall idea is that we build all possible paths
        # starting from orig and of given length. And we increment
        # this length until one of these paths reaches our target or
        # until all reachable types have been reached.
        currentPathLength = 0
        pathToType = {orig: []}  # all paths we know, by end of path.

        def typesWithPathOfLength(length):
            '''Returns the lists of known paths of a given length'''
            result = []
            for type_, path in pathToType.items():
                if len(path) == length:
                    result.append(type_)
            return result

        # We will start exploring paths which start from types
        # reachable in zero steps. That is paths which start from
        # orig.
        typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # Explore paths while there are new paths to be explored
        while len(typesToStartFrom) > 0:
            for startingType in typesToStartFrom:
                # Where can we go in one step starting from here ?
                outputs = self._mtmap.get(startingType)
                if outputs:
                    for reachedType, transforms in outputs.items():
                        # Does this lead to a type we never reached before ?
                        if reachedType not in six.iterkeys(
                                pathToType) and transforms:  # noqa
                            # Yes, we did not know any path reaching this type
                            # Let's remember the path to here
                            pathToType[reachedType] = (
                                pathToType[startingType] + [transforms[0]])
                            if reachedType == target:
                                # This is the first time we reach our target.
                                # We have our shortest path to target.
                                return pathToType[target]
            # We explored all possible paths of length currentPathLength
            # Let's increment that length.
            currentPathLength += 1
            # What are the next types to start from ?
            typesToStartFrom = typesWithPathOfLength(currentPathLength)
        # We are done exploring paths starting from orig
        # and this exploration did not reach our target.
        # Hence there is no path from orig to target.
        return None

    def _getPaths(self, orig, target, requirements, path=None, result=None):
        """return some of the paths for transformation from orig mimetype to
        target mimetype with the guarantee that the shortest path is included.
        If target is the same as orig, then returns an empty path.
        """

        shortest = 9999
        if result:
            for okPath in result:
                shortest = min(shortest, len(okPath))

        if orig == target:
            return [[]]
        if path is None:
            result = []
            path = []
            requirements = list(requirements)
        outputs = self._mtmap.get(orig)
        if outputs is None:
            return result

        registry = getToolByName(self, 'mimetypes_registry')
        mto = registry.lookup(target)
        # target mimetype aliases
        target_aliases = mto[0].mimetypes

        path.append(None)
        for o_mt, transforms in outputs.items():
            for transform in transforms:
                required = 0
                name = transform.name()
                if name in requirements:
                    requirements.remove(name)
                    required = 1
                if transform in path:
                    # avoid infinite loop...
                    continue
                path[-1] = transform
                if o_mt in target_aliases:
                    if not requirements:
                        result.append(path[:])
                        if len(path[:]) < shortest:
                            # here is a shorter one !
                            shortest = len(path)
                else:
                    if len(path) < shortest:
                        # keep exploring this path, it is still short enough
                        self._getPaths(o_mt, target, requirements, path,
                                       result)
                if required:
                    requirements.append(name)
        path.pop()

        return result

    @security.private
    def manage_afterAdd(self, item, container):
        """ overload manage_afterAdd to finish initialization when the
        transform tool is added
        """
        Folder.manage_afterAdd(self, item, container)
        try:
            initialize(self)
        except TransformException:
            # may fail on copy or zexp import
            pass

    @security.protected(ManagePortal)
    def manage_addTransform(self, id, module, REQUEST=None):
        """ add a new transform to the tool """
        transform = Transform(id, module)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def manage_addTransformsChain(self, id, description, REQUEST=None):
        """ add a new transform to the tool """
        transform = TransformsChain(id, description)
        self._setObject(id, transform)
        self._mapTransform(transform)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def manage_setCacheValidityTime(self, seconds, REQUEST=None):
        """set  the lifetime of cached data in seconds"""
        self.max_sec_in_cache = int(seconds)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_main')

    @security.protected(ManagePortal)
    def reloadTransforms(self, ids=()):
        """ reload transforms with the given ids
        if no ids, reload all registered transforms

        return a list of (transform_id, transform_module) describing reloaded
        transforms
        """
        if not ids:
            ids = self.objectIds()
        reloaded = []
        for id in ids:
            o = getattr(self, id)
            o.reload()
            reloaded.append((id, o.module))
        return reloaded

    # Policy handling methods

    def manage_addPolicy(self,
                         output_mimetype,
                         required_transforms,
                         REQUEST=None):
        """ add a policy for a given output mime types"""
        registry = getToolByName(self, 'mimetypes_registry')
        if not registry.lookup(output_mimetype):
            raise TransformException('Unknown MIME type')
        if output_mimetype in self._policies:
            msg = 'A policy for output %s is yet defined' % output_mimetype
            raise TransformException(msg)

        required_transforms = tuple(required_transforms)
        self._policies[output_mimetype] = required_transforms
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_editTransformationPolicyForm')

    def manage_delPolicies(self, outputs, REQUEST=None):
        """ remove policies for given output mime types"""
        for mimetype in outputs:
            del self._policies[mimetype]
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_editTransformationPolicyForm')

    def listPolicies(self):
        """ return the list of defined policies

        a policy is a 2-uple (output_mime_type, [list of required transforms])
        """
        # XXXFIXME: backward compat, should be removed latter
        if not hasattr(self, '_policies'):
            self._policies = PersistentMapping()
        return list(self._policies.items())

    # mimetype oriented conversions (iengine interface)

    @security.private
    def registerTransform(self, transform):
        """register a new transform

        transform isn't a Zope Transform (the wrapper) but the wrapped
        transform the persistence wrapper will be created here
        """
        # needed when call from transform.transforms.initialize which
        # register non zope transform
        module = str(transform.__module__)
        transform = Transform(transform.name(), module, transform)
        if not ITransform.providedBy(transform):
            raise TransformException('%s does not implement ITransform' %
                                     transform)
        name = transform.name()
        __traceback_info__ = (name, transform)
        if name not in self.objectIds():
            self._setObject(name, transform)
            self._mapTransform(transform)

    @security.protected(ManagePortal)
    def ZopeFind(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.ZopeFind(self, *args, **kwargs)
        except MissingBinary:
            log('ZopeFind: catched MissingBinary exception')

    @security.protected(View)
    def objectItems(self, *args, **kwargs):
        """Don't break ZopeFind feature when a transform can't be loaded
        """
        try:
            return Folder.objectItems(self, *args, **kwargs)
        except MissingBinary:
            log('objectItems: catched MissingBinary exception')
            return []

    # available mimetypes ####################################################
    def listAvailableTextInputs(self):
        """Returns a list of mimetypes that can be used as input for textfields
        by building a list of the inputs beginning with "text/" of all
        transforms.
        """
        available_types = []
        candidate_transforms = [object[1] for object in self.objectItems()]
        for candidate in candidate_transforms:
            for input in candidate.inputs:
                if input.startswith("text/") and input not in available_types:
                    available_types.append(input)
        return available_types