示例#1
0
def includeme(config):
    config.add_view(
        TOSForm,
        context=IRoot,
        name="tos_form",
        permission=PERM_VIEW,
        renderer="arche:templates/form.pt",
    )
    config.add_view(
        AgreedTOSView,
        context=IUser,
        name="agreed_tos",
        permission=PERM_VIEW,
        renderer="arche_tos:templates/agreed_tos.pt",
    )
    config.add_view(
        RevokeAgreementForm,
        context=IUser,
        name="revoke_agreement",
        permission=PERM_EDIT,
        renderer="arche:templates/form.pt",
    )
    config.add_view(
        ManageTOSView,
        context=IRoot,
        name="_manage_tos",
        permission=PERM_MANAGE_USERS,
        renderer="arche_tos:templates/manage_tos.pt",
    )
    config.add_view(
        ListRevokedUsers,
        context=IRoot,
        name="_list_revoked_tos_users",
        permission=PERM_MANAGE_USERS,
        renderer="arche_tos:templates/list_revoked_users.pt",
    )
    config.add_view(
        TOSSettings,
        context=IRoot,
        name="_tos_settings",
        permission=PERM_MANAGE_USERS,
        renderer="arche:templates/form.pt",
    )
    config.add_exception_view(terms_not_accepted, context=TermsNotAccepted)
    config.add_view_action(
        agreed_tos_menu_item,
        "user_menu",
        "agreed_tos",
        title=_("Terms of service"),
        priority=40,
    )
    config.add_view_action(
        generic_submenu_items,
        "site_menu",
        "manage_tos",
        title=_("Manage TOS"),
        permission=PERM_MANAGE_USERS,
        priority=50,
        view_name="_manage_tos",
    )
示例#2
0
 def __call__(self, node, value):
     allowed_userids = set()
     for userid in self.root.users.keys():
         if principal_has_permisson(self.request,
                                    userid,
                                    PERM_MANAGE_USERS,
                                    context=self.root):
             allowed_userids.add(userid)
     non_admins = set(value) - allowed_userids
     if non_admins:
         msg = _(
             "users_arent_admins_error",
             default=
             "The following users don't have the permission to manage users on this site: '${users}' "
             "Perhaps they aren't admins?",
             mapping={"users": "', '".join(non_admins)},
         )
         raise colander.Invalid(node, msg)
     for userid in allowed_userids:
         if not self.root.users[userid].email:
             msg = _(
                 "user_bad_email_error",
                 default="${userid} doesn't have an email address set.",
                 mapping={"userid": userid},
             )
             raise colander.Invalid(node, msg)
示例#3
0
class TOSAgreeSchema(colander.Schema):
    widget = maybe_modal_form
    agree_check = colander.SchemaNode(
        colander.Bool(),
        title=_("I've read the full agreement and I agree to it"),
        validator=colander.Function(
            _return, _("You must agree to the terms to use this site.")),
    )
示例#4
0
 def agree_success(self, appstruct):
     self.flash_messages.add(_("Thank you!"), type="success")
     self.tos_manager.agree_to(self.tos_manager.find_tos())
     if self.use_ajax:
         return Response(
             render("arche:templates/deform/destroy_modal.pt", {},
                    request=self.request))
     return HTTPFound(location=self.request.resource_url(self.context))
示例#5
0
def typed_revoke_description(node, kw):
    request = kw["request"]
    return _(
        "Type '${sentence}' to confirm that you want to do this.",
        mapping={
            "sentence": request.localizer.translate(TYPED_REVOKE_SENTENCE)
        },
    )
示例#6
0
 def save_success(self, appstruct):
     if appstruct != self.appstruct():
         self.settings.clear()
         self.settings.update(appstruct)
         self.flash_messages.add(self.default_success, type="success")
     else:
         self.flash_messages.add(_("No changes made"))
     return self._relocate_response()
示例#7
0
def available_languages(node, kw):
    request = kw["request"]
    langs = list(request.root.site_settings.get("languages", ()))
    if request.localizer.locale_name not in langs:
        langs.insert(0, request.localizer.locale_name)
    values = [(x, x) for x in langs]
    values.insert(0, ("", _("Any language")))
    return deform.widget.SelectWidget(values=values)
示例#8
0
def enable_typed_revoke_description(node, kw):
    request = kw["request"]
    return _(
        "Require typing '${sentence}' on revoke.",
        mapping={
            "sentence": request.localizer.translate(TYPED_REVOKE_SENTENCE)
        },
    )
示例#9
0
 def tos(self):
     uid = self.request.params.get("tos", None)
     if uid:
         # Current user probably hasn't got view permission on that object as default.
         # Which is correct, it shouldn't be part of the site.
         tos = self.request.resolve_uid(uid, perm=None)
         if not ITOS.providedBy(tos):
             raise HTTPNotFound(_("Agreement not found"))
         return tos
示例#10
0
class TOSRevokeSchema(colander.Schema):
    typed_revoke = colander.SchemaNode(
        colander.String(),
        title=_("Type confirmation"),
        description=typed_revoke_description,
        validator=TypedRevokeValidator,
    )
    checked_revoke = colander.SchemaNode(
        colander.Bool(),
        title=_(
            "I understand the consequences and want to revoke my agreement."),
        validator=colander.Function(
            _return, _("Read above and tick here if you want to do this.")),
    )
    current_password = colander.SchemaNode(
        colander.String(),
        title=_("Password check due to severe consequences of revoking this"),
        widget=deform.widget.PasswordWidget(size=20),
        validator=deferred_current_password_validator,
    )

    def after_bind(self, schema, kw):
        request = kw["request"]
        view = kw["view"]
        tos = view.tos
        remove_fields = set()
        # If current user doesn't have a password, we can't really check against that
        if not request.profile.password:
            remove_fields.add("current_password")
        # Don't enforce fields on inactive TOS
        if tos.wf_state == "enabled":
            if not tos.check_password_on_revoke:
                remove_fields.add("current_password")
            if not tos.check_typed_on_revoke:
                remove_fields.add("typed_revoke")
        else:
            remove_fields.update(["current_password", "typed_revoke"])
        if "typed_revoke" not in remove_fields:
            remove_fields.add("checked_revoke")
        for k in remove_fields:
            if k in schema:
                del schema[k]
示例#11
0
def includeme(config):
    """
    Include components

    In your paster ini file, add the following keys to customize:
    # Number of seconds to wait before kicking out users that haven't agreed
    arche_tos.grace_seconds = <int>
    # Number of seconds to wait between each check
    arche_tos.check_interval = <int>
    """
    settings = config.registry.settings
    prefix = "arche_tos.%s"
    for k in ("grace_seconds", "check_interval"):
        key = prefix % k
        if key in settings:
            val = int(settings[key])
            setattr(TOSManager, k, val)
    config.registry.registerAdapter(TOSManager)
    config.registry.registerAdapter(AgreedTOS)
    config.registry.registerAdapter(RevokedTOS)
    config.registry.registerAdapter(TOSSettings)
    config.add_subscriber(check_terms, [IBaseView, IViewInitializedEvent])
    config.add_subscriber(email_data_consent_managers, IImportantAgreementsRevoked)
    config.add_ref_guard(
        protect_enabled_tos,
        requires=(ITOS,),
        catalog_result=False,
        allow_move=True,
        title=_("This would delete enabled Terms of service"),
    )
    config.add_ref_guard(
        protect_tos_folder,
        requires=(IArcheFolder,),
        catalog_result=False,
        allow_move=True,
        title=_(
            "ref_guard_deleting_marked_folder",
            default="Deleting the folder marked as base for terms of service isn't allowed. "
            "See terms of service settings.",
        ),
    )
示例#12
0
class TOSSettingsSchema(colander.Schema):
    data_consent_managers = colander.SchemaNode(
        colander.Set(),
        title=_("Data Consent Manager(s)"),
        descruption=_(
            "Users handling consent issues - must have administrator rights"),
        widget=UserReferenceWidget(),
        validator=OnlyAdministratorsWithEmailValidator,
    )
    email_consent_managers = colander.SchemaNode(
        colander.Bool(),
        title=_("Notify via email?"),
        description=_(
            "email_consent_managers_desc",
            default=
            "Email consent managers when a user revokes an important agreement.",
        ),
    )
    tos_folder = colander.SchemaNode(
        colander.String(),
        title=_("Folder to place TOS in"),
        description=_(
            "tos_folder_schema_description",
            default=
            "If you don't have any folders yet, create one in the root of your site."
        ),
        widget=ReferenceWidget(multiple=False,
                               query_params={"type_name": "Folder"}))
示例#13
0
 def revoke_success(self, appstruct):
     important_revoked = self.tos_manager.revoke_agreement(self.tos)
     if important_revoked:
         msg = _(
             "revoked_consent_enabled_tos",
             default="You've revoked your consent. "
             "Note that this website will not be usable without "
             "agreeing to these terms.",
         )
     else:
         msg = _(
             "revoked_consent_inactive_tos",
             default=
             "You've revoked your consent to terms that were marked as inactive.",
         )
     self.tos_manager.clear_checked()
     self.flash_messages.add(msg,
                             type="danger",
                             require_commit=False,
                             auto_destruct=False)
     return self.relocate_response(
         self.request.resource_url(self.profile, "agreed_tos"))
示例#14
0
def terms_not_accepted(context, request):
    headers = forget(request)
    request.session.invalidate()
    fm = IFlashMessages(request)
    fm.add(
        _("You need to accept the terms to use this site."),
        type="danger",
        require_commit=False,
        auto_destruct=False,
    )
    # Context is the exception
    return HTTPFound(location=request.resource_url(request.root),
                     headers=headers)
示例#15
0
class TOSForm(BaseForm, TOSMixin):
    type_name = "TOS"
    schema_name = "agree"
    title = _("New terms require your attention")
    buttons = (deform.Button("agree", title=_("Agree")), )

    @property
    def use_ajax(self):
        return self.request.is_xhr

    def before_fields(self):
        values = {"tos_items": self.tos_manager.find_tos(), "view": self}
        return render("arche_tos:templates/tos_listing.pt",
                      values,
                      request=self.request)

    def agree_success(self, appstruct):
        self.flash_messages.add(_("Thank you!"), type="success")
        self.tos_manager.agree_to(self.tos_manager.find_tos())
        if self.use_ajax:
            return Response(
                render("arche:templates/deform/destroy_modal.pt", {},
                       request=self.request))
        return HTTPFound(location=self.request.resource_url(self.context))
示例#16
0
class TOS(Content, ContextACLMixin):
    type_name = "TOS"
    type_title = _("Terms of service")
    add_permission = 'Add TOS'
    nav_visible = False
    listing_visible = True
    search_visible = False
    title = ""
    body = ""
    collapse_text = False
    revoke_body = ""
    lang = ""
    check_password_on_revoke = False
    check_typed_on_revoke = False

    @property
    def is_active(self):
        return self.wf_state == "enabled"
示例#17
0
def email_data_consent_managers(event):
    request = event.request
    root = request.root
    settings = ITOSSettings(root)
    if settings.get("email_consent_managers", None):
        tos_manager = ITOSManager(request)
        for user in tos_manager.get_consent_managers():
            values = {
                "revoked_user": event.user,
                "revoked_tos": event.revoked_tos,
                "user": user,
                "site_title": root.title,
                "tos_link": request.resource_url(root, "_manage_tos"),
            }
            html = render(
                "arche_tos:templates/email_revoked_consent.pt", values, request
            )
            subject = _(
                "revoked_consent_subject",
                default="Revoked consent notice from ${title}",
                mapping={"title": root.title},
            )
            request.send_email(subject, [user.email], html)
示例#18
0
 def buttons(self):
     kwargs = {}
     if self.context.wf_state == "enabled":
         kwargs["css_class"] = "btn-danger"
     return (deform.Button("revoke", title=_("Revoke"),
                           **kwargs), button_cancel)
示例#19
0
 def __call__(self, node, value):
     if self.request.localizer.translate(TYPED_REVOKE_SENTENCE) != value:
         raise colander.Invalid(node, _("Wrong sentence"))
示例#20
0
def _return(value):
    return value == True  # Will be interpreted as failed check by Function method.


class TOSAgreeSchema(colander.Schema):
    widget = maybe_modal_form
    agree_check = colander.SchemaNode(
        colander.Bool(),
        title=_("I've read the full agreement and I agree to it"),
        validator=colander.Function(
            _return, _("You must agree to the terms to use this site.")),
    )


TYPED_REVOKE_SENTENCE = _("I understand the consequences")


@colander.deferred
class TypedRevokeValidator(object):
    def __init__(self, node, kw):
        self.request = kw["request"]

    def __call__(self, node, value):
        if self.request.localizer.translate(TYPED_REVOKE_SENTENCE) != value:
            raise colander.Invalid(node, _("Wrong sentence"))


@colander.deferred
def typed_revoke_description(node, kw):
    request = kw["request"]
示例#21
0
class TOSSchema(colander.Schema):
    title = colander.SchemaNode(colander.String(), title=_("Title"))
    body = colander.SchemaNode(
        colander.String(),
        title=_("Text to agree to"),
        widget=deform.widget.RichTextWidget(),
    )
    collapse_text = colander.SchemaNode(
        colander.Bool(),
        title=_("Collapse agreement text?"),
        description=_("Causes a read full text link to be displayed instead."),
    )
    revoke_body = colander.SchemaNode(
        colander.String(),
        title=_("Consequences of revoking the agreement"),
        description=_(
            "revoke_body_description",
            default="Will be displayed when the revocation form "
            "is shown to inform of the consequences. "
            "If the conseqences are severe, please do express that here!",
        ),
        widget=deform.widget.RichTextWidget(),
        missing="",
    )
    lang = colander.SchemaNode(
        colander.String(),
        title=_("Only for this language"),
        description=_(
            "If set, only show this agreement for users using this lang."),
        widget=available_languages,
        default="",
        missing="",
    )
    check_password_on_revoke = colander.SchemaNode(
        colander.Bool(),
        title=_("Require password check on revoke"),
        description=_("This will only be enforced for enabled TOS"),
        default=False,
    )
    check_typed_on_revoke = colander.SchemaNode(
        colander.Bool(),
        title=enable_typed_revoke_description,
        description=_("This will only be enforced for enabled TOS"),
        default=False,
    )
示例#22
0
class RevokeAgreementForm(BaseForm, TOSMixin):
    type_name = "TOS"
    schema_name = "revoke"
    title = _("Revoke agreement")

    @property
    def buttons(self):
        kwargs = {}
        if self.context.wf_state == "enabled":
            kwargs["css_class"] = "btn-danger"
        return (deform.Button("revoke", title=_("Revoke"),
                              **kwargs), button_cancel)

    @reify
    def tos(self):
        uid = self.request.params.get("tos", None)
        if uid:
            # Current user probably hasn't got view permission on that object as default.
            # Which is correct, it shouldn't be part of the site.
            tos = self.request.resolve_uid(uid, perm=None)
            if not ITOS.providedBy(tos):
                raise HTTPNotFound(_("Agreement not found"))
            return tos

    def before_fields(self):
        # List the consequences of revoking this agreement
        values = {"tos": self.tos, "view": self}
        return render(
            "arche_tos:templates/revoke_tos_consequence.pt",
            values,
            request=self.request,
        )

    def revoke_success(self, appstruct):
        important_revoked = self.tos_manager.revoke_agreement(self.tos)
        if important_revoked:
            msg = _(
                "revoked_consent_enabled_tos",
                default="You've revoked your consent. "
                "Note that this website will not be usable without "
                "agreeing to these terms.",
            )
        else:
            msg = _(
                "revoked_consent_inactive_tos",
                default=
                "You've revoked your consent to terms that were marked as inactive.",
            )
        self.tos_manager.clear_checked()
        self.flash_messages.add(msg,
                                type="danger",
                                require_commit=False,
                                auto_destruct=False)
        return self.relocate_response(
            self.request.resource_url(self.profile, "agreed_tos"))

    def cancel_success(self, *args):
        return self.relocate_response(
            self.request.resource_url(self.profile, "agreed_tos"))

    cancel_failure = cancel_success