def render_attachment_table(self): """Renders a ftw-table of attachments. """ columns = ( {'column': 'position', 'column_title': _(u'column_attachment_checkbox', default=u''), 'transform': attachment_checkbox_helper}, {'column': 'content-type', 'column_title': _(u'column_attachment_type', default=u'Type'), 'transform': content_type_helper}, {'column': 'filename', 'column_title': _(u'column_attachment_filename', default=u'Filename'), 'transform': downloadable_filename_helper(self.context)}, {'column': 'size', 'column_title': _(u'column_attachment_size', default=u'Size'), 'transform': human_readable_filesize_helper(self.context)}, ) items = get_attachments(self.context.msg) generator = getUtility(ITableGenerator, 'ftw.tablegenerator') return generator.generate(items, columns, sortable=False)
class IOGMail(form.Schema): """Opengever specific behavior, which add a title Field to the form. """ form.fieldset( u'common', label=base_mf(u'fieldset_common', u'Common'), fields=[u'title', 'original_message', 'message_source']) form.order_before(title='message') dexteritytextindexer.searchable('title') title = schema.TextLine( title=dossier_mf(u'label_title', default=u'Title'), required=False, ) form.mode(original_message=DISPLAY_MODE) form.read_permission(original_message='cmf.ManagePortal') form.write_permission(original_message='cmf.ManagePortal') original_message = field.NamedBlobFile( title=_(u'label_original_message', default=u'Raw *.msg message before conversion'), required=False, ) form.mode(message_source=DISPLAY_MODE) form.read_permission(message_source='cmf.ManagePortal') form.write_permission(message_source='cmf.ManagePortal') message_source = schema.Choice( title=_('label_message_source', default='Message source'), vocabulary=get_message_source_vocabulary(), required=False, )
def __call__(self): items = get_attachments(self.context.msg) if not len(items): msg = _(u'error_no_attachments_to_extract', default=u'This mail has no attachments to extract.') IStatusMessage(self.request).addStatusMessage(msg, type='warning') return self.request.RESPONSE.redirect(self.context.absolute_url()) elif self.request.get('form.cancelled'): return self.request.RESPONSE.redirect(self.context.absolute_url()) elif self.request.get('form.submitted'): attachments = self.request.get('attachments') if not attachments: msg = _(u'error_no_attachments_selected', default=u'You have not selected any attachments.') IStatusMessage(self.request).addStatusMessage(msg, type='error') else: attachments = [int(pos) for pos in attachments] delete_action = self.request.get('delete_action', 'nothing') if delete_action not in self.allowed_delete_actions: raise ValueError('Expected delete action to be one of ' + str(self.allowed_delete_actions)) self.extract_attachments(attachments, delete_action) dossier = self.find_parent_dossier() return self.request.RESPONSE.redirect( os.path.join(dossier.absolute_url(), '#documents')) return grok.View.__call__(self)
def get_metadata_config(self): rows = [ FieldRow('title'), FieldRow('IDocumentMetadata.document_date'), FieldRow('IDocumentMetadata.document_type'), FieldRow('IDocumentMetadata.document_author'), CustomRow(self.render_creator_link, label=ogdmf('label_creator', default='creator')), WebIntelligentFieldRow('IDocumentMetadata.description'), FieldRow('IDocumentMetadata.foreign_reference'), TemplateRow(self.file_template, label=_('label_org_message', default='Message')), TemplateRow(self.attachments_template, label=_('label_documents', default='Attachments')), FieldRow('IDocumentMetadata.digitally_available'), FieldRow('IDocumentMetadata.preserved_as_paper'), FieldRow('IDocumentMetadata.receipt_date'), FieldRow('IDocumentMetadata.delivery_date'), FieldRow('IRelatedDocuments.relatedItems'), FieldRow('IClassification.classification'), FieldRow('IClassification.privacy_layer'), TemplateRow(self.public_trial_template, label=ogbmf('label_public_trial', default='Public Trial')), FieldRow('IClassification.public_trial_statement'), ] if api.user.has_permission('cmf.ManagePortal'): rows.append(FieldRow('IOGMail.original_message')) rows.append(FieldRow('IOGMail.message_source')) return rows
def __call__(self): disable_edit_bar() if not self.context.has_attachments(): msg = _(u'error_no_attachments_to_extract', default=u'This mail has no attachments to extract.') api.portal.show_message(msg, request=self.request, type='warning') return self.request.RESPONSE.redirect(self.context.absolute_url()) if self.request.get('form.cancelled'): return self.request.RESPONSE.redirect(self.context.absolute_url()) if self.request.get('form.submitted'): attachments = self.request.get('attachments') if not attachments: msg = _(u'error_no_attachments_selected', default=u'You have not selected any attachments.') api.portal.show_message(msg, request=self.request, type='error') else: attachments = [int(pos) for pos in attachments] delete_action = self.request.get('delete_action', 'nothing') if delete_action not in self.allowed_delete_actions: raise ValueError('Expected delete action to be one of ' + str(self.allowed_delete_actions)) self.extract_attachments(attachments, delete_action) return self.request.RESPONSE.redirect("{}/#documents".format( self.context.get_extraction_parent().absolute_url())) return super(ExtractAttachments, self).__call__()
def get_metadata_config(self): rows = [ FieldRow('title'), FieldRow('IDocumentMetadata.document_date'), FieldRow('IDocumentMetadata.document_type'), FieldRow('IDocumentMetadata.document_author'), CustomRow(self.render_creator_link, label=ogdmf('label_creator', default='creator')), FieldRow('IDocumentMetadata.description'), FieldRow('IDocumentMetadata.foreign_reference'), TemplateRow(self.file_template, label=_('label_org_message', default='Original message')), TemplateRow(self.attachments_template, label=_('label_documents', default='Attachments')), FieldRow('IDocumentMetadata.digitally_available'), FieldRow('IDocumentMetadata.preserved_as_paper'), FieldRow('IDocumentMetadata.receipt_date'), FieldRow('IDocumentMetadata.delivery_date'), FieldRow('IRelatedDocuments.relatedItems'), FieldRow('IClassification.classification'), FieldRow('IClassification.privacy_layer'), TemplateRow(self.public_trial_template, label=ogbmf('label_public_trial', default='Public Trial')), FieldRow('IClassification.public_trial_statement'), ] if api.user.has_permission('cmf.ManagePortal'): rows.append(FieldRow('IOGMail.original_message')) rows.append(FieldRow('IOGMail.message_source')) return rows
def render_attachment_table(self): """Renders a ftw-table of attachments. """ columns = ( { 'column': '', 'transform': attachment_checkbox_helper, 'width': 30 }, { 'column': 'content-type', 'column_title': _(u'column_attachment_type', default=u'Type'), 'transform': content_type_helper }, { 'column': 'filename', 'column_title': _(u'column_attachment_filename', default=u'Filename'), 'transform': downloadable_filename_helper(self.context) }, { 'column': 'size', 'column_title': _(u'column_attachment_size', default=u'Size'), 'transform': human_readable_filesize_helper(self.context) }, ) items = self.context.get_attachments() generator = getUtility(ITableGenerator, 'ftw.tablegenerator') return generator.generate(items, columns, sortable=False)
def __call__(self): items = get_attachments(self.context.msg) if not len(items): msg = _(u'error_no_attachments_to_extract', default=u'This mail has no attachments to extract.') IStatusMessage(self.request).addStatusMessage(msg, type='warning') return self.request.RESPONSE.redirect( self.context.absolute_url()) elif self.request.get('form.cancelled'): return self.request.RESPONSE.redirect( self.context.absolute_url()) elif self.request.get('form.submitted'): attachments = self.request.get('attachments') if not attachments: msg = _(u'error_no_attachments_selected', default=u'You have not selected any attachments.') IStatusMessage(self.request).addStatusMessage(msg, type='error') else: attachments = [int(pos) for pos in attachments] delete_action = self.request.get('delete_action', 'nothing') if delete_action not in self.allowed_delete_actions: raise ValueError('Expected delete action to be one of ' + str(self.allowed_delete_actions)) self.extract_attachments(attachments, delete_action) dossier = self.find_parent_dossier() return self.request.RESPONSE.redirect( os.path.join(dossier.absolute_url(), '#documents')) return grok.View.__call__(self)
def __call__(self): disable_edit_bar() if not self.context.has_attachments(): msg = _(u'error_no_attachments_to_extract', default=u'This mail has no attachments to extract.') api.portal.show_message(msg, request=self.request, type='warning') return self.request.RESPONSE.redirect(self.context.absolute_url()) if self.request.get('form.cancelled'): return self.request.RESPONSE.redirect(self.context.absolute_url()) if self.request.get('form.submitted'): attachments = self.request.get('attachments') if not attachments: msg = _(u'error_no_attachments_selected', default=u'You have not selected any attachments.') api.portal.show_message( msg, request=self.request, type='error') else: attachments = [int(pos) for pos in attachments] delete_action = self.request.get('delete_action', 'nothing') if delete_action not in self.allowed_delete_actions: raise ValueError('Expected delete action to be one of ' + str(self.allowed_delete_actions)) self.extract_attachments(attachments, delete_action) return self.request.RESPONSE.redirect( "{}/#documents".format( self.context.get_extraction_parent().absolute_url())) return super(ExtractAttachments, self).__call__()
def get_message_source_vocabulary(): terms = [ SimpleTerm(MESSAGE_SOURCE_MAILIN, title=_('label_message_source_mailin', default='Mail-in')), SimpleTerm(MESSAGE_SOURCE_DRAG_DROP_UPLOAD, title=_('label_message_source_d_n_d_upload', default='Drag and drop upload')), ] return SimpleVocabulary(terms)
def create_mail(self, text='', objs=[], only_links=''): """Create the mail and attach the the files. For object without a file it include a Link to the Object in to the message""" attachment_parts = [] msg = MIMEMultipart() msg['Date'] = formatdate(localtime=True) # iterate over object list (which can include documents and mails), # create attachement parts for them and prepare docs_links docs_links = '%s:\r\n' % (translate( _('label_documents', default=u'Documents'), context=self.request)) for obj in objs: if IMail.providedBy(obj): obj_file = obj.message else: obj_file = obj.file if only_links or not obj_file: # rewrite the url with clients public url url = '%s/%s' % (get_current_client().public_url, '/'.join( obj.getPhysicalPath()[2:])) docs_links = '%s\r\n - %s (%s)' % (docs_links, obj.title, url) continue docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, translate(_('label_see_attachment', default=u'see attachment'), context=self.request)) mimetype = obj_file.contentType if not mimetype: mimetype = 'application/octet-stream' maintype, subtype = obj_file.contentType.split('/', 1) part = MIMEBase(maintype, subtype) part.set_payload(obj_file.data) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % obj_file.filename) attachment_parts.append(part) # First, create the text part and attach it to the message ... text = '%s\r\n\r\n%s\r\n' % (text.encode( CHARSET, 'ignore'), docs_links.encode(CHARSET)) if not isinstance(text, unicode): text = text.decode('utf8') msg.attach(MIMEText(text, 'plain', CHARSET)) # ... then attach all the attachment parts for part in attachment_parts: msg.attach(part) return msg
def create_mail(self, text="", objs=[], only_links=""): """Create the mail and attach the the files. For object without a file it include a Link to the Object in to the message""" attachment_parts = [] msg = MIMEMultipart() msg["Date"] = formatdate(localtime=True) # iterate over object list (which can include documents and mails), # create attachement parts for them and prepare docs_links docs_links = "%s:\r\n" % (translate(_("label_documents", default=u"Documents"), context=self.request)) for obj in objs: if IMail.providedBy(obj): obj_file = obj.message else: obj_file = obj.file if only_links or not obj_file: # rewrite the url with clients public url url = "%s/%s" % (get_current_client().public_url, "/".join(obj.getPhysicalPath()[2:])) docs_links = "%s\r\n - %s (%s)" % (docs_links, obj.title, url) continue docs_links = "%s\r\n - %s (%s)" % ( docs_links, obj.title, translate(_("label_see_attachment", default=u"see attachment"), context=self.request), ) mimetype = obj_file.contentType if not mimetype: mimetype = "application/octet-stream" maintype, subtype = obj_file.contentType.split("/", 1) part = MIMEBase(maintype, subtype) part.set_payload(obj_file.data) Encoders.encode_base64(part) part.add_header("Content-Disposition", 'attachment; filename="%s"' % obj_file.filename) attachment_parts.append(part) # First, create the text part and attach it to the message ... text = "%s\r\n\r\n%s\r\n" % (text.encode(CHARSET, "ignore"), docs_links.encode(CHARSET)) if not isinstance(text, unicode): text = text.decode("utf8") msg.attach(MIMEText(text, "plain", CHARSET)) # ... then attach all the attachment parts for part in attachment_parts: msg.attach(part) return msg
class MailTabbedView(GeverTabbedView): """Tabbedview for the ftw.mail.mail objects. Provides the following tabs: Overview, Preview (configurable), Journal, Sharing. """ overview_tab = { 'id': 'overview', 'title': _(u'label_overview', default=u'Overview'), } journal_tab = { 'id': 'journal', 'title': _(u'label_journal', default=u'Journal'), } sharing_tab = { 'id': 'sharing', 'title': _(u'label_sharing', default=u'Sharing'), } def __init__(self, context, request): """Slap a warning onto the overview of a trashed mail. We're asserting on the request not having a form as the tabs themselves, which get requested by AJAX, rely on a form in the request data. If we'd also slap the portal warning onto those requests, the next 'full' page view would display them, as the tabs do not consume a portal warning. """ super(MailTabbedView, self).__init__(context, request) if ITrashed.providedBy(self.context): if not self.request.form: msg = _(u'warning_trashed', default=u'This mail is trashed.') api.portal.show_message(msg, self.request, type='warning') @property def preview_tab(self): if api.portal.get_registry_record(name='preview_tab_visible', interface=IMailTabbedviewSettings): return { 'id': 'preview', 'title': _(u'label_preview', default=u'Preview'), } return None def _get_tabs(self): return filter(None, [ self.overview_tab, self.preview_tab, self.journal_tab, self.sharing_tab, ])
def get_metadata_config(self): return [ FieldRow('title'), FieldRow('IDocumentMetadata.document_date'), FieldRow('IDocumentMetadata.document_type'), FieldRow('IDocumentMetadata.document_author'), CustomRow(self.render_creator_link, label=ogdmf('label_creator', default='creator')), FieldRow('IDocumentMetadata.description'), FieldRow('IDocumentMetadata.foreign_reference'), TemplateRow(self.file_template, label=_('label_org_message', default='Original message')), FieldRow('IDocumentMetadata.digitally_available'), FieldRow('IDocumentMetadata.preserved_as_paper'), FieldRow('IDocumentMetadata.receipt_date'), FieldRow('IDocumentMetadata.delivery_date'), FieldRow('IRelatedDocuments.relatedItems'), FieldRow('IClassification.classification'), FieldRow('IClassification.privacy_layer'), TemplateRow(self.public_trial_template, label=ogbmf('label_public_trial', default='Public Trial')), FieldRow('IClassification.public_trial_statement'), ]
def file_sent_mail_in_dossier(self, msg): dossier = self.context mail = createMailInContainer(dossier, msg.as_string()) mail.delivery_date = date.today() mail.reindexObject(idxs=['delivery_date']) status_msg = _(u"Sent mail filed as '${title}'.", mapping={'title': mail.title_or_id()}) IStatusMessage(self.request).addStatusMessage(status_msg, type='info')
def preview_tab(self): if api.portal.get_registry_record( name='preview_tab_visible', interface=IMailTabbedviewSettings): return { 'id': 'preview', 'title': _(u'label_preview', default=u'Preview'), } return None
def preview_tab(self): if api.portal.get_registry_record(name='preview_tab_visible', interface=IMailTabbedviewSettings): return { 'id': 'preview', 'title': _(u'label_preview', default=u'Preview'), } return None
def validateHasEmail(self): """ check if minium one e-mail-address is given.""" if len(self.intern_receiver) == 0 and not self.extern_receiver: raise NoMail( _( u"You have to select a intern \ or enter a extern mail-addres" ) )
def file_sent_mail_in_dossier(self, msg): dossier = self.context mail = createMailInContainer(dossier, msg.as_string()) mail.delivery_date = date.today() mail.reindexObject(idxs=['delivery_date']) status_msg = _( u"Sent mail filed as '${title}'.", mapping={'title': mail.title_or_id()}) IStatusMessage(self.request).addStatusMessage(status_msg, type='info')
def send_button_handler(self, action): """Create and Send the Email.""" data, errors = self.extractData() if len(errors) == 0: mh = getToolByName(self.context, 'MailHost') userid = self.context.portal_membership.getAuthenticatedMember() userid = userid.getId() intern_receiver = [] for receiver in data.get('intern_receiver', []): # cut away the username intern_receiver.append(receiver.split(':')[0]) extern_receiver = data.get('extern_receiver') or [] addresses = intern_receiver + extern_receiver # create the mail msg = self.create_mail( data.get('message'), data.get('documents'), only_links=data.get('documents_as_links')) msg['Subject'] = Header(data.get('subject'), CHARSET) user = ogds_service().fetch_user(userid) sender_address = user and user.email if not sender_address: portal = self.context.portal_url.getPortalObject() sender_address = portal.email_from_address msg['From'] = make_addr_header( user.label(), sender_address, CHARSET) header_to = Header(','.join(addresses), CHARSET) msg['To'] = header_to # send it mfrom = u'{} <{}>'.format( user.label(), sender_address).encode(CHARSET) mh.send(msg, mfrom=mfrom, mto=','.join(addresses)) # Store a copy of the sent mail in dossier if (data.get('file_copy_in_dossier', False) and self._allow_save_file_copy_in_context()): self.file_sent_mail_in_dossier(msg) # let the user know that the mail was sent info = _(u'info_mail_sent', 'E-mail has been sent.') notify(DocumentSent( self.context, userid, header_to, data.get('subject'), data.get('message'), data.get('documents'))) IStatusMessage(self.request).addStatusMessage(info, type='info') # and redirect to default view / tab return self.request.RESPONSE.redirect( get_containing_document_tab_url(data.get('documents')[0]))
def get_items(self): return [ { "class": "created", "label": _("byline_created", default="Created"), "content": self.created(), "replace": False, }, { "class": "sequenceNumber", "label": _("label_sequence_number", default="Sequence Number"), "content": self.sequence_number(), "replace": False, }, { "class": "reference_number", "label": _("label_reference_number", default="Reference Number"), "content": self.reference_number(), "replace": False, }, ]
def extract_attachments(self, positions, delete_action): docs = self.context.extract_attachments_into_parent(positions) for document in docs: msg = _(u'info_extracted_document', default=u'Created document ${title}', mapping={'title': document.Title().decode('utf-8')}) api.portal.show_message(msg, request=self.request, type='info') # delete the attachments from the email message, if needed if delete_action == 'selected': self.context.delete_attachments(positions) elif delete_action == 'all': self.context.delete_all_attachments()
def get_items(self): return [ { 'class': 'created', 'label': _('byline_created', default='Created'), 'content': self.created(), 'replace': False }, { 'class': 'sequenceNumber', 'label': _('label_sequence_number', default='Sequence Number'), 'content': self.sequence_number(), 'replace': False }, { 'class': 'reference_number', 'label': _('label_reference_number', default='Reference Number'), 'content': self.reference_number(), 'replace': False }, ]
def send_button_handler(self, action): """ create and Send the Email """ data, errors = self.extractData() if len(errors) == 0: mh = getToolByName(self.context, 'MailHost') contact_info = getUtility(IContactInformation) userid = self.context.portal_membership.getAuthenticatedMember() userid = userid.getId() intern_receiver = [] for receiver in data.get('intern_receiver', []): # cut away the username intern_receiver.append(receiver.split(':')[0]) extern_receiver = data.get('extern_receiver') or [] addresses = intern_receiver + extern_receiver # create the mail msg = self.create_mail( data.get('message'), data.get('documents'), only_links=data.get('documents_as_links')) msg['Subject'] = Header(data.get('subject'), CHARSET) sender_address = contact_info.get_email(userid) if not sender_address: portal = self.context.portal_url.getPortalObject() sender_address = portal.email_from_address mail_from = '%s <%s>' % ( contact_info.describe(userid).encode(CHARSET), sender_address.encode(CHARSET)) msg['From'] = Header(mail_from, CHARSET) header_to = Header(','.join(addresses), CHARSET) msg['To'] = header_to # send it mh.send(msg, mfrom=mail_from, mto=','.join(addresses)) # let the user know that the mail was sent info = _(u'info_mails_sent', 'Mails sent') notify(DocumentSent( self.context, userid, header_to, data.get('subject'), data.get('message'), data.get('documents'))) IStatusMessage(self.request).addStatusMessage(info, type='info') # and redirect to default view / tab return self.request.RESPONSE.redirect( get_containg_document_tab_url(data.get('documents')[0]))
class MailTabbedView(GeverTabbedView): """Tabbedview for the ftw.mail.mail objects. Provides the following tabs: Overview, Preview (configurable), Journal, Sharing. """ overview_tab = { 'id': 'overview', 'title': _(u'label_overview', default=u'Overview'), } journal_tab = { 'id': 'journal', 'title': _(u'label_journal', default=u'Journal'), } sharing_tab = { 'id': 'sharing', 'title': _(u'label_sharing', default=u'Sharing'), } @property def preview_tab(self): if api.portal.get_registry_record(name='preview_tab_visible', interface=IMailTabbedviewSettings): return { 'id': 'preview', 'title': _(u'label_preview', default=u'Preview'), } return None def _get_tabs(self): return filter(None, [ self.overview_tab, self.preview_tab, self.journal_tab, self.sharing_tab, ])
def send_button_handler(self, action): """ create and Send the Email """ data, errors = self.extractData() if len(errors) == 0: mh = getToolByName(self.context, 'MailHost') contact_info = getUtility(IContactInformation) userid = self.context.portal_membership.getAuthenticatedMember() userid = userid.getId() intern_receiver = [] for receiver in data.get('intern_receiver', []): # cut away the username intern_receiver.append(receiver.split(':')[0]) extern_receiver = data.get('extern_receiver') or [] addresses = intern_receiver + extern_receiver # create the mail msg = self.create_mail(data.get('message'), data.get('documents'), only_links=data.get('documents_as_links')) msg['Subject'] = Header(data.get('subject'), CHARSET) sender_address = contact_info.get_user(userid).email if not sender_address: portal = self.context.portal_url.getPortalObject() sender_address = portal.email_from_address mail_from = '%s <%s>' % (contact_info.describe(userid).encode( CHARSET), sender_address.encode(CHARSET)) msg['From'] = Header(mail_from, CHARSET) header_to = Header(','.join(addresses), CHARSET) msg['To'] = header_to # send it mh.send(msg, mfrom=mail_from, mto=','.join(addresses)) # let the user know that the mail was sent info = _(u'info_mails_sent', 'Mails sent') notify( DocumentSent(self.context, userid, header_to, data.get('subject'), data.get('message'), data.get('documents'))) IStatusMessage(self.request).addStatusMessage(info, type='info') # and redirect to default view / tab return self.request.RESPONSE.redirect( get_containg_document_tab_url(data.get('documents')[0]))
def __init__(self, context, request): """Slap a warning onto the overview of a trashed mail. We're asserting on the request not having a form as the tabs themselves, which get requested by AJAX, rely on a form in the request data. If we'd also slap the portal warning onto those requests, the next 'full' page view would display them, as the tabs do not consume a portal warning. """ super(MailTabbedView, self).__init__(context, request) if ITrashed.providedBy(self.context): if not self.request.form: msg = _(u'warning_trashed', default=u'This mail is trashed.') api.portal.show_message(msg, self.request, type='warning')
def updateWidgets(self): super(SendDocumentForm, self).updateWidgets() if not self._allow_save_file_copy_in_context(): file_copy_widget = self.widgets['file_copy_in_dossier'] disabled_hint_text = translate( _(u'file_copy_widget_disabled_hint_text', default='(only possible for open dossiers)'), context=self.request) file_copy_widget.value = [] checkbox = file_copy_widget.items[0] checkbox['label'] = u'{} {}'.format( checkbox['label'], disabled_hint_text) file_copy_widget.disabled = 'disabled'
class SendDocumentForm(form.Form): """A form to send documents over mail with.""" fields = field.Fields(ISendDocumentSchema) ignoreContext = True label = _('heading_send_as_email', default="Send as email") fields['extern_receiver'].widgetFactory[INPUT_MODE] \ = TextLinesFieldWidget fields['intern_receiver'].widgetFactory[INPUT_MODE] = ParameterizedWidget( KeywordWidget, async=True) fields['documents_as_links'].widgetFactory[INPUT_MODE] \ = SingleCheckBoxFieldWidget def update(self): """Put default value for documents field, into the request, because this view would call from the document tab in the dossier view """ paths = self.request.get('paths', []) if paths: self.request.set('form.widgets.documents', paths) super(SendDocumentForm, self).update() def updateWidgets(self): super(SendDocumentForm, self).updateWidgets() if not self._allow_save_file_copy_in_context(): file_copy_widget = self.widgets['file_copy_in_dossier'] disabled_hint_text = translate( _(u'file_copy_widget_disabled_hint_text', default='(only possible for open dossiers)'), context=self.request) file_copy_widget.value = [] checkbox = file_copy_widget.items[0] checkbox['label'] = u'{} {}'.format( checkbox['label'], disabled_hint_text) file_copy_widget.disabled = 'disabled' @button.buttonAndHandler(_(u'button_send', default=u'Send')) def send_button_handler(self, action): """Create and Send the Email.""" data, errors = self.extractData() if len(errors) == 0: mh = getToolByName(self.context, 'MailHost') userid = self.context.portal_membership.getAuthenticatedMember() userid = userid.getId() intern_receiver = [] for receiver in data.get('intern_receiver', []): # cut away the username intern_receiver.append(receiver.split(':')[0]) extern_receiver = data.get('extern_receiver') or [] addresses = intern_receiver + extern_receiver # create the mail msg = self.create_mail( data.get('message'), data.get('documents'), only_links=data.get('documents_as_links')) msg['Subject'] = Header(data.get('subject'), CHARSET) user = ogds_service().fetch_user(userid) sender_address = user and user.email if not sender_address: portal = self.context.portal_url.getPortalObject() sender_address = portal.email_from_address msg['From'] = make_addr_header( user.label(), sender_address, CHARSET) header_to = Header(','.join(addresses), CHARSET) msg['To'] = header_to # send it mfrom = u'{} <{}>'.format( user.label(), sender_address).encode(CHARSET) mh.send(msg, mfrom=mfrom, mto=','.join(addresses)) # Store a copy of the sent mail in dossier if (data.get('file_copy_in_dossier', False) and self._allow_save_file_copy_in_context()): self.file_sent_mail_in_dossier(msg) # let the user know that the mail was sent info = _(u'info_mail_sent', 'E-mail has been sent.') notify(DocumentSent( self.context, userid, header_to, data.get('subject'), data.get('message'), data.get('documents'))) IStatusMessage(self.request).addStatusMessage(info, type='info') # and redirect to default view / tab return self.request.RESPONSE.redirect( get_containing_document_tab_url(data.get('documents')[0])) @button.buttonAndHandler(_('cancel_back', default=u'Cancel')) def cancel_button_handler(self, action): data, errors = self.extractData() if data.get('documents'): url = get_containing_document_tab_url(data.get('documents')[0]) else: url = get_containing_document_tab_url(self.context) return self.request.RESPONSE.redirect(url) def create_mail(self, text='', objs=[], only_links=''): """Create the mail and attach the the files. For object without a file it include a Link to the Object in to the message. """ attachment_parts = [] msg = MIMEMultipart() msg['Date'] = formatdate(localtime=True) # iterate over object list (which can include documents and mails), # create attachement parts for them and prepare docs_links docs_links = '%s:\r\n' % (translate( _('label_documents', default=u'Attachments'), context=self.request)) for obj in objs: if IMail.providedBy(obj): obj_file = obj.message else: obj_file = obj.file if only_links or not obj_file: # rewrite the url with current adminunit's public url url = '%s/%s' % ( get_current_admin_unit().public_url, '/'.join(obj.getPhysicalPath()[2:])) docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, url) continue docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, translate( _('label_see_attachment', default=u'see attachment'), context=self.request)) mimetype = obj_file.contentType if not mimetype: mimetype = 'application/octet-stream' maintype, subtype = obj_file.contentType.split('/', 1) part = MIMEBase(maintype, subtype) part.set_payload(obj_file.data) if mimetype != 'message/rfc822': Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % obj_file.filename) attachment_parts.append(part) # First, create the text part and attach it to the message ... text = '%s\r\n\r\n%s\r\n' % ( text.encode(CHARSET, 'ignore'), docs_links.encode(CHARSET)) if not isinstance(text, unicode): text = text.decode('utf8') msg.attach(MIMEText(text, 'plain', CHARSET)) # ... then attach all the attachment parts for part in attachment_parts: msg.attach(part) return msg def file_sent_mail_in_dossier(self, msg): dossier = self.context mail = createMailInContainer(dossier, msg.as_string()) mail.delivery_date = date.today() mail.reindexObject(idxs=['delivery_date']) status_msg = _( u"Sent mail filed as '${title}'.", mapping={'title': mail.title_or_id()}) IStatusMessage(self.request).addStatusMessage(status_msg, type='info') def _allow_save_file_copy_in_context(self): """The field file_copy_in_dossier should not be allowed for closed dossiers or on all other contenttypes (i.e. the Inbox). """ if IDossierMarker.providedBy(self.context): return self.context.is_open() return False
def validateHasEmail(self): """Check if minium one e-mail-address is given.""" if len(self.intern_receiver) == 0 and not self.extern_receiver: raise NoMail(_(u'You have to select a intern \ or enter a extern mail-addres'))
def create_initial_journal_entry(mail): title = _( u'label_journal_initialized', default=u'Journal initialized') journal_entry_factory(mail, 'Journal initialized', title)
class ISendDocumentSchema(Interface): """The Send Document Form Schema.""" intern_receiver = schema.Tuple( title=_('intern_receiver', default="Intern receiver"), description=_('help_intern_receiver', default="Live Search: search for users and contacts"), value_type=schema.Choice( title=_(u"mails"), source=AllEmailContactsAndUsersSourceBinder()), required=False, missing_value=(), # important! default=(), ) extern_receiver = schema.List( title=_('extern_receiver', default="Extern receiver"), description=_('help_extern_receiver', default="email addresses of the receivers. " "Enter manually the addresses, one per each line."), value_type=schema.TextLine(title=_('receiver'), ), required=False, ) subject = schema.TextLine( title=_(u'label_subject', default=u'Subject'), required=True, ) message = schema.Text( title=_(u'label_message', default=u'Message'), required=True, ) documents = RelationList( title=_(u'label_documents', default=u'Attachments'), default=[], missing_value=[], value_type=RelationChoice( title=u"Documents", source=DossierPathSourceBinder( portal_type=("opengever.document.document", "ftw.mail.mail"), navigation_tree_query={ 'object_provides': ['opengever.dossier.behaviors.dossier.IDossierMarker', 'opengever.task.task.ITask', 'opengever.document.document.IDocumentSchema', 'ftw.mail.mail.IMail', ]}), ), required=False, ) documents_as_links = schema.Bool( title=_(u'label_documents_as_link', default=u'Send documents only als links'), required=True, ) file_copy_in_dossier = schema.Bool( title=_(u'label_file_copy_in_dossier', default=u'File a copy of the sent mail in dossier'), required=True, default=True, ) @invariant def validateHasEmail(self): """Check if minium one e-mail-address is given.""" if len(self.intern_receiver) == 0 and not self.extern_receiver: raise NoMail(_(u'You have to select a intern \ or enter a extern mail-addres'))
class SendDocumentForm(form.Form): """ The Send Documents per Mail Formular """ fields = field.Fields(ISendDocumentSchema) ignoreContext = True label = _('heading_send_as_email', default="Send as email") fields['extern_receiver'].widgetFactory[INPUT_MODE] \ = TextLinesFieldWidget fields['intern_receiver'].widgetFactory[INPUT_MODE] \ = AutocompleteMultiFieldWidget fields['documents_as_links'].widgetFactory[INPUT_MODE] \ = SingleCheckBoxFieldWidget def update(self): """ put default value for documents field, into the request, because this view would call from the document tab in the dossier view """ paths = self.request.get('paths', []) if paths: self.request.set('form.widgets.documents', paths) super(SendDocumentForm, self).update() @button.buttonAndHandler(_(u'button_send', default=u'Send')) def send_button_handler(self, action): """ create and Send the Email """ data, errors = self.extractData() if len(errors) == 0: mh = getToolByName(self.context, 'MailHost') contact_info = getUtility(IContactInformation) userid = self.context.portal_membership.getAuthenticatedMember() userid = userid.getId() intern_receiver = [] for receiver in data.get('intern_receiver', []): # cut away the username intern_receiver.append(receiver.split(':')[0]) extern_receiver = data.get('extern_receiver') or [] addresses = intern_receiver + extern_receiver # create the mail msg = self.create_mail(data.get('message'), data.get('documents'), only_links=data.get('documents_as_links')) msg['Subject'] = Header(data.get('subject'), CHARSET) sender_address = contact_info.get_user(userid).email if not sender_address: portal = self.context.portal_url.getPortalObject() sender_address = portal.email_from_address mail_from = '%s <%s>' % (contact_info.describe(userid).encode( CHARSET), sender_address.encode(CHARSET)) msg['From'] = Header(mail_from, CHARSET) header_to = Header(','.join(addresses), CHARSET) msg['To'] = header_to # send it mh.send(msg, mfrom=mail_from, mto=','.join(addresses)) # let the user know that the mail was sent info = _(u'info_mails_sent', 'Mails sent') notify( DocumentSent(self.context, userid, header_to, data.get('subject'), data.get('message'), data.get('documents'))) IStatusMessage(self.request).addStatusMessage(info, type='info') # and redirect to default view / tab return self.request.RESPONSE.redirect( get_containg_document_tab_url(data.get('documents')[0])) @button.buttonAndHandler(_('cancel_back', default=u'Cancel')) def cancel_button_handler(self, action): data, errors = self.extractData() if data.get('documents'): url = get_containg_document_tab_url(data.get('documents')[0]) else: url = get_containg_document_tab_url(self.context) return self.request.RESPONSE.redirect(url) def create_mail(self, text='', objs=[], only_links=''): """Create the mail and attach the the files. For object without a file it include a Link to the Object in to the message""" attachment_parts = [] msg = MIMEMultipart() msg['Date'] = formatdate(localtime=True) # iterate over object list (which can include documents and mails), # create attachement parts for them and prepare docs_links docs_links = '%s:\r\n' % (translate( _('label_documents', default=u'Documents'), context=self.request)) for obj in objs: if IMail.providedBy(obj): obj_file = obj.message else: obj_file = obj.file if only_links or not obj_file: # rewrite the url with clients public url url = '%s/%s' % (get_current_client().public_url, '/'.join( obj.getPhysicalPath()[2:])) docs_links = '%s\r\n - %s (%s)' % (docs_links, obj.title, url) continue docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, translate(_('label_see_attachment', default=u'see attachment'), context=self.request)) mimetype = obj_file.contentType if not mimetype: mimetype = 'application/octet-stream' maintype, subtype = obj_file.contentType.split('/', 1) part = MIMEBase(maintype, subtype) part.set_payload(obj_file.data) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % obj_file.filename) attachment_parts.append(part) # First, create the text part and attach it to the message ... text = '%s\r\n\r\n%s\r\n' % (text.encode( CHARSET, 'ignore'), docs_links.encode(CHARSET)) if not isinstance(text, unicode): text = text.decode('utf8') msg.attach(MIMEText(text, 'plain', CHARSET)) # ... then attach all the attachment parts for part in attachment_parts: msg.attach(part) return msg
class NoMail(Invalid): """ The No Mail was defined Exception.""" __doc__ = _(u"No Mail Address")
def extract_attachments(self, positions, delete_action): dossier = self.find_parent_dossier() attachments_to_extract = filter( lambda att: att.get('position') in positions, get_attachments(self.context.msg)) # create documents from the selected attachments for att in attachments_to_extract: pos = att.get('position') filename = att.get('filename') # remove line breaks from the filename filename = re.sub('\s{1,}', ' ', filename) kwargs = {'title': filename[:filename.rfind('.')].decode('utf-8'), 'file': self.get_attachment_as_namedfile(pos), 'keywords': (), 'digitally_available': True} doc = createContentInContainer(dossier, 'opengever.document.document', **kwargs) for schemata in iterSchemata(doc): for name, field in getFieldsInOrder(schemata): if name not in kwargs.keys(): default = queryMultiAdapter(( doc, doc.REQUEST, # request None, # form field, None, # Widget ), IValue, name='default') if default is not None: default = default.get() if default is None: default = getattr(field, 'default', None) if default is None: try: default = field.missing_value except: pass field.set(field.interface(doc), default) # add a reference from the attachment to the mail intids = getUtility(IIntIds) iid = intids.getId(self.context) # prevent circular dependencies from opengever.document.behaviors import IRelatedDocuments IRelatedDocuments(doc).relatedItems = [RelationValue(iid)] msg = _(u'info_extracted_document', default=u'Created document ${title}', mapping={'title': doc.Title().decode('utf-8')}) IStatusMessage(self.request).addStatusMessage(msg, type='info') # reindex the new document to index also all the default values doc.reindexObject() # delete the attachments from the email message, if needed if delete_action in ('all', 'selected'): if delete_action == 'selected': pos_to_delete = positions else: # all pos_to_delete = [int(att['position']) for att in get_attachments(self.context.msg)] # set the new message file msg = remove_attachments(self.context.msg, pos_to_delete) self.context.message = NamedFile( data=msg.as_string(), contentType=self.context.message.contentType, filename=self.context.message.filename)
class NoMail(Invalid): """This is a dummy class to raise an exception in the lack of a mail address. """ __doc__ = _(u"No Mail Address")
class FilesTooLarge(schema.interfaces.ValidationError): """ Total size of selected files is too large. """ __doc__ = _(u"The files you selected are larger than the maximum size")
def create_mail(self, text='', objs=[], only_links=''): """Create the mail and attach the the files. For object without a file it include a Link to the Object in to the message. """ attachment_parts = [] msg = MIMEMultipart() msg['Date'] = formatdate(localtime=True) # iterate over object list (which can include documents and mails), # create attachement parts for them and prepare docs_links docs_links = '%s:\r\n' % (translate( _('label_documents', default=u'Attachments'), context=self.request)) for obj in objs: obj_file = obj.get_file() if only_links or not obj_file: # rewrite the url with current adminunit's public url url = '%s/%s' % ( get_current_admin_unit().public_url, '/'.join(obj.getPhysicalPath()[2:])) docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, url) continue docs_links = '%s\r\n - %s (%s)' % ( docs_links, obj.title, translate( _('label_see_attachment', default=u'see attachment'), context=self.request)) mimetype = obj_file.contentType if not mimetype: mimetype = 'application/octet-stream' maintype, subtype = obj_file.contentType.split('/', 1) part = MIMEBase(maintype, subtype) part.set_payload(obj_file.data) if mimetype != 'message/rfc822': Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % obj_file.filename) attachment_parts.append(part) # First, create the text part and attach it to the message ... text = '%s\r\n\r\n%s\r\n' % ( text.encode(CHARSET, 'ignore'), docs_links.encode(CHARSET)) if not isinstance(text, unicode): text = text.decode('utf8') msg.attach(MIMEText(text, 'plain', CHARSET)) # ... then attach all the attachment parts for part in attachment_parts: msg.attach(part) return msg
def extract_attachments(self, positions, delete_action): dossier = self.find_parent_dossier() attachments_to_extract = filter( lambda att: att.get('position') in positions, get_attachments(self.context.msg)) # create documents from the selected attachments for att in attachments_to_extract: pos = att.get('position') filename = att.get('filename') # remove line breaks from the filename filename = re.sub('\s{1,}', ' ', filename) kwargs = { 'title': filename[:filename.rfind('.')].decode('utf-8'), 'file': self.get_attachment_as_namedfile(pos), 'keywords': (), 'digitally_available': True } doc = createContentInContainer(dossier, 'opengever.document.document', **kwargs) for schemata in iterSchemata(doc): for name, field in getFieldsInOrder(schemata): if name not in kwargs.keys(): default = queryMultiAdapter( ( doc, doc.REQUEST, # request None, # form field, None, # Widget ), IValue, name='default') if default is not None: default = default.get() if default is None: default = getattr(field, 'default', None) if default is None: try: default = field.missing_value except: pass field.set(field.interface(doc), default) # add a reference from the attachment to the mail intids = getUtility(IIntIds) iid = intids.getId(self.context) # prevent circular dependencies from opengever.document.behaviors import IRelatedDocuments IRelatedDocuments(doc).relatedItems = [RelationValue(iid)] msg = _(u'info_extracted_document', default=u'Created document ${title}', mapping={'title': doc.Title().decode('utf-8')}) IStatusMessage(self.request).addStatusMessage(msg, type='info') # reindex the new document to index also all the default values doc.reindexObject() # delete the attachments from the email message, if needed if delete_action in ('all', 'selected'): if delete_action == 'selected': pos_to_delete = positions else: # all pos_to_delete = [ int(att['position']) for att in get_attachments(self.context.msg) ] # set the new message file msg = remove_attachments(self.context.msg, pos_to_delete) self.context.message = NamedFile( data=msg.as_string(), contentType=self.context.message.contentType, filename=self.context.message.filename)