Beispiel #1
0
 def set_state(self, version_id, state, principal=None):
     """Set state of given version"""
     if str(version_id) not in self:
         return
     # update version state
     version = self[str(version_id)]
     wf_state = IWorkflowState(version)
     wf_state.version_id = version_id
     wf_state.state = state
     if principal is not None:
         wf_state.state_principal = principal
     # update versions/states mapping
     if state is None:
         state = NONE_STATE
     old_state = self.state_by_version[version_id]
     versions = self.versions_by_state[old_state]
     if version_id in versions:
         versions.remove(version_id)
         if versions:
             self.versions_by_state[old_state] = versions
         else:
             del self.versions_by_state[old_state]
     self.state_by_version[version_id] = state
     versions = self.versions_by_state.get(state, [])
     versions.append(version_id)
     self.versions_by_state[state] = versions
Beispiel #2
0
def handle_workflow_version_transition(event):
    """Handle workflow version transition"""
    principal = event.principal
    factory = get_object_factory(IWorkflowStateHistoryItem)
    if factory is not None:
        item = factory(date=gmtime(datetime.utcnow()),
                       source_version=IWorkflowState(
                           event.old_object).version_id,
                       source_state=event.source,
                       target_state=event.destination,
                       transition_id=event.transition.transition_id,
                       principal=principal.id
                       if IPrincipalInfo.providedBy(principal) else principal,
                       comment=event.comment)
        IWorkflowState(event.object).history.append(item)  # pylint: disable=no-member
Beispiel #3
0
 def label(self):
     """Label getter"""
     translate = self.request.localizer.translate
     state = IWorkflowState(self.context)
     return translate(_("Version {id} - {state}")).format(
         id=state.version_id,
         state=translate(self.workflow.get_state_label(state.state)))
Beispiel #4
0
 def create_and_add(self, data):
     data = data.get(self, {})
     info = IWorkflowInfo(self.context)
     result = info.fire_transition(self.transition.transition_id, comment=data.get('comment'))
     info.fire_automatic()
     IWorkflowState(self.context).state_urgency = data.get('urgent_request') or False
     self.request.registry.notify(ObjectModifiedEvent(self.context))
     return result or info
Beispiel #5
0
def delete_action(wf, context):  # pylint: disable=invalid-name,unused-argument
    """Delete draft version, and parent if single version"""
    versions = IWorkflowVersions(context)
    versions.remove_version(IWorkflowState(context).version_id)
    if not versions.get_last_versions():
        document = get_parent(versions, IDocument)
        folder = get_parent(document, IDocumentFolder)
        del folder[document.__name__]
Beispiel #6
0
 def fire_transition(self,
                     transition_id,
                     comment=None,
                     side_effect=None,
                     check_security=True,
                     principal=None,
                     request=None):
     # pylint: disable=too-many-arguments
     """Fire transition with given ID"""
     versions = IWorkflowVersions(self.parent)
     state = IWorkflowState(self.context)
     if request is None:
         request = check_request()
     # this raises InvalidTransitionError if id is invalid for current state
     transition = self.wf.get_transition(state.state, transition_id)
     # check whether we may execute this workflow transition
     if check_security and transition.permission:
         if not request.has_permission(transition.permission,
                                       context=self.context):
             raise HTTPUnauthorized()
     # now make sure transition can still work in this context
     if not transition.condition(self, self.context):
         raise ConditionFailedError()
     # perform action, return any result as new version
     if principal is None:
         principal = request.principal
     result = transition.action(self, self.context)
     if result is not None:
         # stamp it with version
         versions.add_version(result, transition.destination, principal)
         # execute any side effect:
         if side_effect is not None:
             side_effect(result)
         event = WorkflowVersionTransitionEvent(result, self.wf, principal,
                                                self.context,
                                                transition.source,
                                                transition.destination,
                                                transition, comment)
     else:
         versions.set_state(state.version_id, transition.destination,
                            principal)
         # execute any side effect
         if side_effect is not None:
             side_effect(self.context)
         event = WorkflowTransitionEvent(self.context, self.wf, principal,
                                         transition.source,
                                         transition.destination, transition,
                                         comment)
     # change state of context or new object
     registry = request.registry
     registry.notify(event)
     # send modified event for original or new object
     if result is None:
         registry.notify(ObjectModifiedEvent(self.context))
     else:
         registry.notify(ObjectModifiedEvent(result))
     return result
Beispiel #7
0
 def _get_transitions(self, trigger):
     """Retrieve all possible transitions from workflow utility"""
     state = IWorkflowState(self.context)
     transitions = self.wf.get_transitions(state.state)
     # now filter these transitions to retrieve all possible
     # transitions in this context, and return their ids
     return [
         transition for transition in transitions
         if transition.trigger == trigger
     ]
Beispiel #8
0
 def remove_version(self,
                    version_id,
                    state='deleted',
                    comment=None,
                    principal=None,
                    request=None):
     # pylint: disable=too-many-arguments
     """Remove version with given ID"""
     if str(version_id) not in self:
         return
     # update version state
     version = self[str(version_id)]
     wf_state = IWorkflowState(version)
     if comment:
         if request is None:
             request = check_request()
         translate = request.localizer.translate
         workflow = get_utility(
             IWorkflow,
             name=get_parent(self, IWorkflowManagedContent).workflow_name)
         item = WorkflowHistoryItem(
             date=datetime.utcnow(),
             source_version=wf_state.version_id,
             source_state=translate(
                 workflow.states.getTerm(wf_state.state).title),
             target_state=translate(workflow.states.getTerm(state).title),
             principal=request.principal.id,
             comment=comment)
         wf_state.history.append(item)  # pylint: disable=no-member
     wf_state.state = state
     if principal is not None:
         wf_state.state_principal = principal
     # remove given version
     state = self.state_by_version[version_id]
     versions = self.versions_by_state[state]
     versions.remove(version_id)
     if versions:
         self.versions_by_state[state] = versions
     else:
         del self.versions_by_state[state]
     del self.state_by_version[version_id]
     self.deleted[version_id] = self[str(version_id)]
     del self[str(version_id)]
Beispiel #9
0
 def edit_permission(self):
     """Document edit permission getter"""
     document = self.context
     request = self.request
     if request.has_permission(MANAGE_APPLICATION_PERMISSION, context=self):
         return MANAGE_APPLICATION_PERMISSION
     state = IWorkflowState(document)
     if state in ZFILES_WORKFLOW.visible_states:
         return FORBIDDEN_PERMISSION
     return MANAGE_DOCUMENT_PERMISSION
Beispiel #10
0
def set_file_properties(request, oid, properties, version=None):
    """Set document properties"""
    container = get_utility(IDocumentContainer)
    document = container.update_document(oid,
                                         version,
                                         properties=properties,
                                         request=request)
    if document is None:
        return None
    state = IWorkflowState(document)
    return {'version': state.version_id}
Beispiel #11
0
def handle_cloned_object(event):
    """Add comment when an object is cloned"""
    request = check_request()
    translate = request.localizer.translate
    source_state = IWorkflowState(event.source)
    factory = get_object_factory(IWorkflowStateHistoryItem)
    if factory is not None:
        item = factory(
            date=datetime.utcnow(),
            principal=request.principal.id,
            comment=translate(
                _("Clone created from version {source} (in « {state} » "
                  "state)")).format(source=source_state.version_id,
                                    state=translate(
                                        IWorkflow(
                                            event.source).get_state_label(
                                                source_state.state))))
        target_state = IWorkflowState(event.object)
        target_state.history.clear()  # pylint: disable=no-member
        target_state.history.append(item)  # pylint: disable=no-member
Beispiel #12
0
 def add_version(self, content, state, principal=None):
     """Add new version to versions list"""
     self.last_version_id += 1
     version_id = self.last_version_id
     # init version state
     alsoProvides(content, IWorkflowVersion)
     wf_state = IWorkflowState(content)
     wf_state.version_id = version_id
     wf_state.state = state
     if principal is not None:
         wf_state.state_principal = principal
     # store new version
     if state is None:
         state = NONE_STATE
     self[str(version_id)] = content
     self.state_by_version[version_id] = state
     versions = self.versions_by_state.get(state, [])
     versions.append(version_id)
     self.versions_by_state[state] = versions
     return version_id
Beispiel #13
0
 def visible_publication_date(self):
     """Visible publication date getter"""
     displayed_date = self.displayed_publication_date
     if displayed_date == DISPLAY_FIRST_VERSION:
         state = IWorkflowState(self.__parent__, None)
         if (state is not None) and (state.version_id > 1):
             versions = IWorkflowVersions(self.__parent__, None)
             if versions is not None:
                 version = versions.get_version(1)  # pylint: disable=assignment-from-no-return
                 return IWorkflowPublicationInfo(
                     version).publication_effective_date
     return self.publication_effective_date
Beispiel #14
0
def set_file_data(request, oid, data, properties=None, version=None):
    """Set document data"""
    container = get_utility(IDocumentContainer)
    if isinstance(data, Binary):
        data = data.data
    else:
        data = base64.b64decode(data)
    document = container.update_document(oid, version, data, properties,
                                         request)
    if document is None:
        return None
    state = IWorkflowState(document)
    return {'version': state.version_id}
Beispiel #15
0
 def _get_viewlets(self):
     translate = self.request.localizer.translate
     for version in IWorkflowVersions(
             self.context).get_last_versions(count=0):
         state = IWorkflowState(version)
         item = MenuItem(version, self.request, self.view, self)
         item.label = translate(_("Version {id} - {state}")).format(
             id=state.version_id,
             state=translate(self.workflow.get_state_label(state.state)))
         item.icon_class = 'fas fa-arrow-right'
         if version is self.context:
             item.css_class = 'bg-primary text-white'
         item.href = absolute_url(version, self.request,
                                  'admin#{}'.format(self.request.view_name))
         yield 'version_{}'.format(state.version_id), item
Beispiel #16
0
 def get_label(content, request=None, format=True):  # pylint: disable=redefined-builtin
     """Workflow state label getter"""
     if request is None:
         request = check_request()
     translate = request.localizer.translate
     state = IWorkflowState(content)
     header = STATES_HEADERS.get(state.state)
     if header is not None:
         state_label = translate(header)
         if format:
             state_label = translate(_('{state} {date}')).format(
                 state=state_label, date=format_datetime(state.state_date))
     else:
         state_label = translate(_("Unknown state"))
     return state_label
Beispiel #17
0
def publish_action(wf, context):  # pylint: disable=invalid-name,unused-argument
    """Publish version"""
    request = check_request()
    translate = request.localizer.translate
    publication_info = IWorkflowPublicationInfo(context)
    publication_info.publication_date = datetime.utcnow()
    publication_info.publisher = request.principal.id
    version_id = IWorkflowState(context).version_id
    for version in IWorkflowVersions(context).get_versions(
        (PUBLISHED_STATE, )):
        if version is not context:
            IWorkflowInfo(version).fire_transition_toward(
                ARCHIVED_STATE,
                comment=translate(
                    _("Published version {0}")).format(version_id))
Beispiel #18
0
 def update_document(self,
                     oid,
                     version=None,
                     data=None,
                     properties=None,
                     request=None,
                     check_permission=True):
     # pylint: disable=too-many-arguments
     """Update document data or properties"""
     if not oid:
         return None
     document = self.get_document(oid, version)
     if document is None:
         raise HTTPNotFound()
     if request is None:
         request = check_request()
     if check_permission and \
             not request.has_permission(MANAGE_DOCUMENT_PERMISSION, context=document):
         raise HTTPForbidden()
     if properties is None:
         properties = {}
     if data is not None:
         document_hash = get_hash(data)
         if document_hash == document.hash:
             # unmodified file content
             data = None
             _filename = properties.pop('filename', None)
         else:
             # modified file content, check state and create new version if required
             if request is None:
                 request = check_request()
             state = IWorkflowState(document)
             if state.state != DRAFT_STATE:
                 translate = request.localizer.translate
                 workflow_info = IWorkflowInfo(document)
                 document = workflow_info.fire_transition_toward(  # pylint: disable=assignment-from-no-return
                     DRAFT_STATE,
                     comment=translate(_("Document content update")),
                     request=request)
                 request.response.status = HTTPCreated.code
     state = document.update(data, properties)
     request.registry.notify(ObjectModifiedEvent(document))
     if state.state == DELETED_STATE:
         return None
     return document
Beispiel #19
0
 def update(self, data, properties, request=None):
     """Document data setter"""
     if properties:
         if request is None:
             request = check_request()
         self.updater = request.principal.id
     if data is not None:
         if 'filename' in properties:
             data = (properties.pop('filename'), data)
         self.data = data
     if (not self.title) or ('title' in properties):
         self.title = properties.pop('title', '<UNDEFINED>')
     if (not self.application_name) or ('application_name' in properties):
         self.application_name = properties.pop('application_name',
                                                '<UNDEFINED>')
     self.update_roles(properties, request)
     self.update_security_policy(properties, request)
     self.update_status(properties, request)
     self.update_properties(properties, request)
     return IWorkflowState(self)
Beispiel #20
0
 def fire_transition_toward(self,
                            state,
                            comment=None,
                            side_effect=None,
                            check_security=True,
                            principal=None,
                            request=None):
     # pylint: disable=too-many-arguments
     """Fire transition(s) to given state"""
     current_state = IWorkflowState(self.context)
     if state == current_state.state:  # unchanged state
         return None
     transition_ids = self.get_fireable_transition_ids_toward(
         state, check_security)
     if not transition_ids:
         raise NoTransitionAvailableError(current_state.state, state)
     if len(transition_ids) != 1:
         raise AmbiguousTransitionError(current_state.state, state)
     return self.fire_transition(transition_ids[0], comment, side_effect,
                                 check_security, principal, request)
Beispiel #21
0
 def get_label(content, request=None, format=True):  # pylint: disable=redefined-builtin
     """Workflow state label getter"""
     if request is None:
         request = check_request()
     translate = request.localizer.translate
     state = IWorkflowState(content)
     if len(state.history) <= 2:
         header = STATES_HEADERS.get(state.state)
         if header is not None:
             if state.version_id == 1:
                 state_label = translate(header)
             else:
                 state_label = translate(_("new version created"))
         else:
             state_label = translate(_("Unknown state"))
     else:
         state_label = translate(_('publication refused'))
     if format:
         state_label = translate(_('{state} {date}')).format(
             state=state_label, date=format_datetime(state.state_date))
     return state_label
Beispiel #22
0
 def title(self):
     """Title getter"""
     translate = self.request.localizer.translate  # pylint: disable=no-member
     return translate(_("Version {version} history")).format(
         version=IWorkflowState(self.context).version_id)  # pylint: disable=no-member
Beispiel #23
0
 def get_value(self, obj):
     state = IWorkflowState(obj)
     return self.request.localizer.translate(STATE_LABELS.get(state.state))
Beispiel #24
0
 def to_json(self, fields=None, request=None):
     """Get document properties in JSON format"""
     if request is None:
         request = check_request()
     dc = IZopeDublinCore(self)  # pylint: disable=invalid-name
     state = IWorkflowState(self)
     roles = IDocumentRoles(self)
     result = {
         'api':
         absolute_url(request.root, request,
                      'api/zfiles/rest/{}'.format(self.oid)),
         'oid':
         self.oid,
         'title':
         self.title,
         'application_name':
         self.application_name,
         'filename':
         self.data.filename,
         'filesize':
         self.data.get_size(),
         'content_type':
         self.data.content_type,
         'href':
         absolute_url(self.data, request),
         'hash':
         self.hash,
         'properties':
         self.properties,
         'tags':
         list(self.tags or ()),
         'version':
         state.version_id,
         'status':
         state.state,
         'creator':
         list(roles.creator)[0],
         'created_time':
         dc.created.isoformat() if dc.created else None,  # pylint: disable=no-member
         'owner':
         list(roles.owner)[0],
         'updater':
         self.updater,
         'updated_time':
         dc.modified.isoformat() if dc.modified else None,  # pylint: disable=no-member
         'status_updater':
         state.state_principal,
         'status_update_time':
         state.state_date.isoformat(),  # pylint: disable=no-member
         'access_mode':
         ACCESS_MODE_IDS[self.access_mode],
         'readers':
         list(roles.readers or ()),
         'update_mode':
         ACCESS_MODE_IDS[self.update_mode],
         'managers':
         list(roles.managers or ())
     }
     if fields:
         for key in tuple(result.keys()):
             if key not in fields:
                 del result[key]
     return result
Beispiel #25
0
 def values(self):
     """History table getter"""
     yield from IWorkflowState(self.context).history  # pylint: disable=not-an-iterable
Beispiel #26
0
 def get_value(self, obj):
     state = IWorkflowState(obj)
     return state.version_id