示例#1
0
class IComunicatoStampa(model.Schema):
    arguments = schema.Tuple(
        title=_("arguments_label", default=u"Arguments"),
        description=_("arguments_help", default="Select one or more values."),
        value_type=schema.TextLine(),
        required=True,
        missing_value=(),
    )

    directives.widget(
        "arguments",
        AjaxSelectFieldWidget,
        vocabulary="rer.ufficiostampa.vocabularies.arguments",
        pattern_options={"allowNewItems": "false"},
    )

    legislature = schema.TextLine(
        title=_(u"label_legislature", default=u"Legislature"),
        description=u"",
        required=True,
        defaultFactory=defaultLegislature,
    )
    directives.mode(legislature="display")

    message_sent = schema.Bool(
        title=_(u"label_sent", default=u"Sent"),
        description=u"",
        required=False,
        default=False,
    )
    comunicato_number = schema.TextLine(title=u"",
                                        description=u"",
                                        required=False)
    directives.omitted("message_sent")
    directives.omitted("comunicato_number")
示例#2
0
class ILegislaturesRowSchema(model.Schema):
    legislature = schema.SourceText(
        title=_(
            "legislature_label",
            default=u"Legislature",
        ),
        description=_(
            "legislature_help",
            default=u"Insert the legislature name.",
        ),
        required=True,
    )
    arguments = schema.List(
        title=_(
            "legislature_arguments_label",
            default=u"Arguments",
        ),
        description=_(
            "legislature_arguments_help",
            default=u"Insert a list of arguments related to this legislature."
            u" One per line.",
        ),
        required=True,
        value_type=schema.TextLine(),
    )
示例#3
0
class UfficiostampaSettingsEditForm(controlpanel.RegistryEditForm):
    """
    """

    schema = IRerUfficiostampaSettings
    id = "UfficiostampaSettingsEditForm"
    label = _(u"Ufficio Stampa settings")
    description = u""

    fields = field.Fields(IRerUfficiostampaSettings)
    fields["legislatures"].widgetFactory = JSONFieldWidget

    @property
    def can_manage_settings(self):
        current = api.user.get_current()
        return api.user.has_permission("rer.ufficiostampa: Manage Settings",
                                       user=current)

    def updateWidgets(self):
        """
        """
        super(UfficiostampaSettingsEditForm, self).updateWidgets()
        self.widgets["legislatures"].schema = ILegislaturesRowSchema

        if not self.can_manage_settings:
            fields = [
                "token_secret",
                "token_salt",
                "frontend_url",
                "external_sender_url",
                "css_styles",
                "comunicato_number",
                "comunicato_year",
            ]
            for field_id in fields:
                self.widgets[field_id].mode = HIDDEN_MODE

    @button.buttonAndHandler(_(u"Save"), name="save")
    def handleSave(self, action):
        super(UfficiostampaSettingsEditForm, self).handleSave(self, action)

    @button.buttonAndHandler(_(u"Cancel"), name="cancel")
    def handleCancel(self, action):
        if not self.can_manage_settings:
            api.portal.show_message(
                message=_(u"Changes canceled."),
                type="info",
                request=self.request,
            )
            self.request.response.redirect(u"{0}/channels-management".format(
                api.portal.get().absolute_url()))
        else:
            super(UfficiostampaSettingsEditForm,
                  self).handleCancel(self, action)
示例#4
0
 def label(self):
     types_tool = api.portal.get_tool(name="portal_types")
     return _(
         "send_form_title",
         u"Send ${type}",
         mapping={"type": types_tool.getTypeInfo(self.context.portal_type).title},
     )
示例#5
0
 def parse_query(self):
     data = json_body(self.request)
     if "file" not in data:
         raise BadRequest(
             _("missing_file",
               default=u"You need to pass a file at least."))
     return data
示例#6
0
    def send(self, message, mto, site_title):
        portal = api.portal.get()
        overview_controlpanel = getMultiAdapter((portal, self.request),
                                                name="overview-controlpanel")
        if overview_controlpanel.mailhost_warning():
            logger.error("MailHost is not configured.")
            return False

        registry = getUtility(IRegistry)
        encoding = registry.get("plone.email_charset", "utf-8")
        mailHost = api.portal.get_tool(name="MailHost")
        subject = translate(
            _(
                "cancel_subscription_subject_label",
                default=u"Manage channels subscriptions cancel for ${site}",
                mapping={"site": site_title},
            ),
            context=self.request,
        )
        try:
            mailHost.send(
                message,
                mto=mto,
                mfrom=mail_from(),
                subject=subject,
                charset=encoding,
                msg_type="text/html",
                immediate=True,
            )
        except (SMTPException, RuntimeError) as e:
            logger.exception(e)
            return False
        return True
示例#7
0
 def handleCancel(self, action):
     return self.return_with_message(
         message=_(
             "cancel_action",
             default=u"Action cancelled",
         ),
         type=u"info",
     )
示例#8
0
class ICancelSubscriptionsRequestForm(Interface):
    """ define field to manage subscriptions """

    email = schema.Email(
        title=_(u"manage_subscriptions_email_title", default=u"Email"),
        description=u"",
        required=True,
    )
示例#9
0
 def add_send_error_message(self):
     api.portal.show_message(
         message=_(
             "error_send_mail",
             default=u"Error sending mail. Contact site administrator.",
         ),
         request=self.request,
         type="error",
     )
示例#10
0
    def send_external(self, data, body):
        frontend_url = self.get_value_from_settings(field="frontend_url")
        external_sender_url = self.get_value_from_settings(field="external_sender_url")

        channel_url = api.portal.get().absolute_url()
        if frontend_url:
            channel_url = frontend_url
        subscribers = self.get_subscribers(data)
        send_uid = self.set_history_start(data=data, subscribers=len(subscribers))

        payload = {
            "channel_url": channel_url,
            "subscribers": subscribers,
            "subject": self.subject,
            "mfrom": mail_from(),
            "text": body,
            "send_uid": send_uid,
        }

        params = {"url": external_sender_url}
        attachments = self.get_attachments_external(data)
        if attachments:
            params["data"] = payload
            params["files"] = self.get_attachments_external(data)
        else:
            params["data"] = json.dumps(payload)
            params["headers"] = {"Content-Type": "application/json"}

        try:
            response = requests.post(**params)
        except (ConnectionError, Timeout) as e:
            logger.exception(e)
            self.add_send_error_message()
            if send_uid:
                self.update_history(send_id=send_uid, status="error")
            return
        if response.status_code != 200:
            logger.error(
                'Unable to send "{message}": {reason}'.format(  # noqa
                    message=self.subject,
                    reason=response.text,
                )
            )
            self.add_send_error_message()
            if send_uid:
                self.update_history(send_id=send_uid, status="error")
            return
        # finish status will be managed via async calls
        api.portal.show_message(
            message=_(
                "success_send_mail_async",
                default=u"Send queued with success. "
                u"See the status in send history.",
            ),
            request=self.request,
            type="info",
        )
示例#11
0
 def handleCancel(self, action):
     api.portal.show_message(
         message=_(
             "cancel_action",
             default=u"Action cancelled",
         ),
         type=u"info",
         request=self.request,
     )
     return self.request.response.redirect(self.context.absolute_url())
示例#12
0
 def handleCancel(self, action):
     if not self.can_manage_settings:
         api.portal.show_message(
             message=_(u"Changes canceled."),
             type="info",
             request=self.request,
         )
         self.request.response.redirect(u"{0}/channels-management".format(
             api.portal.get().absolute_url()))
     else:
         super(UfficiostampaSettingsEditForm,
               self).handleCancel(self, action)
示例#13
0
 def send_notify_unsubscription(self, channels, record, deleted=False):
     portal = api.portal.get()
     site_title = get_site_title()
     overview_controlpanel = getMultiAdapter((portal, self.request),
                                             name="overview-controlpanel")
     if overview_controlpanel.mailhost_warning():
         logger.error("MailHost is not configured.")
         return False
     registry = getUtility(IRegistry)
     encoding = registry.get("plone.email_charset", "utf-8")
     mailHost = api.portal.get_tool(name="MailHost")
     subject = translate(
         _(
             "subscriptions_updated_label",
             default=u"Subscriptions updated for ${site}",
             mapping={"site": site_title},
         ),
         context=self.request,
     )
     message = prepare_email_message(
         context=api.portal.get(),
         template="@@cancel_subscriptions_notify_template",
         parameters={
             "site_title":
             site_title,
             "name":
             "{} {}".format(
                 record.attrs.get("surname", ""),
                 record.attrs.get("name", ""),
             ),
             "email":
             record.attrs.get("email", ""),
             "deleted":
             deleted,
             "channels":
             channels,
         },
     )
     mto = mail_from()
     try:
         mailHost.send(
             message,
             mto=mto,
             mfrom=mto,
             subject=subject,
             charset=encoding,
             msg_type="text/html",
             immediate=True,
         )
     except (SMTPException, RuntimeError) as e:
         logger.exception(e)
         return False
     return True
示例#14
0
 def handleSave(self, action):
     data, errors = self.extractData()
     if errors:
         self.status = self.formErrorsMessage
         return
     tool = getUtility(ISubscriptionsStore)
     subscription_id = data.get("uid", None)
     record = tool.get_record(subscription_id)
     if not record:
         msg = _(
             u"manage_subscriptions_inexistent_mail",
             default=u"Mail not found. Unable to change settings.",
         )
         return self.return_with_message(message=msg, type="error")
     record_channels = record.attrs.get("channels", [])
     data_channels = data.get("channels", [])
     removed_channels = [
         x for x in record_channels if x not in data_channels
     ]
     if not data_channels:
         # completely unsubscribed, so remove it from the db
         tool.delete(id=subscription_id)
         self.send_notify_unsubscription(channels=removed_channels,
                                         record=record,
                                         deleted=True)
     else:
         tool.update(
             id=subscription_id,
             data={"channels": data.get("channels")},
         )
         self.send_notify_unsubscription(channels=removed_channels,
                                         record=record,
                                         deleted=False)
     return self.return_with_message(
         message=_(
             "cancel_subscriptions_success",
             default=u"Cancel registered.",
         ),
         type=u"info",
     )
示例#15
0
def check_emails(value):
    """Check that all values are valid email addresses"""
    reg_tool = api.portal.get_tool(name="portal_registration")
    for address in value:
        if not reg_tool.isValidEmail(address):
            raise Invalid(
                _(
                    "validation_invalid_email",
                    default="Invalid email address: ${address}",
                    mapping={"address": address},
                )
            )
    return True
示例#16
0
 def add(self, data):
     old_record = self.search(query={"email": data.get("email", "")})
     if old_record:
         msg = translate(
             _(
                 "address_already_registered",
                 default=u"E-mail address already registered.",
             ),
             context=getRequest(),
         )
         if six.PY2:
             msg = msg.encode("utf-8")
         raise ValueError(msg)
     return super(SubscriptionsStore, self).add(data=data)
示例#17
0
    def get_csv_data(self, data):
        if data.get("content-type", "") != "text/comma-separated-values":
            raise BadRequest(
                _(
                    "wrong_content_type",
                    default=u"You need to pass a csv file.",
                ))
        csv_data = data["data"]
        if data.get("encoding", "") == "base64":
            csv_data = base64.b64decode(csv_data)
            try:
                csv_data = csv_data.decode()
            except UnicodeDecodeError:
                pass
            csv_value = StringIO(csv_data)
        else:
            csv_value = csv_data

        try:
            dialect = csv.Sniffer().sniff(csv_data, delimiters=";,")
            return {
                "csv":
                csv.DictReader(
                    csv_value,
                    lineterminator=dialect.lineterminator,
                    quoting=dialect.quoting,
                    doublequote=dialect.doublequote,
                    delimiter=dialect.delimiter,
                    quotechar=dialect.quotechar,
                )
            }
        except Exception as e:
            logger.exception(e)
            return {
                "error": _("error_reading_csv",
                           default=u"Error reading csv file.")
            }
示例#18
0
 def handleSave(self, action):
     data, errors = self.extractData()
     if not self.get_subscribers(data=data):
         raise ActionExecutionError(
             Invalid(
                 _(
                     "empty_subscribers",
                     default=u"You need to provide at least one email address or channel.",  # noqa
                 )
             )
         )
     if errors:
         self.status = self.formErrorsMessage
         return
     return self.sendMessage(data=data)
示例#19
0
class ICancelSubscriptionsForm(Interface):
    """  """

    channels = schema.List(
        title=_(
            u"manage_subscriptions_channels_title",
            default=u"Deselect the channels that you do not want to be "
            u"subscribed anymore.",
        ),
        description=u"",
        required=False,
        defaultFactory=getSubscriptions,
        missing_value=(),
        value_type=schema.Choice(source=subscriptionsVocabulary),
    )
    uid = schema.Int(readonly=True, defaultFactory=getUid)
示例#20
0
class ISendForm(Interface):
    channels = schema.List(
        title=_(u"send_channels_title", default=u"Channels"),
        description=_(
            u"send_channels_description",
            default=u"Select which channels should receive this Comunicato. "
            u"All email address subscribed to this channel will receive it. ",
        ),
        required=False,
        missing_value=(),
        value_type=schema.Choice(source="rer.ufficiostampa.vocabularies.channels"),
    )
    additional_addresses = schema.List(
        title=_(u"additional_addresses_title", default=u"Additional addresses"),
        description=_(
            u"additional_addresses_description",
            default=u"Insert a list of additional addressed that will receive "
            u"the mail. One per line. You can use this field also for testing "
            u"without sending emails to all subscribed addresses.",
        ),
        required=False,
        missing_value=(),
        value_type=schema.TextLine(),
        constraint=check_emails,
    )
    notes = schema.Text(
        title=_(u"notes_title", default=u"Notes"),
        description=_(
            u"notes_description",
            default=u"Additional notes.",
        ),
        required=False,
    )
    attachments = schema.List(
        title=_(u"send_attachments_title", default=u"Attachments"),
        description=_(
            u"send_attachments_description",
            default=u"Select which attachment you want to send via email. "
            u"You can only select first level Files and Images.",
        ),
        required=False,
        missing_value=(),
        value_type=schema.Choice(source="rer.ufficiostampa.vocabularies.attachments"),
        defaultFactory=default_attachments,
    )
示例#21
0
    def send_internal(self, data, body):
        portal = api.portal.get()
        overview_controlpanel = getMultiAdapter(
            (portal, self.request), name="overview-controlpanel"
        )
        if overview_controlpanel.mailhost_warning():
            return {"error": "MailHost is not configured."}
        subscribers = self.get_subscribers(data)
        registry = getUtility(IRegistry)
        encoding = registry.get("plone.email_charset", "utf-8")

        msg = EmailMessage()
        msg.set_content(body)
        msg["Subject"] = self.subject
        msg["From"] = mail_from()
        msg["Reply-To"] = mail_from()
        msg.replace_header("Content-Type", 'text/html; charset="utf-8"')

        self.manage_attachments(data=data, msg=msg)
        host = api.portal.get_tool(name="MailHost")
        msg["Bcc"] = ", ".join(subscribers)
        send_id = self.set_history_start(data=data, subscribers=len(subscribers))

        try:
            host.send(msg, charset=encoding)
        except (SMTPException, RuntimeError) as e:
            logger.exception(e)
            self.add_send_error_message()
            self.update_history(send_id=send_id, status="error")
            return
        api.portal.show_message(
            message=_(
                "success_send_mail",
                default=u"Send complete.",
            ),
            request=self.request,
            type="info",
        )

        if send_id:
            self.update_history(send_id=send_id, status="success")
示例#22
0
    def handleSave(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        email = data.get("email", None)
        tool = getUtility(ISubscriptionsStore)
        subscriptions = tool.search(query={"email": email})
        if not subscriptions:
            msg = _(
                u"manage_subscriptions_request_inexistent_mail",
                default=u"Mail not found. Unable to send the link.",
            )
            api.portal.show_message(message=msg,
                                    request=self.request,
                                    type=u"error")
            return

        subscription = subscriptions[0]
        # create CSRF token
        token = createToken()

        # sign data
        serializer = self.get_serializer()
        if not serializer:
            msg = _(
                u"manage_subscriptions_request_serializer_error",
                default=u"Serializer secret and salt not set in control panel."
                u" Unable to send the link.",
            )
            api.portal.show_message(message=msg,
                                    request=self.request,
                                    type=u"error")
            return
        secret = serializer.dumps({
            "id": subscription.intid,
            "email": subscription.attrs.get("email", ""),
        })

        # send confirm email
        url = "{url}/cancel-subscriptions?secret={secret}&_authenticator={token}".format(  # noqa
            url=self.context.absolute_url(),
            secret=secret,
            token=token)
        site_title = get_site_title()
        mail_text = prepare_email_message(
            context=api.portal.get(),
            template="@@cancel_subscriptions_mail_template",
            parameters={
                "url": url,
                "site_title": site_title
            },
        )

        res = self.send(message=mail_text, mto=email, site_title=site_title)
        if not res:
            msg = _(
                u"manage_subscriptions_not_send",
                default=u"Unable to send manage subscriptions link. "
                u"Please contact site administrator.",
            )
            msg_type = "error"
        else:
            msg = _(
                u"cancel_subscriptions_send_success",
                default=u"You will receive an email with a link to manage "
                u"your cancellation.",
            )
            msg_type = "info"
        api.portal.show_message(message=msg,
                                request=self.request,
                                type=msg_type)
示例#23
0
class CancelSubscriptionsForm(form.Form):
    label = _("cancel_subscriptions_request_title", u"Delete me")
    description = _(
        "manage_subscriptions_help",
        u"This is the list of your subscriptions.",
    )
    ignoreContext = True
    fields = field.Fields(ICancelSubscriptionsForm)
    fields["channels"].widgetFactory = CheckBoxFieldWidget

    def updateWidgets(self):
        super(CancelSubscriptionsForm, self).updateWidgets()
        self.widgets["uid"].mode = HIDDEN_MODE

    def render(self):
        data = decode_token()
        if data.get("error", ""):
            return self.return_with_message(message=data["error"],
                                            type=u"error")
        return super(CancelSubscriptionsForm, self).render()

    def return_with_message(self, message, type):
        api.portal.show_message(
            message=message,
            request=self.request,
            type=type,
        )
        return self.request.response.redirect(api.portal.get().absolute_url())

    @button.buttonAndHandler(_(u"save_button", default="Save"))
    def handleSave(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return
        tool = getUtility(ISubscriptionsStore)
        subscription_id = data.get("uid", None)
        record = tool.get_record(subscription_id)
        if not record:
            msg = _(
                u"manage_subscriptions_inexistent_mail",
                default=u"Mail not found. Unable to change settings.",
            )
            return self.return_with_message(message=msg, type="error")
        record_channels = record.attrs.get("channels", [])
        data_channels = data.get("channels", [])
        removed_channels = [
            x for x in record_channels if x not in data_channels
        ]
        if not data_channels:
            # completely unsubscribed, so remove it from the db
            tool.delete(id=subscription_id)
            self.send_notify_unsubscription(channels=removed_channels,
                                            record=record,
                                            deleted=True)
        else:
            tool.update(
                id=subscription_id,
                data={"channels": data.get("channels")},
            )
            self.send_notify_unsubscription(channels=removed_channels,
                                            record=record,
                                            deleted=False)
        return self.return_with_message(
            message=_(
                "cancel_subscriptions_success",
                default=u"Cancel registered.",
            ),
            type=u"info",
        )

    @button.buttonAndHandler(_(u"cancel_button", default="Cancel"),
                             name="cancel")
    def handleCancel(self, action):
        return self.return_with_message(
            message=_(
                "cancel_action",
                default=u"Action cancelled",
            ),
            type=u"info",
        )

    def send_notify_unsubscription(self, channels, record, deleted=False):
        portal = api.portal.get()
        site_title = get_site_title()
        overview_controlpanel = getMultiAdapter((portal, self.request),
                                                name="overview-controlpanel")
        if overview_controlpanel.mailhost_warning():
            logger.error("MailHost is not configured.")
            return False
        registry = getUtility(IRegistry)
        encoding = registry.get("plone.email_charset", "utf-8")
        mailHost = api.portal.get_tool(name="MailHost")
        subject = translate(
            _(
                "subscriptions_updated_label",
                default=u"Subscriptions updated for ${site}",
                mapping={"site": site_title},
            ),
            context=self.request,
        )
        message = prepare_email_message(
            context=api.portal.get(),
            template="@@cancel_subscriptions_notify_template",
            parameters={
                "site_title":
                site_title,
                "name":
                "{} {}".format(
                    record.attrs.get("surname", ""),
                    record.attrs.get("name", ""),
                ),
                "email":
                record.attrs.get("email", ""),
                "deleted":
                deleted,
                "channels":
                channels,
            },
        )
        mto = mail_from()
        try:
            mailHost.send(
                message,
                mto=mto,
                mfrom=mto,
                subject=subject,
                charset=encoding,
                msg_type="text/html",
                immediate=True,
            )
        except (SMTPException, RuntimeError) as e:
            logger.exception(e)
            return False
        return True
示例#24
0
class CancelSubscriptionsRequestForm(form.Form):
    label = _("cancel_subscriptions_request_title", u"Delete me")
    description = _(
        "cancel_subscriptions_request_help",
        u"If you want to cancel your subscriptions, please insert your email "
        u"address. You will receive an email with the link. "
        u"That link will expire in 24 hours.",
    )
    ignoreContext = True
    fields = field.Fields(ICancelSubscriptionsRequestForm)

    def updateWidgets(self):
        super(CancelSubscriptionsRequestForm, self).updateWidgets()
        if self.request.get("email", None):
            self.widgets["email"].value = self.request.get("email")

    @button.buttonAndHandler(_(u"send_button", default="Send"))
    def handleSave(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        email = data.get("email", None)
        tool = getUtility(ISubscriptionsStore)
        subscriptions = tool.search(query={"email": email})
        if not subscriptions:
            msg = _(
                u"manage_subscriptions_request_inexistent_mail",
                default=u"Mail not found. Unable to send the link.",
            )
            api.portal.show_message(message=msg,
                                    request=self.request,
                                    type=u"error")
            return

        subscription = subscriptions[0]
        # create CSRF token
        token = createToken()

        # sign data
        serializer = self.get_serializer()
        if not serializer:
            msg = _(
                u"manage_subscriptions_request_serializer_error",
                default=u"Serializer secret and salt not set in control panel."
                u" Unable to send the link.",
            )
            api.portal.show_message(message=msg,
                                    request=self.request,
                                    type=u"error")
            return
        secret = serializer.dumps({
            "id": subscription.intid,
            "email": subscription.attrs.get("email", ""),
        })

        # send confirm email
        url = "{url}/cancel-subscriptions?secret={secret}&_authenticator={token}".format(  # noqa
            url=self.context.absolute_url(),
            secret=secret,
            token=token)
        site_title = get_site_title()
        mail_text = prepare_email_message(
            context=api.portal.get(),
            template="@@cancel_subscriptions_mail_template",
            parameters={
                "url": url,
                "site_title": site_title
            },
        )

        res = self.send(message=mail_text, mto=email, site_title=site_title)
        if not res:
            msg = _(
                u"manage_subscriptions_not_send",
                default=u"Unable to send manage subscriptions link. "
                u"Please contact site administrator.",
            )
            msg_type = "error"
        else:
            msg = _(
                u"cancel_subscriptions_send_success",
                default=u"You will receive an email with a link to manage "
                u"your cancellation.",
            )
            msg_type = "info"
        api.portal.show_message(message=msg,
                                request=self.request,
                                type=msg_type)

    def get_serializer(self):
        try:
            token_secret = api.portal.get_registry_record(
                "token_secret", interface=IRerUfficiostampaSettings)
            token_salt = api.portal.get_registry_record(
                "token_salt", interface=IRerUfficiostampaSettings)
        except (KeyError, InvalidParameterError):
            return None
        if not token_secret or not token_salt:
            None
        return URLSafeTimedSerializer(token_secret, token_salt)

    def send(self, message, mto, site_title):
        portal = api.portal.get()
        overview_controlpanel = getMultiAdapter((portal, self.request),
                                                name="overview-controlpanel")
        if overview_controlpanel.mailhost_warning():
            logger.error("MailHost is not configured.")
            return False

        registry = getUtility(IRegistry)
        encoding = registry.get("plone.email_charset", "utf-8")
        mailHost = api.portal.get_tool(name="MailHost")
        subject = translate(
            _(
                "cancel_subscription_subject_label",
                default=u"Manage channels subscriptions cancel for ${site}",
                mapping={"site": site_title},
            ),
            context=self.request,
        )
        try:
            mailHost.send(
                message,
                mto=mto,
                mfrom=mail_from(),
                subject=subject,
                charset=encoding,
                msg_type="text/html",
                immediate=True,
            )
        except (SMTPException, RuntimeError) as e:
            logger.exception(e)
            return False
        return True
示例#25
0
def decode_token():
    request = getRequest()
    secret = request.form.get("secret", "")
    if not secret:
        return {
            "error":
            _(
                "unsubscribe_confirm_secret_null",
                default=
                u"Unable to manage subscriptions. Token not present.",  # noqa
            )
        }
    try:
        token_secret = api.portal.get_registry_record(
            "token_secret", interface=IRerUfficiostampaSettings)
        token_salt = api.portal.get_registry_record(
            "token_salt", interface=IRerUfficiostampaSettings)
    except (KeyError, InvalidParameterError):
        return {
            "error":
            _(
                "unsubscribe_confirm_secret_token_settings_error",
                default=
                u"Unable to manage subscriptions. Token keys not set in control panel.",  # noqa
            )
        }
    if not token_secret or not token_salt:
        return {
            "error":
            _(
                "unsubscribe_confirm_secret_token_settings_error",
                default=
                u"Unable to manage subscriptions. Token keys not set in control panel.",  # noqa
            )
        }
    serializer = URLSafeTimedSerializer(token_secret, token_salt)
    try:
        data = serializer.loads(secret, max_age=86400)
    except SignatureExpired:
        return {
            "error":
            _(
                "unsubscribe_confirm_secret_expired",
                default=u"Unable to manage subscriptions. Token expired.",
            )
        }
    except BadSignature:
        return {
            "error":
            _(
                "unsubscribe_confirm_secret_invalid",
                default=u"Unable to manage subscriptions. Invalid token.",
            )
        }
    record_id = data.get("id", "")
    email = data.get("email", "")
    if not record_id or not email:
        return {
            "error":
            _(
                "unsubscribe_confirm_invalid_parameters",
                default=u"Unable to manage subscriptions. Invalid parameters.",
            )
        }
    tool = getUtility(ISubscriptionsStore)
    record = tool.get_record(record_id)
    if not record:
        return {
            "error":
            _(
                "unsubscribe_confirm_invalid_id",
                default=u"Unable to manage subscriptions. Invalid id.",
            )
        }
    if record.attrs.get("email", "") != email:
        return {
            "error":
            _(
                "unsubscribe_confirm_invalid_email",
                default=u"Unable to manage subscriptions. Invalid email.",
            )
        }
    return {"data": record}
示例#26
0
    def reply(self):
        alsoProvides(self.request, IDisableCSRFProtection)
        query = self.parse_query()
        tool = getUtility(ISubscriptionsStore)

        clear = query.get("clear", False)
        overwrite = query.get("overwrite", False)
        if clear:
            tool.clear()
        csv_data = self.get_csv_data(data=query["file"])
        if csv_data.get("error", "") or not csv_data.get("csv", None):
            self.request.response.setStatus(500)
            return dict(error=dict(
                type="InternalServerError",
                message=csv_data.get("error", ""),
            ))
        res = {
            "skipped": [],
            "imported": 0,
        }

        i = 1
        for row in csv_data.get("csv", []):
            i += 1
            email = row.get("email", "")
            row["channels"] = row["channels"].split(",")
            if not email:
                msg = translate(
                    _(
                        "skip_no_email",
                        default=u"[${row}] - row without email",
                        mapping={"row": i},
                    ),
                    context=self.request,
                )
                logger.warning("[SKIP] - {}".format(msg))
                res["skipped"].append(msg)
                continue
            records = tool.search(query={"email": email})
            if not records:
                # add it
                record_id = tool.add(data=row)
                if not record_id:
                    msg = translate(
                        _(
                            "skip_unable_to_add",
                            default=u"[${row}] - unable to add",
                            mapping={"row": i},
                        ),
                        context=self.request,
                    )
                    logger.warning("[SKIP] - {}".format(msg))
                    res["skipped"].append(msg)
                    continue
                res["imported"] += 1
            else:
                if len(records) != 1:
                    msg = translate(
                        _(
                            "skip_duplicate_multiple",
                            default=
                            u'[${row}] - Multiple values for "${email}"',  # noqa
                            mapping={
                                "row": i,
                                "email": email
                            },
                        ),
                        context=self.request,
                    )
                    logger.warning("[SKIP] - {}".format(msg))
                    res["skipped"].append(msg)
                    continue
                record = records[0]
                if not overwrite:
                    msg = translate(
                        _(
                            "skip_duplicate",
                            default=
                            u'[${row}] - "${email}" already in database',  # noqa
                            mapping={
                                "row": i,
                                "email": email
                            },
                        ),
                        context=self.request,
                    )
                    if six.PY2:
                        msg = msg.encode("utf-8")
                    logger.warning("[SKIP] - {}".format(msg))
                    res["skipped"].append(msg)
                    continue
                else:
                    tool.update(id=record.intid, data=row)
                    res["imported"] += 1

        return res
示例#27
0
def getSearchFields():
    request = getRequest()
    portal = api.portal.get()

    return [
        {
            "id":
            "SearchableText",
            "label":
            translate(
                _("comunicati_search_text_label", default=u"Search text"),
                context=request,
            ),
            "help":
            "",
            "type":
            "text",
        },
        {
            "id":
            "portal_type",
            "label":
            translate(
                _("label_portal_type", default="Type"),
                context=request,
            ),
            "help":
            "",
            "type":
            "checkbox",
            "options":
            getTypesValues(),
            "default":
            getTypesDefault(),
            "hidden":
            api.user.is_anonymous(),
        },
        {
            "id":
            "created",
            "label":
            translate(
                _("comunicati_search_created_label", default=u"Date"),
                context=request,
            ),
            "help":
            "",
            "type":
            "date",
        },
        {
            "id":
            "legislature",
            "label":
            translate(
                _("label_legislature", default="Legislature"),
                context=request,
            ),
            "help":
            "",
            "type":
            "select",
            "multivalued":
            True,
            "options":
            getVocabularyTermsForForm(
                context=portal,
                vocab_name="rer.ufficiostampa.vocabularies.legislatures",
            ),
        },
        {
            "id":
            "arguments",
            "label":
            translate(
                _("legislature_arguments_label", default="Arguments"),
                context=request,
            ),
            "help":
            "",
            "type":
            "select",
            "multivalued":
            True,
            "options":
            getVocabularyTermsForForm(
                context=portal,
                vocab_name="rer.ufficiostampa.vocabularies.all_arguments",
            ),
        },
    ]
示例#28
0
class IRerUfficiostampaSettings(model.Schema):
    legislatures = schema.SourceText(
        title=_(
            "legislatures_label",
            default=u"List of legislatures",
        ),
        description=_(
            "legislatures_help",
            default=u"This is a list of all legislatures. The last one is the"
            u" one used to fill fields in a new Comunicato.",
        ),
        required=True,
    )

    subscription_channels = schema.List(
        title=_(u"subscription_channels_label",
                default=u"Subscription Channels"),
        description=_(
            u"subscription_channels_description",
            default=u"List of available subscription channels."
            u"One per line."
            u"These channels will be used for users subscriptions "
            u"and for select to which channel send a Comunicato.",
        ),
        required=True,
        default=[],
        missing_value=[],
        value_type=schema.TextLine(),
    )

    token_secret = schema.TextLine(
        title=_("token_secret_label", default=u"Token secret"),
        description=_(
            "token_secret_help",
            default=u"Insert the secret key for token generation.",
        ),
        required=True,
    )
    token_salt = schema.TextLine(
        title=_("token_salt_label", default=u"Token salt"),
        description=_(
            "token_salt_help",
            default=u"Insert the salt for token generation. This, in "
            u"conjunction with the secret, will generate unique tokens for "
            u"subscriptions management links.",
        ),
        required=True,
    )

    frontend_url = schema.TextLine(
        title=_("frontend_url_label", default=u"Frontend URL"),
        description=_(
            "frontend_url_help",
            default=u"If the frontend site is published with a different URL "
            u"than the backend, insert it here. All links in emails will be "
            u"converted with that URL.",
        ),
        required=False,
    )
    external_sender_url = schema.TextLine(
        title=_("external_sender_url_label", default=u"External sender URL"),
        description=_(
            "external_sender_url_help",
            default=u"If you want to send emails with an external tool "
            u"(rer.newsletterdispatcher.flask), insert the url of the service "
            u"here. If empty, all emails will be sent from Plone.",
        ),
        required=False,
    )

    css_styles = schema.SourceText(
        title=_(
            "css_styles_label",
            default=u"Styles",
        ),
        description=_(
            "css_styles_help",
            default=u"Insert a list of CSS styles for received emails.",
        ),
        required=True,
    )
    comunicato_number = schema.Int(
        title=_(
            "comunicato_number_label",
            default=u"Comunicato number",
        ),
        description=_(
            "comunicato_number_help",
            default=u"The number of last sent Comunicato. You don't have to "
            "edit this. It's automatically updated when a Comunicato is published.",  # noqa
        ),
        required=True,
        default=0,
    )
    comunicato_year = schema.Int(
        title=_(
            "comunicato_year_label",
            default=u"Comunicato year",
        ),
        description=_(
            "comunicato_year_help",
            default=u"You don't have to edit this. It's automatically updated"
            u" on every new year.",
        ),
        required=True,
        default=2021,
    )
示例#29
0
class SendForm(form.Form):
    description = _(
        "send_form_help",
        u"Send this Comunicato or Invito to a list of recipients.",
    )
    ignoreContext = True
    fields = field.Fields(ISendForm)
    fields["channels"].widgetFactory = CheckBoxFieldWidget
    fields["attachments"].widgetFactory = CheckBoxFieldWidget

    @property
    def label(self):
        types_tool = api.portal.get_tool(name="portal_types")
        return _(
            "send_form_title",
            u"Send ${type}",
            mapping={"type": types_tool.getTypeInfo(self.context.portal_type).title},
        )

    @button.buttonAndHandler(_(u"send_button", default="Send"))
    def handleSave(self, action):
        data, errors = self.extractData()
        if not self.get_subscribers(data=data):
            raise ActionExecutionError(
                Invalid(
                    _(
                        "empty_subscribers",
                        default=u"You need to provide at least one email address or channel.",  # noqa
                    )
                )
            )
        if errors:
            self.status = self.formErrorsMessage
            return
        return self.sendMessage(data=data)

    @button.buttonAndHandler(_(u"cancel_button", default="Cancel"), name="cancel")
    def handleCancel(self, action):
        api.portal.show_message(
            message=_(
                "cancel_action",
                default=u"Action cancelled",
            ),
            type=u"info",
            request=self.request,
        )
        return self.request.response.redirect(self.context.absolute_url())

    def sendMessage(self, data):
        external_sender_url = self.get_value_from_settings(field="external_sender_url")

        body = prepare_email_message(
            context=self.context,
            template="@@send_mail_template",
            parameters={
                "notes": data.get("notes", ""),
                "site_title": get_site_title(),
                "date": DateTime(),
                "folders": self.get_folders_attachments(),
            },
        )

        if external_sender_url:
            self.send_external(data=data, body=body)
        else:
            self.send_internal(data=data, body=body)
        return self.request.response.redirect(self.context.absolute_url())

    def get_value_from_settings(self, field):
        try:
            return api.portal.get_registry_record(
                field, interface=IRerUfficiostampaSettings
            )
        except (KeyError, InvalidParameterError):
            return None
        return None

    def set_history_start(self, data, subscribers):
        # if it's a preview, do not store infos
        if not data.get("channels", []):
            return ""

        # mark as sent
        self.context.message_sent = True

        tool = getUtility(ISendHistoryStore)
        intid = tool.add(
            {
                "type": self.type_name,
                "title": self.context.Title(),
                "number": getattr(self.context, "comunicato_number", ""),
                "url": self.context.absolute_url(),
                "recipients": subscribers,
                "channels": data.get("channels", []),
                "status": "sending",
            }
        )
        return intid

    def update_history(self, send_id, status):
        tool = getUtility(ISendHistoryStore)
        res = tool.update(
            id=send_id,
            data={"completed_date": datetime.now(), "status": status},
        )
        if res and "error" in res:
            logger.error(
                'Unable to update history with id "{}": {}'.format(
                    send_id, res["error"]
                )
            )

    @property
    @memoize
    def type_name(self):
        types_tool = api.portal.get_tool(name="portal_types")
        return types_tool.getTypeInfo(self.context.portal_type).title

    @property
    @memoize
    def subject(self):
        value = u"{type}: {title}".format(
            type=self.context.portal_type == "ComunicatoStampa"
            and "Comunicato Regione"  # noqa
            or "Invito Regione",  # noqa
            title=self.context.title,
        )
        if six.PY2 and HAS_FTYFY:
            return fix_text(value)
        return value

    def get_subscribers(self, data):
        channels = data.get("channels", [])
        subscribers = set()
        tool = getUtility(ISubscriptionsStore)
        for channel in channels:
            records = tool.search(query={"channels": channel})
            subscribers.update([x.attrs.get("email", "") for x in records])

        subscribers.update(data.get("additional_addresses", []))
        return sorted(list(subscribers))

    def get_folders_attachments(self):
        if self.context.portal_type == "InvitoStampa":
            return []
        return self.context.listFolderContents(
            contentFilter={"portal_type": ["Folder"]}
        )

    def get_attachments(self, data):
        attachments = []
        for item_id in data.get("attachments", []):
            item = self.context.get(item_id, None)
            if not item:
                continue
            field = item.portal_type == "Image" and item.image or item.file
            attachments.append(
                {
                    "data": field.data,
                    "filename": field.filename,
                    "content_type": item.content_type(),
                }
            )
        return attachments

    def get_attachments_external(self, data):
        attachments = []
        for item_id in data.get("attachments", []):
            item = self.context.get(item_id, None)
            if not item:
                continue
            field = item.portal_type == "Image" and item.image or item.file
            attachments.append(
                (
                    field.filename,
                    (field.filename, field.open(), item.content_type()),
                )
            )
        return attachments

    def manage_attachments(self, data, msg):
        attachments = self.get_attachments(data=data)
        for attachment in attachments:
            msg.add_attachment(
                attachment["data"],
                maintype=attachment["content_type"],
                subtype=attachment["content_type"],
                filename=attachment["filename"],
            )

    def add_send_error_message(self):
        api.portal.show_message(
            message=_(
                "error_send_mail",
                default=u"Error sending mail. Contact site administrator.",
            ),
            request=self.request,
            type="error",
        )

    # main methods

    def send_internal(self, data, body):
        portal = api.portal.get()
        overview_controlpanel = getMultiAdapter(
            (portal, self.request), name="overview-controlpanel"
        )
        if overview_controlpanel.mailhost_warning():
            return {"error": "MailHost is not configured."}
        subscribers = self.get_subscribers(data)
        registry = getUtility(IRegistry)
        encoding = registry.get("plone.email_charset", "utf-8")

        msg = EmailMessage()
        msg.set_content(body)
        msg["Subject"] = self.subject
        msg["From"] = mail_from()
        msg["Reply-To"] = mail_from()
        msg.replace_header("Content-Type", 'text/html; charset="utf-8"')

        self.manage_attachments(data=data, msg=msg)
        host = api.portal.get_tool(name="MailHost")
        msg["Bcc"] = ", ".join(subscribers)
        send_id = self.set_history_start(data=data, subscribers=len(subscribers))

        try:
            host.send(msg, charset=encoding)
        except (SMTPException, RuntimeError) as e:
            logger.exception(e)
            self.add_send_error_message()
            self.update_history(send_id=send_id, status="error")
            return
        api.portal.show_message(
            message=_(
                "success_send_mail",
                default=u"Send complete.",
            ),
            request=self.request,
            type="info",
        )

        if send_id:
            self.update_history(send_id=send_id, status="success")

    def send_external(self, data, body):
        frontend_url = self.get_value_from_settings(field="frontend_url")
        external_sender_url = self.get_value_from_settings(field="external_sender_url")

        channel_url = api.portal.get().absolute_url()
        if frontend_url:
            channel_url = frontend_url
        subscribers = self.get_subscribers(data)
        send_uid = self.set_history_start(data=data, subscribers=len(subscribers))

        payload = {
            "channel_url": channel_url,
            "subscribers": subscribers,
            "subject": self.subject,
            "mfrom": mail_from(),
            "text": body,
            "send_uid": send_uid,
        }

        params = {"url": external_sender_url}
        attachments = self.get_attachments_external(data)
        if attachments:
            params["data"] = payload
            params["files"] = self.get_attachments_external(data)
        else:
            params["data"] = json.dumps(payload)
            params["headers"] = {"Content-Type": "application/json"}

        try:
            response = requests.post(**params)
        except (ConnectionError, Timeout) as e:
            logger.exception(e)
            self.add_send_error_message()
            if send_uid:
                self.update_history(send_id=send_uid, status="error")
            return
        if response.status_code != 200:
            logger.error(
                'Unable to send "{message}": {reason}'.format(  # noqa
                    message=self.subject,
                    reason=response.text,
                )
            )
            self.add_send_error_message()
            if send_uid:
                self.update_history(send_id=send_uid, status="error")
            return
        # finish status will be managed via async calls
        api.portal.show_message(
            message=_(
                "success_send_mail_async",
                default=u"Send queued with success. "
                u"See the status in send history.",
            ),
            request=self.request,
            type="info",
        )