Example #1
0
class Variables(ContainerTab):
    """A container for variable definitions"""

    meta_type = 'Workflow Variables'

    all_meta_types = ({'name':VariableDefinition.meta_type,
                       'action':'addVariable',
                       },)

    _manage_variables = DTMLResource('dtml/variables', globals())

    def manage_main(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._manage_variables(
            REQUEST,
            management_view='Variables',
            manage_tabs_message=manage_tabs_message,
            )

    def addVariable(self, id, REQUEST=None):
        '''
        '''
        vdef = VariableDefinition(id)
        self._setObject(id, vdef)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Variable added.')

    def deleteVariables(self, ids, REQUEST=None):
        '''
        '''
        for id in ids:
            self._delObject(id)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Variable(s) removed.')

    def _checkId(self, id, allow_dup=0):
        wf_def = aq_parent(aq_inner(self))
        if id == wf_def.state_var:
            raise 'Bad Request', '"%s" is used for keeping state.' % id
        return ContainerTab._checkId(self, id, allow_dup)

    def getStateVar(self):
        wf_def = aq_parent(aq_inner(self))
        return wf_def.state_var

    def setStateVar(self, id, REQUEST=None):
        '''
        '''
        wf_def = aq_parent(aq_inner(self))
        if id != wf_def.state_var:
            self._checkId(id)
            wf_def.state_var = str(id)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Set state variable.')
Example #2
0
class States(ContainerTab):
    """A container for state definitions"""

    meta_type = 'Workflow States'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    all_meta_types = ({
        'name': StateDefinition.meta_type,
        'action': 'addState',
    }, )

    _manage_states = DTMLResource('dtml/states', globals())

    def manage_main(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._manage_states(
            REQUEST,
            management_view='States',
            manage_tabs_message=manage_tabs_message,
        )

    def addState(self, id, REQUEST=None):
        '''
        '''
        sdef = StateDefinition(id)
        self._setObject(id, sdef)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'State added.')

    def deleteStates(self, ids, REQUEST=None):
        '''
        '''
        for id in ids:
            self._delObject(id)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'State(s) removed.')

    def setInitialState(self, id=None, ids=None, REQUEST=None):
        '''
        '''
        if not id:
            if len(ids) != 1:
                raise ValueError, 'One and only one state must be selected'
            id = ids[0]
        id = str(id)
        aq_parent(aq_inner(self)).initial_state = id
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Initial state selected.')
Example #3
0
class PropertiesTool(UniqueObject, SimpleItem, ActionProviderBase):

    implements(IPropertiesTool)
    __implements__ = (z2IPropertiesTool, ActionProviderBase.__implements__)

    id = 'portal_properties'
    meta_type = 'Default Properties Tool'

    security = ClassSecurityInfo()

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

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLResource('dtml/explainPropertiesTool', globals())

    #
    #   'portal_properties' interface methods
    #
    security.declareProtected(ManagePortal, 'editProperties')

    def editProperties(self, props):
        '''Change portal settings'''
        aq_parent(aq_inner(self)).manage_changeProperties(props)
        self.MailHost.smtp_host = props['smtp_server']
        if hasattr(self, 'propertysheets'):
            ps = self.propertysheets
            if hasattr(ps, 'props'):
                ps.props.manage_changeProperties(props)

    def title(self):
        return self.aq_inner.aq_parent.title

    def smtp_server(self):
        return self.MailHost.smtp_host
Example #4
0
class Worklists(ContainerTab):
    """A container for worklist definitions"""

    meta_type = 'Worklists'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    all_meta_types = ({
        'name': WorklistDefinition.meta_type,
        'action': 'addWorklist',
    }, )

    _manage_worklists = DTMLResource('dtml/worklists', globals())

    def manage_main(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._manage_worklists(
            REQUEST,
            management_view='Worklists',
            manage_tabs_message=manage_tabs_message,
        )

    def addWorklist(self, id, REQUEST=None):
        '''
        '''
        qdef = WorklistDefinition(id)
        self._setObject(id, qdef)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Worklist added.')

    def deleteWorklists(self, ids, REQUEST=None):
        '''
        '''
        for id in ids:
            self._delObject(id)
        if REQUEST is not None:
            return self.manage_main(REQUEST, 'Worklist(s) removed.')
Example #5
0
class WorkflowUIMixin:
    '''
    '''

    security = ClassSecurityInfo()

    security.declareProtected(ManagePortal, 'manage_properties')
    manage_properties = DTMLResource('dtml/workflow_properties', globals())
    manage_groups = PageTemplateResource('dtml/workflow_groups.pt', globals())

    security.declareProtected(ManagePortal, 'setProperties')
    def setProperties(self, title, manager_bypass=0, props=None, REQUEST=None):
        """Sets basic properties.
        """
        self.title = str(title)
        self.manager_bypass = manager_bypass and 1 or 0
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.creation_guard = g
        else:
            self.creation_guard = None
        if REQUEST is not None:
            return self.manage_properties(
                REQUEST, manage_tabs_message='Properties changed.')

    _permissions_form = DTMLResource('dtml/workflow_permissions', globals())

    security.declareProtected(ManagePortal, 'manage_permissions')
    def manage_permissions(self, REQUEST, manage_tabs_message=None):
        """Displays the form for choosing which permissions to manage.
        """
        return self._permissions_form(REQUEST,
                                      management_view='Permissions',
                                      manage_tabs_message=manage_tabs_message,
                                      )

    security.declareProtected(ManagePortal, 'addManagedPermission')
    def addManagedPermission(self, p, REQUEST=None):
        """Adds to the list of permissions to manage.
        """
        if p in self.permissions:
            raise ValueError, 'Already a managed permission: ' + p
        if REQUEST is not None and p not in self.getPossiblePermissions():
            raise ValueError, 'Not a valid permission name:' + p
        self.permissions = self.permissions + (p,)
        if REQUEST is not None:
            return self.manage_permissions(
                REQUEST, manage_tabs_message='Permission added.')

    security.declareProtected(ManagePortal, 'delManagedPermissions')
    def delManagedPermissions(self, ps, REQUEST=None):
        """Removes from the list of permissions to manage.
        """
        if ps:
            l = list(self.permissions)
            for p in ps:
                l.remove(p)
            self.permissions = tuple(l)
        if REQUEST is not None:
            return self.manage_permissions(
                REQUEST, manage_tabs_message='Permission(s) removed.')

    security.declareProtected(ManagePortal, 'getPossiblePermissions')
    def getPossiblePermissions(self):
        """Returns the list of all permissions that can be managed.
        """
        # possible_permissions is in AccessControl.Role.RoleManager.
        return list(self.possible_permissions())

    security.declareProtected(ManagePortal, 'getGroups')
    def getGroups(self):
        """Returns the names of groups managed by this workflow.
        """
        return tuple(self.groups)

    security.declareProtected(ManagePortal, 'getAvailableGroups')
    def getAvailableGroups(self):
        """Returns a list of available group names.
        """
        gf = aq_get( self, '__allow_groups__', None, 1 )
        if gf is None:
            return ()
        try:
            groups = gf.searchGroups()
        except AttributeError:
            return ()
        else:
            return [g['id'] for g in groups]

    security.declareProtected(ManagePortal, 'addGroup')
    def addGroup(self, group, RESPONSE=None):
        """Adds a group by name.
        """
        if group not in self.getAvailableGroups():
            raise ValueError(group)
        self.groups = self.groups + (group,)
        if RESPONSE is not None:
            RESPONSE.redirect(
                "%s/manage_groups?manage_tabs_message=Added+group."
                % self.absolute_url())

    security.declareProtected(ManagePortal, 'delGroups')
    def delGroups(self, groups, RESPONSE=None):
        """Removes groups by name.
        """
        self.groups = tuple([g for g in self.groups if g not in groups])
        if RESPONSE is not None:
            RESPONSE.redirect(
                "%s/manage_groups?manage_tabs_message=Groups+removed."
                % self.absolute_url())

    security.declareProtected(ManagePortal, 'getAvailableRoles')
    def getAvailableRoles(self):
        """Returns the acquired roles mixed with base_cms roles.
        """
        roles = list(self.valid_roles())
        for role in getDefaultRolePermissionMap().keys():
            if role not in roles:
                roles.append(role)
        roles.sort()
        return roles

    security.declareProtected(ManagePortal, 'getRoles')
    def getRoles(self):
        """Returns the list of roles managed by this workflow.
        """
        roles = self.roles
        if roles is not None:
            return roles
        roles = getDefaultRolePermissionMap().keys()
        if roles:
            # Map the base_cms roles by default.
            roles.sort()
            return roles
        return self.valid_roles()

    security.declareProtected(ManagePortal, 'setRoles')
    def setRoles(self, roles, RESPONSE=None):
        """Changes the list of roles mapped to groups by this workflow.
        """
        avail = self.getAvailableRoles()
        for role in roles:
            if role not in avail:
                raise ValueError(role)
        self.roles = tuple(roles)
        if RESPONSE is not None:
            RESPONSE.redirect(
                "%s/manage_groups?manage_tabs_message=Roles+changed."
                % self.absolute_url())

    security.declareProtected(ManagePortal, 'getGuard')
    def getGuard(self):
        """Returns the initiation guard.

        If no init guard has been created, returns a temporary object.
        """
        if self.creation_guard is not None:
            return self.creation_guard
        else:
            return Guard().__of__(self)  # Create a temporary guard.

    security.declarePublic('guardExprDocs')
    def guardExprDocs(self):
        """Returns documentation on guard expressions.
        """
        text = resource_string('doc/expressions.stx', globals())
        from DocumentTemplate.DT_Var import structured_text
        return structured_text(text)
Example #6
0
class DefaultDublinCoreImpl(PropertyManager):
    """ Mix-in class which provides Dublin Core methods.
    """

    implements(IDublinCore, ICatalogableDublinCore, IMutableDublinCore)
    __implements__ = (z2IDublinCore, z2ICatalogableDublinCore,
                      z2IMutableDublinCore)

    security = ClassSecurityInfo()

    def __init__(self,
                 title='',
                 subject=(),
                 description='',
                 contributors=(),
                 effective_date=None,
                 expiration_date=None,
                 format='text/html',
                 language='',
                 rights=''):
        now = DateTime()
        self.creation_date = now
        self.modification_date = now
        self.creators = ()
        self._editMetadata(title, subject, description, contributors,
                           effective_date, expiration_date, format, language,
                           rights)

    #
    #  Set-modification-date-related methods.
    #  In DefaultDublinCoreImpl for lack of a better place.
    #

    # Class variable default for an upgrade.
    modification_date = None

    security.declarePrivate('notifyModified')

    def notifyModified(self):
        """ Take appropriate action after the resource has been modified.

        Update creators and modification_date.
        """
        self.addCreator()
        self.setModificationDate()

    security.declareProtected(ModifyPortalContent, 'addCreator')

    def addCreator(self, creator=None):
        """ Add creator to Dublin Core creators.
        """
        if creator is None:
            mtool = getToolByName(self, 'portal_membership', None)
            creator = mtool and mtool.getAuthenticatedMember().getId()

        # call self.listCreators() to make sure self.creators exists
        if creator and not creator in self.listCreators():
            self.creators = self.creators + (creator, )

    security.declareProtected(ModifyPortalContent, 'setModificationDate')

    def setModificationDate(self, modification_date=None):
        """ Set the date when the resource was last modified.

        When called without an argument, sets the date to now.
        """
        if modification_date is None:
            self.modification_date = DateTime()
        else:
            self.modification_date = self._datify(modification_date)

    #
    #  DublinCore interface query methods
    #
    security.declareProtected(View, 'Title')

    def Title(self):
        """ Dublin Core Title element - resource name.
        """
        return self.title

    security.declareProtected(View, 'listCreators')

    def listCreators(self):
        """ List Dublin Core Creator elements - resource authors.
        """
        if not hasattr(aq_base(self), 'creators'):
            # for content created with CMF versions before 1.5
            owner_tuple = self.getOwnerTuple()
            if owner_tuple:
                self.creators = (owner_tuple[1], )
            else:
                self.creators = ()
        return self.creators

    security.declareProtected(View, 'Creator')

    def Creator(self):
        """ Dublin Core Creator element - resource author.
        """
        creators = self.listCreators()
        return creators and creators[0] or ''

    security.declareProtected(View, 'Subject')

    def Subject(self):
        """ Dublin Core Subject element - resource keywords.
        """
        return getattr(self, 'subject', ())  # compensate for *old* content

    security.declareProtected(View, 'Description')

    def Description(self):
        """ Dublin Core Description element - resource summary.
        """
        return self.description

    security.declareProtected(View, 'Publisher')

    def Publisher(self):
        """ Dublin Core Publisher element - resource publisher.
        """
        tool = getToolByName(self, 'portal_metadata', None)

        if tool is not None:
            return tool.getPublisher()

        return 'No publisher'

    security.declareProtected(View, 'listContributors')

    def listContributors(self):
        """ Dublin Core Contributor elements - resource collaborators.
        """
        return self.contributors

    security.declareProtected(View, 'Contributors')

    def Contributors(self):
        """ Deprecated alias of listContributors.
        """
        return self.listContributors()

    security.declareProtected(View, 'Date')

    def Date(self):
        """ Dublin Core Date element - default date.
        """
        # Return effective_date if set, modification date otherwise
        date = getattr(self, 'effective_date', None)
        if date is None:
            date = self.modified()
        return date.toZone(_zone).ISO()

    security.declareProtected(View, 'CreationDate')

    def CreationDate(self):
        """ Dublin Core Date element - date resource created.
        """
        # return unknown if never set properly
        if self.creation_date:
            return self.creation_date.toZone(_zone).ISO()
        else:
            return 'Unknown'

    security.declareProtected(View, 'EffectiveDate')

    def EffectiveDate(self):
        """ Dublin Core Date element - date resource becomes effective.
        """
        ed = getattr(self, 'effective_date', None)
        return ed and ed.toZone(_zone).ISO() or 'None'

    security.declareProtected(View, 'ExpirationDate')

    def ExpirationDate(self):
        """ Dublin Core Date element - date resource expires.
        """
        ed = getattr(self, 'expiration_date', None)
        return ed and ed.toZone(_zone).ISO() or 'None'

    security.declareProtected(View, 'ModificationDate')

    def ModificationDate(self):
        """ Dublin Core Date element - date resource last modified.
        """
        return self.modified().toZone(_zone).ISO()

    security.declareProtected(View, 'Type')

    def Type(self):
        """ Dublin Core Type element - resource type.
        """
        ti = self.getTypeInfo()
        return ti is not None and ti.Title() or 'Unknown'

    security.declareProtected(View, 'Format')

    def Format(self):
        """ Dublin Core Format element - resource format.
        """
        return self.format

    security.declareProtected(View, 'Identifier')

    def Identifier(self):
        """ Dublin Core Identifier element - resource ID.
        """
        # XXX: fixme using 'portal_metadata' (we need to prepend the
        #      right prefix to self.getPhysicalPath().
        return self.absolute_url()

    security.declareProtected(View, 'Language')

    def Language(self):
        """ Dublin Core Language element - resource language.
        """
        return self.language

    security.declareProtected(View, 'Rights')

    def Rights(self):
        """ Dublin Core Rights element - resource copyright.
        """
        return self.rights

    #
    #  DublinCore utility methods
    #
    def content_type(self):
        """ WebDAV needs this to do the Right Thing (TM).
        """
        return self.Format()

    __FLOOR_DATE = DateTime(1970, 0)  # always effective

    security.declareProtected(View, 'isEffective')

    def isEffective(self, date):
        """ Is the date within the resource's effective range?
        """
        pastEffective = (self.effective_date is None
                         or self.effective_date <= date)
        beforeExpiration = (self.expiration_date is None
                            or self.expiration_date >= date)
        return pastEffective and beforeExpiration

    #
    #  CatalogableDublinCore methods
    #
    security.declareProtected(View, 'created')

    def created(self):
        """ Dublin Core Date element - date resource created.
        """
        # allow for non-existent creation_date, existed always
        date = getattr(self, 'creation_date', None)
        return date is None and self.__FLOOR_DATE or date

    security.declareProtected(View, 'effective')

    def effective(self):
        """ Dublin Core Date element - date resource becomes effective.
        """
        marker = []
        date = getattr(self, 'effective_date', marker)
        if date is marker:
            date = getattr(self, 'creation_date', None)
        return date is None and self.__FLOOR_DATE or date

    __CEILING_DATE = DateTime(2500, 0)  # never expires

    security.declareProtected(View, 'expires')

    def expires(self):
        """ Dublin Core Date element - date resource expires.
        """
        date = getattr(self, 'expiration_date', None)
        return date is None and self.__CEILING_DATE or date

    security.declareProtected(View, 'modified')

    def modified(self):
        """ Dublin Core Date element - date resource last modified.
        """
        date = self.modification_date
        if date is None:
            # Upgrade.
            date = self.bobobase_modification_time()
            self.modification_date = date
        return date

    security.declareProtected(View, 'getMetadataHeaders')

    def getMetadataHeaders(self):
        """ Return RFC-822-style headers.
        """
        hdrlist = []
        hdrlist.append(('Title', self.Title()))
        hdrlist.append(('Subject', ', '.join(self.Subject())))
        hdrlist.append(('Publisher', self.Publisher()))
        hdrlist.append(('Description', self.Description()))
        hdrlist.append(('Contributors', '; '.join(self.Contributors())))
        hdrlist.append(('Effective_date', self.EffectiveDate()))
        hdrlist.append(('Expiration_date', self.ExpirationDate()))
        hdrlist.append(('Type', self.Type()))
        hdrlist.append(('Format', self.Format()))
        hdrlist.append(('Language', self.Language()))
        hdrlist.append(('Rights', self.Rights()))
        return hdrlist

    #
    #  MutableDublinCore methods
    #
    security.declarePrivate('_datify')

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

    security.declareProtected(ModifyPortalContent, 'setTitle')

    def setTitle(self, title):
        """ Set Dublin Core Title element - resource name.
        """
        self.title = title

    security.declareProtected(ModifyPortalContent, 'setCreators')

    def setCreators(self, creators):
        """ Set Dublin Core Creator elements - resource authors.
        """
        self.creators = tuplize('creators', creators)

    security.declareProtected(ModifyPortalContent, 'setSubject')

    def setSubject(self, subject):
        """ Set Dublin Core Subject element - resource keywords.
        """
        self.subject = tuplize('subject', subject)

    security.declareProtected(ModifyPortalContent, 'setDescription')

    def setDescription(self, description):
        """ Set Dublin Core Description element - resource summary.
        """
        self.description = description

    security.declareProtected(ModifyPortalContent, 'setContributors')

    def setContributors(self, contributors):
        """ Set Dublin Core Contributor elements - resource collaborators.
        """
        # XXX: fixme
        self.contributors = tuplize('contributors', contributors, semi_split)

    security.declareProtected(ModifyPortalContent, 'setEffectiveDate')

    def setEffectiveDate(self, effective_date):
        """ Set Dublin Core Date element - date resource becomes effective.
        """
        self.effective_date = self._datify(effective_date)

    security.declareProtected(ModifyPortalContent, 'setExpirationDate')

    def setExpirationDate(self, expiration_date):
        """ Set Dublin Core Date element - date resource expires.
        """
        self.expiration_date = self._datify(expiration_date)

    security.declareProtected(ModifyPortalContent, 'setFormat')

    def setFormat(self, format):
        """ Set Dublin Core Format element - resource format.
        """
        self.format = format

    security.declareProtected(ModifyPortalContent, 'setLanguage')

    def setLanguage(self, language):
        """ Set Dublin Core Language element - resource language.
        """
        self.language = language

    security.declareProtected(ModifyPortalContent, 'setRights')

    def setRights(self, rights):
        """ Set Dublin Core Rights element - resource copyright.
        """
        self.rights = rights

    #
    #  Management tab methods
    #

    security.declarePrivate('_editMetadata')

    def _editMetadata(self,
                      title=_marker,
                      subject=_marker,
                      description=_marker,
                      contributors=_marker,
                      effective_date=_marker,
                      expiration_date=_marker,
                      format=_marker,
                      language=_marker,
                      rights=_marker):
        """ Update the editable metadata for this resource.
        """
        if title is not _marker:
            self.setTitle(title)
        if subject is not _marker:
            self.setSubject(subject)
        if description is not _marker:
            self.setDescription(description)
        if contributors is not _marker:
            self.setContributors(contributors)
        if effective_date is not _marker:
            self.setEffectiveDate(effective_date)
        if expiration_date is not _marker:
            self.setExpirationDate(expiration_date)
        if format is not _marker:
            self.setFormat(format)
        if language is not _marker:
            self.setLanguage(language)
        if rights is not _marker:
            self.setRights(rights)

    security.declareProtected(ModifyPortalContent, 'manage_metadata')
    manage_metadata = DTMLResource('dtml/zmi_metadata', globals())

    security.declareProtected(ModifyPortalContent, 'manage_editMetadata')

    def manage_editMetadata(self, title, subject, description, contributors,
                            effective_date, expiration_date, format, language,
                            rights, REQUEST):
        """ Update metadata from the ZMI.
        """
        self._editMetadata(title, subject, description, contributors,
                           effective_date, expiration_date, format, language,
                           rights)
        REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_metadata' +
                                     '?manage_tabs_message=Metadata+updated.')

    security.declareProtected(ModifyPortalContent, 'editMetadata')

    def editMetadata(self,
                     title='',
                     subject=(),
                     description='',
                     contributors=(),
                     effective_date=None,
                     expiration_date=None,
                     format='text/html',
                     language='en-US',
                     rights=''):
        """
        Need to add check for webDAV locked resource for TTW methods.
        """
        # as per bug #69, we cant assume they use the webdav
        # locking interface, and fail gracefully if they dont
        if hasattr(self, 'failIfLocked'):
            self.failIfLocked()

        self._editMetadata(title=title,
                           subject=subject,
                           description=description,
                           contributors=contributors,
                           effective_date=effective_date,
                           expiration_date=expiration_date,
                           format=format,
                           language=language,
                           rights=rights)
        self.reindexObject()
Example #7
0
class TransitionDefinition(SimpleItem):
    """Transition definition"""

    meta_type = 'Workflow Transition'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    title = ''
    description = ''
    new_state_id = ''
    trigger_type = TRIGGER_USER_ACTION
    guard = None
    actbox_name = ''
    actbox_url = ''
    actbox_category = 'workflow'
    var_exprs = None  # A mapping.
    script_name = None  # Executed before transition
    after_script_name = None  # Executed after transition

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

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

    def getId(self):
        return self.id

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

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

    def getVarExprText(self, id):
        if not self.var_exprs:
            return ''
        else:
            expr = self.var_exprs.get(id, None)
            if expr is not None:
                return expr.text
            else:
                return ''

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

    def getAvailableStateIds(self):
        return self.getWorkflow().states.keys()

    def getAvailableScriptIds(self):
        return self.getWorkflow().scripts.keys()

    def getAvailableVarIds(self):
        return self.getWorkflow().variables.keys()

    _properties_form = DTMLResource('dtml/transition_properties', globals())

    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,
                      title,
                      new_state_id,
                      trigger_type=TRIGGER_USER_ACTION,
                      script_name='',
                      after_script_name='',
                      actbox_name='',
                      actbox_url='',
                      actbox_category='workflow',
                      props=None,
                      REQUEST=None,
                      description=''):
        '''
        '''
        self.title = str(title)
        self.description = str(description)
        self.new_state_id = str(new_state_id)
        self.trigger_type = int(trigger_type)
        self.script_name = str(script_name)
        self.after_script_name = str(after_script_name)
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.guard = g
        else:
            self.guard = None
        self.actbox_name = str(actbox_name)
        self.actbox_url = str(actbox_url)
        self.actbox_category = str(actbox_category)
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')

    _variables_form = DTMLResource('dtml/transition_variables', globals())

    def manage_variables(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._variables_form(
            REQUEST,
            management_view='Variables',
            manage_tabs_message=manage_tabs_message,
        )

    def getVariableExprs(self):
        ''' get variable exprs for management UI
        '''
        ve = self.var_exprs
        if ve is None:
            return []
        else:
            ret = []
            for key in ve.keys():
                ret.append((key, self.getVarExprText(key)))
            return ret

    def getWorkflowVariables(self):
        ''' get all variables that are available form
            workflow and not handled yet.
        '''
        wf_vars = self.getAvailableVarIds()
        if self.var_exprs is None:
            return wf_vars
        ret = []
        for vid in wf_vars:
            if not self.var_exprs.has_key(vid):
                ret.append(vid)
        return ret

    def addVariable(self, id, text, REQUEST=None):
        '''
        Add a variable expression.
        '''
        if self.var_exprs is None:
            self.var_exprs = PersistentMapping()

        expr = None
        if text:
            expr = Expression(str(text))
        self.var_exprs[id] = expr

        if REQUEST is not None:
            return self.manage_variables(REQUEST, 'Variable added.')

    def deleteVariables(self, ids=[], REQUEST=None):
        ''' delete a WorkflowVariable from State
        '''
        ve = self.var_exprs
        for id in ids:
            if ve.has_key(id):
                del ve[id]

        if REQUEST is not None:
            return self.manage_variables(REQUEST, 'Variables deleted.')

    def setVariables(self, ids=[], REQUEST=None):
        ''' set values for Variables set by this state
        '''
        if self.var_exprs is None:
            self.var_exprs = PersistentMapping()

        ve = self.var_exprs

        if REQUEST is not None:
            for id in ve.keys():
                fname = 'varexpr_%s' % id

                val = REQUEST[fname]
                expr = None
                if val:
                    expr = Expression(str(REQUEST[fname]))
                ve[id] = expr

            return self.manage_variables(REQUEST, 'Variables changed.')
Example #8
0
class Link(PortalContent, DefaultDublinCoreImpl):
    """A Link.
    """

    __implements__ = (PortalContent.__implements__,
                      DefaultDublinCoreImpl.__implements__)

    meta_type = 'Link'
    URL_FORMAT = format = 'text/url'
    effective_date = expiration_date = None
    _isDiscussable = 1

    security = ClassSecurityInfo()

    def __init__(self, id, title='', remote_url='', description=''):
        DefaultDublinCoreImpl.__init__(self)
        self.id = id
        self.title = title
        self.description = description
        self._edit(remote_url)
        self.format = self.URL_FORMAT

    security.declareProtected(ModifyPortalContent, 'manage_edit')
    manage_edit = DTMLResource('dtml/zmi_editLink', globals())

    security.declareProtected(ModifyPortalContent, 'manage_editLink')

    def manage_editLink(self, remote_url, REQUEST=None):
        """
            Update the Link via the ZMI.
        """
        self._edit(remote_url)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() + '/manage_edit' +
                                         '?manage_tabs_message=Link+updated')

    security.declarePrivate('_edit')

    def _edit(self, remote_url):
        """
            Edit the Link
        """
        tokens = urlparse.urlparse(remote_url, 'http')
        if tokens[0] == 'http':
            if tokens[1]:
                # We have a nethost. All is well.
                url = urlparse.urlunparse(tokens)
            elif tokens[2:] == ('', '', '', ''):
                # Empty URL
                url = ''
            else:
                # Relative URL, keep it that way, without http:
                tokens = ('', '') + tokens[2:]
                url = urlparse.urlunparse(tokens)
        else:
            # Other scheme, keep original
            url = urlparse.urlunparse(tokens)
        self.remote_url = url

    security.declareProtected(ModifyPortalContent, 'edit')

    def edit(self, remote_url):
        """ Update and reindex. """
        self._edit(remote_url)
        self.reindexObject()

    security.declareProtected(View, 'SearchableText')

    def SearchableText(self):
        """
            text for indexing
        """
        return "%s %s" % (self.title, self.description)

    security.declareProtected(View, 'getRemoteUrl')

    def getRemoteUrl(self):
        """
            returns the remote URL of the Link
        """
        return self.remote_url

    security.declarePrivate('_writeFromPUT')

    def _writeFromPUT(self, body):
        headers = {}
        headers, body = parseHeadersBody(body, headers)
        lines = body.split('\n')
        self.edit(lines[0])
        headers['Format'] = self.URL_FORMAT
        new_subject = keywordsplitter(headers)
        headers['Subject'] = new_subject or self.Subject()
        new_contrib = contributorsplitter(headers)
        headers['Contributors'] = new_contrib or self.Contributors()
        haveheader = headers.has_key
        for key, value in self.getMetadataHeaders():
            if not haveheader(key):
                headers[key] = value

        self._editMetadata(
            title=headers['Title'],
            subject=headers['Subject'],
            description=headers['Description'],
            contributors=headers['Contributors'],
            effective_date=headers['Effective_date'],
            expiration_date=headers['Expiration_date'],
            format=headers['Format'],
            language=headers['Language'],
            rights=headers['Rights'],
        )

    ## FTP handlers
    security.declareProtected(ModifyPortalContent, 'PUT')

    def PUT(self, REQUEST, RESPONSE):
        """
            Handle HTTP / WebDAV / FTP PUT requests.
        """
        self.dav__init(REQUEST, RESPONSE)
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
        body = REQUEST.get('BODY', '')
        try:
            self._writeFromPUT(body)
            RESPONSE.setStatus(204)
            return RESPONSE
        except ResourceLockedError, msg:
            transaction.abort()
            RESPONSE.setStatus(423)
            return RESPONSE
Example #9
0
class StateDefinition(SimpleItem):
    """State definition"""

    meta_type = 'Workflow State'

    manage_options = (
        {
            'label': 'Properties',
            'action': 'manage_properties'
        },
        {
            'label': 'Permissions',
            'action': 'manage_permissions'
        },
        {
            'label': 'Groups',
            'action': 'manage_groups'
        },
        {
            'label': 'Variables',
            'action': 'manage_variables'
        },
    )

    title = ''
    description = ''
    transitions = ()  # The ids of possible transitions.
    permission_roles = None  # { permission: [role] or (role,) }
    group_roles = None  # { group name : (role,) }
    var_values = None  # PersistentMapping if set.  Overrides transition exprs.

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

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

    def getId(self):
        return self.id

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

    def getTransitions(self):
        return filter(self.getWorkflow().transitions.has_key, self.transitions)

    def getTransitionTitle(self, tid):
        t = self.getWorkflow().transitions.get(tid, None)
        if t is not None:
            return t.title
        return ''

    def getAvailableTransitionIds(self):
        return self.getWorkflow().transitions.keys()

    def getAvailableVarIds(self):
        return self.getWorkflow().variables.keys()

    def getManagedPermissions(self):
        return list(self.getWorkflow().permissions)

    def getAvailableRoles(self):
        return self.getWorkflow().getAvailableRoles()

    def getPermissionInfo(self, p):
        """Returns the list of roles to be assigned to a permission.
        """
        roles = None
        if self.permission_roles:
            roles = self.permission_roles.get(p, None)
        if roles is None:
            return {'acquired': 1, 'roles': []}
        else:
            if isinstance(roles, tuple):
                acq = 0
            else:
                acq = 1
            return {'acquired': acq, 'roles': list(roles)}

    def getGroupInfo(self, group):
        """Returns the list of roles to be assigned to a group.
        """
        if self.group_roles:
            return self.group_roles.get(group, ())
        return ()

    _properties_form = DTMLResource('dtml/state_properties', globals())

    def manage_properties(self, REQUEST, manage_tabs_message=None):
        """Show state properties ZMI form."""
        return self._properties_form(
            REQUEST,
            management_view='Properties',
            manage_tabs_message=manage_tabs_message,
        )

    def setProperties(self,
                      title='',
                      transitions=(),
                      REQUEST=None,
                      description=''):
        """Set the properties for this State."""
        self.title = str(title)
        self.description = str(description)
        self.transitions = tuple(map(str, transitions))
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')

    _variables_form = DTMLResource('dtml/state_variables', globals())

    def manage_variables(self, REQUEST, manage_tabs_message=None):
        """Show State variables ZMI form."""
        return self._variables_form(
            REQUEST,
            management_view='Variables',
            manage_tabs_message=manage_tabs_message,
        )

    def getVariableValues(self):
        """Get VariableValues for management UI."""
        vv = self.var_values
        if vv is None:
            return []
        else:
            return vv.items()

    def getWorkflowVariables(self):
        """Get all variables that are available from the workflow and
        not handled yet.
        """
        wf_vars = self.getAvailableVarIds()
        if self.var_values is None:
            return wf_vars
        ret = []
        for vid in wf_vars:
            if not self.var_values.has_key(vid):
                ret.append(vid)
        return ret

    def addVariable(self, id, value, REQUEST=None):
        """Add a WorkflowVariable to State."""
        if self.var_values is None:
            self.var_values = PersistentMapping()

        self.var_values[id] = value

        if REQUEST is not None:
            return self.manage_variables(REQUEST, 'Variable added.')

    def deleteVariables(self, ids=[], REQUEST=None):
        """Delete a WorkflowVariable from State."""
        vv = self.var_values
        for id in ids:
            if vv.has_key(id):
                del vv[id]

        if REQUEST is not None:
            return self.manage_variables(REQUEST, 'Variables deleted.')

    def setVariables(self, ids=[], REQUEST=None):
        """Set values for Variables set by this State."""
        if self.var_values is None:
            self.var_values = PersistentMapping()

        vv = self.var_values

        if REQUEST is not None:
            for id in vv.keys():
                fname = 'varval_%s' % id
                vv[id] = str(REQUEST[fname])
            return self.manage_variables(REQUEST, 'Variables changed.')

    _permissions_form = DTMLResource('dtml/state_permissions', globals())

    def manage_permissions(self, REQUEST, manage_tabs_message=None):
        """Present TTW UI for managing this State's permissions."""
        return self._permissions_form(
            REQUEST,
            management_view='Permissions',
            manage_tabs_message=manage_tabs_message,
        )

    def setPermissions(self, REQUEST):
        """Set the permissions in REQUEST for this State."""
        pr = self.permission_roles
        if pr is None:
            self.permission_roles = pr = PersistentMapping()
        pr.clear()
        for p in self.getManagedPermissions():
            roles = []
            acquired = REQUEST.get('acquire_' + p, 0)
            for r in self.getAvailableRoles():
                if REQUEST.get('%s|%s' % (p, r), 0):
                    roles.append(r)
            roles.sort()
            if not acquired:
                roles = tuple(roles)
            pr[p] = roles
        return self.manage_permissions(REQUEST, 'Permissions changed.')

    def setPermission(self, permission, acquired, roles):
        """Set a permission for this State."""
        pr = self.permission_roles
        if pr is None:
            self.permission_roles = pr = PersistentMapping()
        if acquired:
            roles = list(roles)
        else:
            roles = tuple(roles)
        pr[permission] = roles

    manage_groups = PageTemplateResource('dtml/state_groups.pt', globals())

    def setGroups(self, REQUEST, RESPONSE=None):
        """Set the group to role mappings in REQUEST for this State.
        """
        map = self.group_roles
        if map is None:
            self.group_roles = map = PersistentMapping()
        map.clear()
        all_roles = self.getWorkflow().getRoles()
        for group in self.getWorkflow().getGroups():
            roles = []
            for role in all_roles:
                if REQUEST.get('%s|%s' % (group, role), 0):
                    roles.append(role)
            roles.sort()
            roles = tuple(roles)
            map[group] = roles
        if RESPONSE is not None:
            RESPONSE.redirect(
                "%s/manage_groups?manage_tabs_message=Groups+changed." %
                self.absolute_url())
Example #10
0
class Guard(Persistent, Explicit):
    permissions = ()
    roles = ()
    groups = ()
    expr = None

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    guardForm = DTMLResource('dtml/guard', globals())

    def check(self, sm, wf_def, ob, **kw):
        """Checks conditions in this guard.
        """
        u_roles = None
        if wf_def.manager_bypass:
            # Possibly bypass.
            u_roles = sm.getUser().getRolesInContext(ob)
            if 'Manager' in u_roles:
                return 1
        if self.permissions:
            for p in self.permissions:
                if _checkPermission(p, ob):
                    break
            else:
                return 0
        if self.roles:
            # Require at least one of the given roles.
            if u_roles is None:
                u_roles = sm.getUser().getRolesInContext(ob)
            for role in self.roles:
                if role in u_roles:
                    break
            else:
                return 0
        if self.groups:
            # Require at least one of the specified groups.
            u = sm.getUser()
            b = aq_base(u)
            if hasattr(b, 'getGroupsInContext'):
                u_groups = u.getGroupsInContext(ob)
            elif hasattr(b, 'getGroups'):
                u_groups = u.getGroups()
            else:
                u_groups = ()
            for group in self.groups:
                if group in u_groups:
                    break
            else:
                return 0
        expr = self.expr
        if expr is not None:
            econtext = createExprContext(StateChangeInfo(ob, wf_def,
                                                         kwargs=kw))
            res = expr(econtext)
            if not res:
                return 0
        return 1

    security.declareProtected(ManagePortal, 'getSummary')

    def getSummary(self):
        # Perhaps ought to be in DTML.
        res = []
        if self.permissions:
            res.append('Requires permission:')
            res.append(formatNameUnion(self.permissions))
        if self.roles:
            if res:
                res.append('<br/>')
            res.append('Requires role:')
            res.append(formatNameUnion(self.roles))
        if self.groups:
            if res:
                res.append('<br/>')
            res.append('Requires group:')
            res.append(formatNameUnion(self.groups))
        if self.expr is not None:
            if res:
                res.append('<br/>')
            res.append('Requires expr:')
            res.append('<code>' + escape(self.expr.text) + '</code>')
        return ' '.join(res)

    def changeFromProperties(self, props):
        '''
        Returns 1 if changes were specified.
        '''
        if props is None:
            return 0
        res = 0
        s = props.get('guard_permissions', None)
        if s:
            res = 1
            p = [permission.strip() for permission in s.split(';')]
            self.permissions = tuple(p)
        s = props.get('guard_roles', None)
        if s:
            res = 1
            r = [role.strip() for role in s.split(';')]
            self.roles = tuple(r)
        s = props.get('guard_groups', None)
        if s:
            res = 1
            g = [group.strip() for group in s.split(';')]
            self.groups = tuple(g)
        s = props.get('guard_expr', None)
        if s:
            res = 1
            self.expr = Expression(s)
        return res

    security.declareProtected(ManagePortal, 'getPermissionsText')

    def getPermissionsText(self):
        if not self.permissions:
            return ''
        return '; '.join(self.permissions)

    security.declareProtected(ManagePortal, 'getRolesText')

    def getRolesText(self):
        if not self.roles:
            return ''
        return '; '.join(self.roles)

    security.declareProtected(ManagePortal, 'getGroupsText')

    def getGroupsText(self):
        if not self.groups:
            return ''
        return '; '.join(self.groups)

    security.declareProtected(ManagePortal, 'getExprText')

    def getExprText(self):
        if not self.expr:
            return ''
        return str(self.expr.text)
Example #11
0
class DiscussionTool(UniqueObject, SimpleItem, ActionProviderBase):
    """ Links content to discussions.
    """

    implements(IDiscussionTool)
    __implements__ = (z2IDiscussionTool, ActionProviderBase.__implements__)

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

    security = ClassSecurityInfo()

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

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLResource('dtml/explainDiscussionTool', globals())

    #
    #   'portal_discussion' interface methods
    #

    security.declarePublic('overrideDiscussionFor')

    def overrideDiscussionFor(self, content, allowDiscussion):
        """ Override discussability for the given object or clear the setting.
        """
        mtool = getToolByName(self, 'portal_membership')
        if not mtool.checkPermission(ModifyPortalContent, content):
            raise AccessControl_Unauthorized

        if allowDiscussion is None or allowDiscussion == 'None':
            if hasattr(aq_base(content), 'allow_discussion'):
                del content.allow_discussion
        else:
            content.allow_discussion = bool(allowDiscussion)

    security.declarePublic('getDiscussionFor')

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

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

        return content.talkback  # Return wrapped talkback

    security.declarePublic('isDiscussionAllowedFor')

    def isDiscussionAllowedFor(self, content):
        """ Get boolean indicating whether discussion is allowed for content.
        """
        if hasattr(content, 'allow_discussion'):
            return bool(content.allow_discussion)
        typeInfo = getToolByName(self, 'portal_types').getTypeInfo(content)
        if typeInfo:
            return bool(typeInfo.allowDiscussion())
        return False

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

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

        content.talkback = DiscussionItemContainer()
        return content.talkback
Example #12
0
class MetadataTool(UniqueObject, SimpleItem, ActionProviderBase):

    implements(IMetadataTool)
    __implements__ = (z2IMetadataTool, ActionProviderBase.__implements__)

    id = 'portal_metadata'
    meta_type = 'Default Metadata Tool'

    #
    #   Default values.
    #
    publisher = ''
    element_specs = None
    #initial_values_hook = None
    #validation_hook     = None

    security = ClassSecurityInfo()

    def __init__(
            self,
            publisher=None
        #, initial_values_hook=None
        #, validation_hook=None
        ,
            element_specs=DEFAULT_ELEMENT_SPECS):

        self.editProperties(publisher
                            #, initial_values_hook
                            #, validation_hook
                            )

        self.element_specs = PersistentMapping()

        for name, is_multi_valued in element_specs:
            self.element_specs[name] = ElementSpec(is_multi_valued)

    #
    #   ZMI methods
    #
    manage_options = (
        ActionProviderBase.manage_options +
        ({
            'label': 'Overview',
            'action': 'manage_overview'
        }, {
            'label': 'Properties',
            'action': 'propertiesForm'
        }, {
            'label': 'Elements',
            'action': 'elementPoliciesForm'
        }
         # TODO     , { 'label'      : 'Types'
         #            , 'action'     : 'typesForm'
         #            }
         ) + SimpleItem.manage_options)

    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLResource('dtml/explainMetadataTool', globals())

    security.declareProtected(ManagePortal, 'propertiesForm')
    propertiesForm = DTMLResource('dtml/metadataProperties', globals())

    security.declareProtected(ManagePortal, 'editProperties')

    def editProperties(
            self,
            publisher=None
        # TODO , initial_values_hook=None
        # TODO , validation_hook=None
        ,
            REQUEST=None):
        """
            Form handler for "tool-wide" properties (including list of
            metadata elements).
        """
        if publisher is not None:
            self.publisher = publisher

        # TODO self.initial_values_hook = initial_values_hook
        # TODO self.validation_hook = validation_hook

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

    security.declareProtected(ManagePortal, 'elementPoliciesForm')
    elementPoliciesForm = DTMLResource('dtml/metadataElementPolicies',
                                       globals())

    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 ('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 managed by the tool.
        """
        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 representing the tool's knowledge
            of '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 self.element_specs.has_key(element):
            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, or the default
            if None.
        """
        result = []
        for element, spec in self.listElementSpecs():
            result.append((element, spec.getPolicy(typ)))
        return result

    #
    #   'portal_metadata' interface
    #
    security.declarePrivate('getFullName')

    def getFullName(self, userid):
        """
            Convert an internal userid to a "formal" name, if
            possible, perhaps using the 'portal_membership' tool.

            Used to map userid's for Creator, Contributor DCMI
            queries.
        """
        return userid  # TODO: do lookup here

    security.declarePublic('getPublisher')

    def getPublisher(self):
        """
            Return the "formal" name of the publisher of the
            portal.
        """
        return self.publisher

    security.declarePublic('listAllowedVocabulary')

    def listAllowedVocabulary(self, element, content=None, content_type=None):
        """
            List allowed keywords for a given portal_type, or all
            possible keywords if none supplied.
        """
        spec = self.getElementSpec(element)
        if content_type is None and content:
            content_type = content.getPortalTypeName()
        return spec.getPolicy(content_type).allowedVocabulary()

    security.declarePublic('listAllowedSubjects')

    def listAllowedSubjects(self, content=None, content_type=None):
        """
            List allowed keywords for a given portal_type, or all
            possible keywords if none supplied.
        """
        return self.listAllowedVocabulary('Subject', content, content_type)

    security.declarePublic('listAllowedFormats')

    def listAllowedFormats(self, content=None, content_type=None):
        """
            List the allowed 'Content-type' values for a particular
            portal_type, or all possible formats if none supplied.
        """
        return self.listAllowedVocabulary('Format', content, content_type)

    security.declarePublic('listAllowedLanguages')

    def listAllowedLanguages(self, content=None, content_type=None):
        """
            List the allowed language values.
        """
        return self.listAllowedVocabulary('Language', content, content_type)

    security.declarePublic('listAllowedRights')

    def listAllowedRights(self, content=None, content_type=None):
        """
            List the allowed values for a "Rights"
            selection list;  this gets especially important where
            syndication is involved.
        """
        return self.listAllowedVocabulary('Rights', content, content_type)

    security.declareProtected(ModifyPortalContent, 'setInitialMetadata')

    def setInitialMetadata(self, content):
        """
            Set initial values for content metatdata, supplying
            any site-specific defaults.
        """
        for element, policy in self.listPolicies(content.getPortalTypeName()):

            if not getattr(content, element)():

                if policy.supplyDefault():
                    setter = getattr(content, 'set%s' % element)
                    setter(policy.defaultValue())
                elif policy.isRequired():
                    raise MetadataError, \
                          'Metadata element %s is required.' % element

        # TODO:  Call initial_values_hook, if present

    security.declareProtected(View, 'validateMetadata')

    def validateMetadata(self, content):
        """
            Enforce portal-wide policies about DCI, e.g.,
            requiring non-empty title/description, etc.  Called
            by the CMF immediately before saving changes to the
            metadata of an object.
        """
        for element, policy in self.listPolicies(content.getPortalTypeName()):

            value = getattr(content, element)()
            if not value and policy.isRequired():
                raise MetadataError, \
                        'Metadata element %s is required.' % element

            if value and policy.enforceVocabulary():
                values = policy.isMultiValued() and value or [value]
                for value in values:
                    if not value in policy.allowedVocabulary():
                        raise MetadataError, \
                        'Value %s is not in allowed vocabulary for ' \
                        'metadata element %s.' % ( value, element )
Example #13
0
class MembershipTool(BaseTool):
    """ Implement 'portal_membership' interface using "stock" policies.
    """

    implements(IMembershipTool)
    __implements__ = (z2IMembershipTool, ActionProviderBase.__implements__)

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

    security = ClassSecurityInfo()

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLResource('dtml/explainMembershipTool', globals())

    security.declareProtected(ManagePortal, 'manage_mapRoles')
    manage_mapRoles = DTMLResource('dtml/membershipRolemapping', globals())

    security.declareProtected(ManagePortal, 'manage_setMembersFolderById')

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

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

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

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

    security.declareProtected(ManagePortal, 'setMembersFolderById')

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

    security.declarePublic('getMembersFolder')

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

    security.declarePublic('createMemberArea')

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

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

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

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

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

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

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

    security.declarePublic('createMemberarea')
    createMemberarea = createMemberArea

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

    def getHomeUrl(self, id=None, verifyPermission=0):
        """ Return the URL to a member's home folder, or None.
        """
        home = self.getHomeFolder(id, verifyPermission)
        if home is not None:
            return home.absolute_url()
        else:
            return None
Example #14
0
class VariableDefinition(SimpleItem):
    """Variable definition"""

    meta_type = 'Workflow Variable'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    description = ''
    for_catalog = 1
    for_status = 1
    default_value = ''
    default_expr = None  # Overrides default_value if set
    info_guard = None
    update_always = 1

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

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

    def getDefaultExprText(self):
        if not self.default_expr:
            return ''
        else:
            return self.default_expr.text

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

    def getInfoGuardSummary(self):
        res = None
        if self.info_guard is not None:
            res = self.info_guard.getSummary()
        return res

    _properties_form = DTMLResource('dtml/variable_properties', globals())

    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,
                      default_value='', default_expr='',
                      for_catalog=0, for_status=0,
                      update_always=0,
                      props=None, REQUEST=None):
        '''
        '''
        self.description = str(description)
        self.default_value = str(default_value)
        if default_expr:
            self.default_expr = Expression(default_expr)
        else:
            self.default_expr = None

        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.info_guard = g
        else:
            self.info_guard = None
        self.for_catalog = bool(for_catalog)
        self.for_status = bool(for_status)
        self.update_always = bool(update_always)
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')
Example #15
0
class SyndicationTool(UniqueObject, SimpleItem, ActionProviderBase):
    """
        The syndication tool manages the site-wide policy for
        syndication of folder content as RSS.
    """

    __implements__ = ActionProviderBase.__implements__

    id = 'portal_syndication'
    meta_type = 'Default Syndication Tool'

    security = ClassSecurityInfo()

    #Default Sitewide Values
    isAllowed = 0
    syUpdatePeriod = 'daily'
    syUpdateFrequency = 1
    syUpdateBase = DateTime()
    max_items = 15

    #ZMI Methods
    manage_options = (
        ActionProviderBase.manage_options +
        ({
            'label': 'Overview',
            'action': 'overview',
            'help': ('CMFDefault', 'Syndication-Tool_Overview.stx')
        }, {
            'label': 'Properties',
            'action': 'propertiesForm',
            'help': ('CMFDefault', 'Syndication-Tool_Properties.stx')
        }, {
            'label': 'Policies',
            'action': 'policiesForm',
            'help': ('CMFDefault', 'Syndication-Tool_Policies.stx')
        }, {
            'label': 'Reports',
            'action': 'reportForm',
            'help': ('CMFDefault', 'Syndication-Tool_Reporting.stx')
        }))

    security.declareProtected(ManagePortal, 'overview')
    overview = DTMLResource('dtml/synOverview', globals())

    security.declareProtected(ManagePortal, 'propertiesForm')
    propertiesForm = DTMLResource('dtml/synProps', globals())

    security.declareProtected(ManagePortal, 'policiesForm')
    policiesForm = DTMLResource('dtml/synPolicies', globals())

    security.declareProtected(ManagePortal, 'reportForm')
    reportForm = DTMLResource('dtml/synReports', globals())

    security.declareProtected(ManagePortal, 'editProperties')

    def editProperties(self,
                       updatePeriod=None,
                       updateFrequency=None,
                       updateBase=None,
                       isAllowed=None,
                       max_items=None,
                       REQUEST=None):
        """
        Edit the properties for the SystemWide defaults on the
        SyndicationTool.
        """
        if isAllowed is not None:
            self.isAllowed = isAllowed

        if updatePeriod is not None:
            self.syUpdatePeriod = updatePeriod
        else:
            try:
                del self.syUpdatePeriod
            except (AttributeError, KeyError):
                pass

        if updateFrequency is not None:
            self.syUpdateFrequency = int(updateFrequency)
        else:
            try:
                del self.syUpdateFrequency
            except (AttributeError, KeyError):
                pass

        if updateBase is not None:
            if type(updateBase) is type(''):
                updateBase = DateTime(updateBase)
            self.syUpdateBase = updateBase
        else:
            try:
                del self.syUpdateBase
            except (AttributeError, KeyError):
                pass

        if max_items is not None:
            self.max_items = int(max_items)
        else:
            try:
                del self.max_items
            except (AttributeError, KeyError):
                pass

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

    security.declarePublic('editSyInformationProperties')

    def editSyInformationProperties(self,
                                    obj,
                                    updatePeriod=None,
                                    updateFrequency=None,
                                    updateBase=None,
                                    max_items=None,
                                    REQUEST=None):
        """
        Edit syndication properties for the obj being passed in.
        These are held on the syndication_information object.
        Not Sitewide Properties.
        """
        if not _checkPermission(ManageProperties, obj):
            raise AccessControl_Unauthorized

        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is None:
            raise 'Syndication is Disabled'

        if updatePeriod is not None:
            syInfo.syUpdatePeriod = updatePeriod
        else:
            syInfo.syUpdatePeriod = self.syUpdatePeriod

        if updateFrequency is not None:
            syInfo.syUpdateFrequency = int(updateFrequency)
        else:
            syInfo.syUpdateFrequency = self.syUpdateFrequency

        if updateBase is not None:
            if type(updateBase) is type(''):
                updateBase = DateTime(updateBase)
            syInfo.syUpdateBase = updateBase
        else:
            syInfo.syUpdateBase = self.syUpdateBase

        if max_items is not None:
            syInfo.max_items = int(max_items)
        else:
            syInfo.max_items = self.max_items

    security.declarePublic('enableSyndication')

    def enableSyndication(self, obj):
        """
        Enable syndication for the obj
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is Disabled'

        if hasattr(aq_base(obj), 'syndication_information'):
            raise 'Syndication Information Exists'

        syInfo = SyndicationInformation()
        obj._setObject('syndication_information', syInfo)
        syInfo = obj._getOb('syndication_information')
        syInfo.syUpdatePeriod = self.syUpdatePeriod
        syInfo.syUpdateFrequency = self.syUpdateFrequency
        syInfo.syUpdateBase = self.syUpdateBase
        syInfo.max_items = self.max_items
        syInfo.description = "Channel Description"

    security.declarePublic('disableSyndication')

    def disableSyndication(self, obj):
        """
        Disable syndication for the obj; and remove it.
        """
        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is None:
            raise 'This object does not have Syndication Information'

        obj._delObject('syndication_information')

    security.declarePublic('getSyndicatableContent')

    def getSyndicatableContent(self, obj):
        """
        An interface for allowing folderish items to implement an
        equivalent of PortalFolderBase.contentValues()
        """
        if hasattr(obj, 'synContentValues'):
            values = obj.synContentValues()
        else:
            values = PortalFolderBase.contentValues(obj)
        return values

    security.declarePublic('buildUpdatePeriods')

    def buildUpdatePeriods(self):
        """
        Return a list of possible update periods for the xmlns: sy
        """
        updatePeriods = (('hourly', 'Hourly'), ('daily', 'Daily'), ('weekly',
                                                                    'Weekly'),
                         ('monthly', 'Monthly'), ('yearly', 'Yearly'))
        return updatePeriods

    security.declarePublic('isSiteSyndicationAllowed')

    def isSiteSyndicationAllowed(self):
        """
        Return sitewide syndication policy
        """
        return self.isAllowed

    security.declarePublic('isSyndicationAllowed')

    def isSyndicationAllowed(self, obj=None):
        """
        Check whether syndication is enabled for the site.  This
        provides for extending the method to check for whether a
        particular obj is enabled, allowing for turning on only
        specific folders for syndication.
        """
        syInfo = getattr(aq_base(obj), 'syndication_information', None)
        if syInfo is None:
            return 0
        else:
            return self.isSiteSyndicationAllowed()

    security.declarePublic('getUpdatePeriod')

    def getUpdatePeriod(self, obj=None):
        """
        Return the update period for the RSS syn namespace.
        This is either on the object being passed or the
        portal_syndication tool (if a sitewide value or default
        is set)

        NOTE:  Need to add checks for sitewide policies!!!
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is Not Allowed'

        if obj is None:
            return self.syUpdatePeriod

        syInfo = getattr(obj, 'syndication_information', None)

        if syInfo is not None:
            return syInfo.syUpdatePeriod
        else:
            return 'Syndication is Not Allowed'

    security.declarePublic('getUpdateFrequency')

    def getUpdateFrequency(self, obj=None):
        """
        Return the update frequency (as a positive integer) for
        the syn namespace.  This is either on the object being
        pass or the portal_syndication tool (if a sitewide value
        or default is set).

        Note:  Need to add checks for sitewide policies!!!
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            return self.syUpdateFrequency

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            return syInfo.syUpdateFrequency
        else:
            return 'Syndication is not Allowed'

    security.declarePublic('getUpdateBase')

    def getUpdateBase(self, obj=None):
        """
        Return the base date to be used with the update frequency
        and the update period to calculate a publishing schedule.

        Note:  I'm not sure what's best here, creation date, last
        modified date (of the folder being syndicated) or some
        arbitrary date.  For now, I'm going to build a updateBase
        time from zopetime and reformat it to meet the W3CDTF.
        Additionally, sitewide policy checks might have a place
        here...
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            when = self.syUpdateBase
            return when.ISO()

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            when = syInfo.syUpdateBase
            return when.ISO()
        else:
            return 'Syndication is not Allowed'

    security.declarePublic('getHTML4UpdateBase')

    def getHTML4UpdateBase(self, obj):
        """
        Return HTML4 formated UpdateBase DateTime
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            when = syUpdateBase
            return when.HTML4()

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            when = syInfo.syUpdateBase
            return when.HTML4()
        else:
            return 'Syndication is not Allowed'

    def getMaxItems(self, obj=None):
        """
        Return the max_items to be displayed in the syndication
        """
        if not self.isSiteSyndicationAllowed():
            raise 'Syndication is not Allowed'

        if obj is None:
            return self.max_items

        syInfo = getattr(obj, 'syndication_information', None)
        if syInfo is not None:
            return syInfo.max_items
        else:
            return 'Syndication is not Allowed'
Example #16
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_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):
                # Old version, convert it.
                matches = (matches, )
                self.var_matches[id] = matches
            return matches
        else:
            return ()

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

    _properties_form = DTMLResource('dtml/worklist_properties', globals())

    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',
                      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()
                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)
        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.')
Example #17
0
class Document(PortalContent, DefaultDublinCoreImpl):
    """A Document - Handles both StructuredText and HTML.
    """

    implements(IDocument, IMutableDocument)
    __implements__ = (z2IDocument, z2IMutableDocument,
                      PortalContent.__implements__,
                      DefaultDublinCoreImpl.__implements__)

    meta_type = 'Document'
    effective_date = expiration_date = None
    cooked_text = text = text_format = ''
    _size = 0
    _isDiscussable = 1

    _stx_level = 1  # Structured text level

    _last_safety_belt_editor = ''
    _last_safety_belt = ''
    _safety_belt = ''

    security = ClassSecurityInfo()

    def __init__(self, id, title='', description='', text_format='', text=''):
        DefaultDublinCoreImpl.__init__(self)
        self.id = id
        self.title = title
        self.description = description
        self._edit(text=text, text_format=text_format)
        self.setFormat(text_format)

    security.declareProtected(ModifyPortalContent, 'manage_edit')
    manage_edit = DTMLResource('dtml/zmi_editDocument', globals())

    security.declareProtected(ModifyPortalContent, 'manage_editDocument')

    def manage_editDocument(self, text, text_format, file='', REQUEST=None):
        """ A ZMI (Zope Management Interface) level editing method """
        Document.edit(self, text_format=text_format, text=text, file=file)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/manage_edit' +
                '?manage_tabs_message=Document+updated')

    def _edit(self, text, text_format='', safety_belt=''):
        """ Edit the Document and cook the body.
        """
        if not self._safety_belt_update(safety_belt=safety_belt):
            msg = ("Intervening changes from elsewhere detected."
                   " Please refetch the document and reapply your changes."
                   " (You may be able to recover your version using the"
                   " browser 'back' button, but will have to apply them"
                   " to a freshly fetched copy.)")
            raise EditingConflict(msg)

        self.text = text
        self._size = len(text)

        if not text_format:
            text_format = self.text_format
        if text_format == 'html':
            self.cooked_text = text
        elif text_format == 'plain':
            self.cooked_text = html_quote(text).replace('\n', '<br />')
        else:
            self.cooked_text = HTML(text, level=self._stx_level, header=0)

    #
    #   IMutableDocument method
    #

    security.declareProtected(ModifyPortalContent, 'edit')

    def edit(self, text_format, text, file='', safety_belt=''):
        """ Update the document.

        To add webDav support, we need to check if the content is locked, and if
        so return ResourceLockedError if not, call _edit.

        Note that this method expects to be called from a web form, and so
        disables header processing
        """
        self.failIfLocked()
        if file and (type(file) is not type('')):
            contents = file.read()
            if contents:
                text = contents
        if html_headcheck(text) and text_format.lower() != 'plain':
            text = bodyfinder(text)
        self.setFormat(text_format)
        self._edit(text=text, text_format=text_format, safety_belt=safety_belt)
        self.reindexObject()

    security.declareProtected(ModifyPortalContent, 'setMetadata')

    def setMetadata(self, headers):
        headers['Format'] = self.Format()
        new_subject = keywordsplitter(headers)
        headers['Subject'] = new_subject or self.Subject()
        new_contrib = contributorsplitter(headers)
        headers['Contributors'] = new_contrib or self.Contributors()
        haveheader = headers.has_key
        for key, value in self.getMetadataHeaders():
            if not haveheader(key):
                headers[key] = value
        self._editMetadata(
            title=headers['Title'],
            subject=headers['Subject'],
            description=headers['Description'],
            contributors=headers['Contributors'],
            effective_date=headers['Effective_date'],
            expiration_date=headers['Expiration_date'],
            format=headers['Format'],
            language=headers['Language'],
            rights=headers['Rights'],
        )

    security.declarePrivate('guessFormat')

    def guessFormat(self, text):
        """ Simple stab at guessing the inner format of the text """
        if html_headcheck(text): return 'html'
        else: return 'structured-text'

    security.declarePrivate('handleText')

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

    security.declarePublic('getMetadataHeaders')

    def getMetadataHeaders(self):
        """Return RFC-822-style header spec."""
        hdrlist = DefaultDublinCoreImpl.getMetadataHeaders(self)
        hdrlist.append(('SafetyBelt', self._safety_belt))
        return hdrlist

    security.declarePublic('SafetyBelt')

    def SafetyBelt(self):
        """Return the current safety belt setting.
        For web form hidden button."""
        return self._safety_belt

    def _safety_belt_update(self, safety_belt=''):
        """Check validity of safety belt and update tracking if valid.

        Return 0 if safety belt is invalid, 1 otherwise.

        Note that the policy is deliberately lax if no safety belt value is
        present - "you're on your own if you don't use your safety belt".

        When present, either the safety belt token:
         - ... is the same as the current one given out, or
         - ... is the same as the last one given out, and the person doing the
           edit is the same as the last editor."""

        this_belt = safety_belt
        this_user = getSecurityManager().getUser().getId()

        if (  # we have a safety belt value:
                this_belt
                # and the current object has a safety belt (ie - not freshly made)
                and (self._safety_belt is not None)
                # and the safety belt doesn't match the current one:
                and (this_belt != self._safety_belt)
                # and safety belt and user don't match last safety belt and user:
                and not ((this_belt == self._last_safety_belt) and
                         (this_user == self._last_safety_belt_editor))):
            # Fail.
            return 0

        # We qualified - either:
        #  - the edit was submitted with safety belt stripped, or
        #  - the current safety belt was used, or
        #  - the last one was reused by the last person who did the last edit.
        # In any case, update the tracking.

        self._last_safety_belt_editor = this_user
        self._last_safety_belt = this_belt
        self._safety_belt = str(self._p_mtime)

        return 1

    ### Content accessor methods

    #
    #   IContentish method
    #

    security.declareProtected(View, 'SearchableText')

    def SearchableText(self):
        """ Used by the catalog for basic full text indexing """
        return "%s %s %s" % (self.Title(), self.Description(),
                             self.EditableBody())

    #
    #   IDocument methods
    #

    security.declareProtected(View, 'CookedBody')

    def CookedBody(self, stx_level=None, setlevel=0):
        """ Get the "cooked" (ready for presentation) form of the text.

        The prepared basic rendering of an object.  For Documents, this
        means pre-rendered structured text, or what was between the
        <BODY> tags of HTML.

        If the format is html, and 'stx_level' is not passed in or is the
        same as the object's current settings, return the cached cooked
        text.  Otherwise, recook.  If we recook and 'setlevel' is true,
        then set the recooked text and stx_level on the object.
        """
        if (self.text_format == 'html' or self.text_format == 'plain'
                or (stx_level is None) or (stx_level == self._stx_level)):
            return self.cooked_text
        else:
            cooked = HTML(self.text, level=stx_level, header=0)
            if setlevel:
                self._stx_level = stx_level
                self.cooked_text = cooked
            return cooked

    security.declareProtected(View, 'EditableBody')

    def EditableBody(self):
        """ Get the "raw" (as edited) form of the text.

        The editable body of text.  This is the raw structured text, or
        in the case of HTML, what was between the <BODY> tags.
        """
        return self.text

    #
    #   IDublinCore method
    #

    security.declareProtected(View, 'Format')

    def Format(self):
        """ Dublin Core Format element - resource format.
        """
        if self.text_format == 'html':
            return 'text/html'
        else:
            return 'text/plain'

    #
    #   IMutableDublinCore method
    #

    security.declareProtected(ModifyPortalContent, 'setFormat')

    def setFormat(self, format):
        """ Set text format and Dublin Core resource format.
        """
        value = str(format)
        old_value = self.text_format

        if value == 'text/html' or value == 'html':
            self.text_format = 'html'
        elif value == 'text/plain':
            if self.text_format not in ('structured-text', 'plain'):
                self.text_format = 'structured-text'
        elif value == 'plain':
            self.text_format = 'plain'
        else:
            self.text_format = 'structured-text'

        # Did the format change? We might need to re-cook the content.
        if value != old_value:
            if html_headcheck(self.text) and value != 'plain':
                self.text = bodyfinder(self.text)

            self._edit(self.text,
                       text_format=self.text_format,
                       safety_belt=self._safety_belt)

    ## FTP handlers
    security.declareProtected(ModifyPortalContent, 'PUT')

    def PUT(self, REQUEST, RESPONSE):
        """ Handle HTTP (and presumably FTP?) PUT requests """
        self.dav__init(REQUEST, RESPONSE)
        self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
        body = REQUEST.get('BODY', '')
        headers, body, format = self.handleText(text=body)
        safety_belt = headers.get('SafetyBelt', '')
        if REQUEST.get_header('Content-Type', '') == 'text/html':
            text_format = 'html'
        else:
            text_format = format

        try:
            self.setFormat(text_format)
            self.setMetadata(headers)
            self._edit(text=body, safety_belt=safety_belt)
        except EditingConflict, msg:
            # XXX Can we get an error msg through?  Should we be raising an
            #     exception, to be handled in the FTP mechanism?  Inquiring
            #     minds...
            transaction.abort()
            RESPONSE.setStatus(450)
            return RESPONSE
        except ResourceLockedError, msg:
            transaction.abort()
            RESPONSE.setStatus(423)
            return RESPONSE