class DocumentDataSchema(BaseDocumentSchema): """Document data update schema""" data = SchemaNode( FileUploadType(), title=_("Document data; may be provided in Base64 when using JSON"), missing=drop) filename = SchemaNode(String(), title=_("File name"), missing=drop)
class NewDocumentSchema(DocumentDataSchema): """New document schema""" title = SchemaNode(String(), title=_("Document title")) application_name = SchemaNode(String(), title=_("Source application name")) filename = SchemaNode(String(), title=_("Document file name")) data = SchemaNode( FileUploadType(), title=_("Document data; may be provided in Base64 when using JSON"))
class IDocumentContainerRoles(IContentRoles): """Document container utility roles interface""" application_managers = PrincipalsSetField( title=_("Application managers"), description=_("These principals can only " "manage application properties; " "documents manager role is required " "to manage documents!"), role_id=ZFILES_ADMIN_ROLE, required=False) documents_creators = PrincipalsSetField( title=_("Documents creators"), description=_("These principals will be allowed to " "create or import new documents"), role_id=ZFILES_IMPORTER_ROLE, required=False) documents_managers = PrincipalsSetField( title=_("Documents managers"), description=_("These principals will be allowed to " "manage any document properties"), role_id=ZFILES_MANAGER_ROLE, required=False) documents_readers = PrincipalsSetField( title=_("Documents readers"), description=_("These principals will be allowed to " "read any document properties"), role_id=ZFILES_READER_ROLE, required=False)
class IDocumentRoles(IContentRoles): """Document roles interface""" creator = PrincipalField( title=_("Document creator"), description=_("Name of the principal which created the document"), role_id=ZFILES_CREATOR_ROLE, required=True) owner = PrincipalField( title=_("Document owner"), description=_("Name of the principal which is owner of the document"), role_id=ZFILES_OWNER_ROLE, required=True) readers = PrincipalsSetField( title=_("Document readers"), description=_("Name of principals allowed to read the document"), role_id=ZFILES_READER_ROLE, required=False) managers = PrincipalsSetField( title=_("Document managers"), description=_("Name of principals allowed to update the " "document"), role_id=ZFILES_MANAGER_ROLE, required=False)
class Arguments: """Document creation arguments""" title = String(description=_("Document title"), required=True) application_name = String(description=_("Source application"), required=True) filename = String(description=_("File name"), required=True) data = String(description=_("Document data in Base64 encoding"), required=True) properties = DocumentSchema.properties tags = DocumentSchema.tags status = DocumentSchema.status access_mode = DocumentSchema.access_mode readers = DocumentSchema.readers update_mode = DocumentSchema.update_mode managers = DocumentSchema.managers
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
class DocumentApplicationNameColumn(I18nColumnMixin, GetAttrColumn): """Document application name column""" i18n_header = _("Application") attr_name = 'application_name' weight = 20
class DocumentTitleColumn(I18nColumnMixin, GetAttrColumn): """Document title column""" i18n_header = _("Title") attr_name = 'title' weight = 15
class DocumentOidColumn(I18nColumnMixin, GetAttrColumn): """Document OID column""" i18n_header = _("OID") attr_name = 'oid' weight = 10
class DocumentSearchForm(SearchForm): """Document search form""" title = _("Documents search form") fields = Fields(IDocumentSearchFields) label_css_class = 'col-sm-3' input_css_class = 'col-sm-9' object_data = {'ams-warn-on-change': False} def update_widgets(self, prefix=None): super().update_widgets(prefix) oid = self.widgets.get('oid') if oid is not None: oid.input_css_class = 'col-sm-3' version = self.widgets.get('version') if version is not None: version.input_css_class = 'col-sm-3' placeholder = self.request.localizer.translate( _("No selected principal")) creator = self.widgets.get('creator') if creator is not None: creator.placeholder = placeholder owner = self.widgets.get('owner') if owner is not None: owner.placeholder = placeholder updater = self.widgets.get('updater') if updater is not None: updater.placeholder = placeholder status_updater = self.widgets.get('status_updater') if status_updater is not None: status_updater.placeholder = placeholder
class DocumentContainerSynchronizerEditForm(InnerEditForm): """Document container synchronizer edit form""" legend = _("Synchronizer configuration") fields = Fields(IDocumentSynchronizer) weight = 10
class DocumentVersionColumn(I18nColumnMixin, GetAttrColumn): """Document state column""" i18n_header = _("Version") attr_name = 'version' weight = 40 def get_value(self, obj): state = IWorkflowState(obj) return state.version_id
class DocumentStatusColumn(I18nColumnMixin, GetAttrColumn): """Document state column""" i18n_header = _("State") attr_name = 'state' weight = 30 def get_value(self, obj): state = IWorkflowState(obj) return self.request.localizer.translate(STATE_LABELS.get(state.state))
class IDocument(IWorkflowManagedContent): """Document interface""" containers('.IDocumentFolder') oid = TextLine(title="Document OID", description=_("Document unique identifier"), readonly=True) def get_oid(self): """Generate new unique ID"""
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
class DocumentCreationTimeColumn(I18nColumnMixin, DateColumn): """Document creation time column""" i18n_header = _("Creation time") attr_name = 'created' formatter = SH_DATETIME_FORMAT weight = 50 def get_value(self, obj): dc = IZopeDublinCore(obj) # pylint: disable=invalid-name return format_datetime(dc.created, self.formatter, self.request)
class IDocumentContainer(IBTreeContainer): """Document container utility interface""" contains(IDocumentFolder) oid_prefix = TextLine( title=_("Documents OID prefix"), description=_("Prefix used to identify documents which were " "created locally (unlike documents which were created " "into another documents container and synchronized with " "this container)"), required=False) def add_document(self, data, properties, request): """Add new document""" def import_document(self, oid, data, properties, request): """Import document from outer ZFiles database""" def find_documents(self, params, request=None): """Find documents matching given params""" def get_document(self, oid, version=None): """Retrieve existing document from it's OID If no version number is specified, the last version is returned. """ # pylint: disable=too-many-arguments def update_document(self, oid, version=None, data=None, properties=None, request=None): """Update document data or properties""" def delete_document(self, oid): """Delete document or version"""
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))
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
class Arguments: """Document creation arguments""" oid = String(description=_("Document unique identifier"), required=True) title = String(description=_("Document title"), required=True) application_name = String(description=_("Source application"), required=True) filename = String(description=_("Document file name"), required=True) data = String(description=_("Document data in Base64 encoding"), required=True) owner = String(description=_("Current document owner"), required=True) created_time = String(description=_("Document creation date"), required=True) properties = DocumentSchema.properties tags = DocumentSchema.tags status = DocumentSchema.status access_mode = DocumentSchema.access_mode readers = DocumentSchema.readers update_mode = DocumentSchema.update_mode managers = DocumentSchema.managers
def update_widgets(self, prefix=None): super().update_widgets(prefix) oid = self.widgets.get('oid') if oid is not None: oid.input_css_class = 'col-sm-3' version = self.widgets.get('version') if version is not None: version.input_css_class = 'col-sm-3' placeholder = self.request.localizer.translate( _("No selected principal")) creator = self.widgets.get('creator') if creator is not None: creator.placeholder = placeholder owner = self.widgets.get('owner') if owner is not None: owner.placeholder = placeholder updater = self.widgets.get('updater') if updater is not None: updater.placeholder = placeholder status_updater = self.widgets.get('status_updater') if status_updater is not None: status_updater.placeholder = placeholder
class DocumentPropertiesDisplayForm(AdminDisplayForm): """Document properties display form""" @property def back_url(self): """Form back URL getter""" container = get_utility(IDocumentContainer) return absolute_url(container, self.request, 'admin#search.html') back_url_target = None @property def title(self): """Title getter""" return self.context.title legend = _("Document properties") label_css_class = 'col-sm-3' input_css_class = 'col-sm-9' fields = Fields(IDocumentVersion).omit('__parent__', '__name__') fields['properties'].widget_factory = PropertiesFieldWidget fields['tags'].widget_factory = TextLinesFieldWidget
class IDocumentSynchronizer(Interface): """Documents synchronizer interface""" target = TextLine( title=_("Remote XML-RPC endpoint"), description=_("URL of the remote documents container XML-RPC endpoint " "used for synchronization"), required=False) username = TextLine( title=_("User name"), description=_("Name of the remote user used for synchronization"), required=False) password = Password( title=_("Password"), description=_("Password of the remote user used for synchronization"), required=False) def synchronize(self, oid, mode=IMPORT_MODE, request=None): """Synchronize given OID to remote container"""
class BaseDocumentSchema(MappingSchema): """Base document schema""" title = SchemaNode(String(), title=_("Document title"), missing=drop) application_name = SchemaNode(String(), title=_("Source application name"), missing=drop) filename = SchemaNode(String(), title=_("File name"), missing=drop) properties = SchemaNode(PropertiesMapping(), title=_("Document custom properties"), missing=drop) tags = StringListSchema(title=_("List of document tags"), missing=drop) owner = SchemaNode(String(), title=_("Current document owner"), missing=drop) status = SchemaNode(String(), title=_("Document status"), validator=OneOf((DRAFT_STATE, PUBLISHED_STATE, ARCHIVED_STATE, DELETED_STATE)), default=DRAFT_STATE, missing=drop) access_mode = SchemaNode(String(), title=_("Access mode"), validator=OneOf(ACCESS_MODE_IDS), default='private', missing=drop) readers = StringListSchema(title=_("Document readers IDs"), missing=drop) update_mode = SchemaNode(String(), title=_("Update mode"), validator=OneOf(ACCESS_MODE_IDS), default='private', missing=drop) managers = StringListSchema(title=_("Document managers IDs"), missing=drop)
class FieldsNamesSchema(MappingSchema): """Properties names schema""" fields = StringListSchema(title=_("List of requested fields names"), missing=drop)
class DocumentSearchSchema(MappingSchema): """Document search schema""" oid = StringListSchema(title=_("Document unique identifiers"), missing=drop) version = SchemaNode(Int(), title=_("Document version"), missing=drop) title = SchemaNode(String(), title=_("Document title"), missing=drop) application_name = SchemaNode(String(), title=_("Source application name"), missing=drop) hash = SchemaNode(String(), title=_("SHA512 hash of document data file"), missing=drop) tags = StringListSchema( title=_("Document tags, separated with semicolons"), missing=drop) status = SchemaNode(String(), title=_("Document status"), validator=OneOf((DRAFT_STATE, PUBLISHED_STATE, ARCHIVED_STATE, DELETED_STATE)), default=DRAFT_STATE, missing=drop) creator = SchemaNode(String(), title=_("Document creator principal ID"), missing=drop) created_date = DateRangeSchema(title=_("Document creation dates range"), missing=drop) owner = SchemaNode(String(), title=_("Current document owner"), missing=drop) updater = SchemaNode(String(), title=_("Last document updater principal ID"), missing=drop) updated_date = DateRangeSchema(title=_("Document last update dates range"), missing=drop) status_updater = SchemaNode( String(), title=_("Last workflow status updater principal ID"), missing=drop) status_update_date = DateRangeSchema( title=_("Last workflow status update dates range"), missing=drop) fields = StringListSchema(title=_("List of requested field names"), missing=drop)
class DocumentSchema(BaseDocumentSchema): """Document schema""" api = SchemaNode(String(), title=_("Document base REST API URL")) oid = SchemaNode(String(), title=_("Document unique identifier")) version = SchemaNode(Int(), title=_("Document version")) href = SchemaNode(String(), title=_("Absolute URL of document data file")) hash = SchemaNode(String(), title=_("SHA512 hash of document data file")) filesize = SchemaNode(Int(), title=_("Document file size")) content_type = SchemaNode(String(), title=_("Document content type")) creator = SchemaNode(String(), title=_("Document creator principal ID")) created_time = SchemaNode(DateTime(), title=_("Document creation timestamp")) owner = SchemaNode(String(), title=_("Current document owner")) updater = SchemaNode(String(), title=_("Last document updater principal ID")) updated_time = SchemaNode(DateTime(), title=_("Last document update timestamp")) status_updater = SchemaNode( String(), title=_("Last workflow status updater principal ID")) status_update_time = SchemaNode( DateTime(), title=_("Last document status update timestamp"))
class ImportDocumentSchema(NewDocumentSchema): """Import document schema""" created_time = SchemaNode(DateTime(), title=_("Document creation timestamp")) owner = SchemaNode(String(), title=_("Current document owner"))
title=_("Last document updater principal ID"), missing=drop) updated_date = DateRangeSchema(title=_("Document last update dates range"), missing=drop) status_updater = SchemaNode( String(), title=_("Last workflow status updater principal ID"), missing=drop) status_update_date = DateRangeSchema( title=_("Last workflow status update dates range"), missing=drop) fields = StringListSchema(title=_("List of requested field names"), missing=drop) document_responses = { HTTPOk.code: DocumentSchema(description=_("Document properties")), HTTPCreated.code: DocumentSchema(description=_("Document created")), HTTPNotFound.code: ErrorSchema(description=_("Document not found")), HTTPForbidden.code: ErrorSchema(description=_("Forbidden access")), HTTPBadRequest.code: ErrorSchema(description=_("Missing arguments")), } if TEST_MODE: service_params = {} else: service_params = {'response_schemas': document_responses} container_service = Service(name=REST_CONTAINER_ROUTE, pyramid_route=REST_CONTAINER_ROUTE, description="ZFiles container service")
class DocumentContainerConfigurationEditForm(AdminEditForm): """Document container properties edit form""" legend = _("Configuration") fields = Fields(IDocumentContainer).omit('__parent__', '__name__')