Source code for ploneintranet.invitations.invitations

from datetime import datetime
from datetime import timedelta

from Products.CMFCore.interfaces import ISiteRoot
from zope.annotation import IAnnotations
from zope.component import getUtility
from Products.Five import BrowserView
from zope.globalrequest import getRequest
from plone import api
from BTrees.OOBTree import OOBTree
from plone.protect import CheckAuthenticator, PostOnly

from ploneintranet.invitations.interfaces import ITokenUtility
from ploneintranet.core import ploneintranetCoreMessageFactory as _


ANNOTATION_KEY = "ploneintranet.invitations.invitation_storage"


[docs]class InviteUser(BrowserView): """ Control panel view for sending invitation emails to potential new users of the Plone site THIS IS EXAMPLE CODE AND NEEDS ATTENTION. """ def __call__(self): email = self.request.form.get("email") if email is not None: CheckAuthenticator(self.request) PostOnly(self.request) self.invite_user(email) return self.index()
[docs] def invite_user(self, email): """ Get new token, build email and send it to the given email address :param email: Email address to send invitation to, and for new user :type email: str :return: Tuple of token id and token url """ # TODO - check that this email address is not already registered # TODO - Body and subject to become content portal = api.portal.get() token_util = getUtility(ITokenUtility) days = 3 valid_until, expire_seconds = _ceil_datetime(days=days) token_id, token_url = token_util.generate_new_token( expire_seconds=expire_seconds ) _store_invite(token_id, email) message = """You've been invited to join %s. The following is a unique URL tied to your email address (%s). Clicking the link will create you an account and log in you automatically. %s This token will be valid %d days and expire on %s. Once your account has been created, you can also set up a password to access the site here: %s/mail_password_form?userid=%s """ % ( portal.Title(), email, token_url, days, valid_until, portal.absolute_url(), email, ) api.portal.send_email( recipient=email, subject="Your invitation to %s" % portal.Title(), body=message, ) api.portal.show_message(_("Invitation sent to %s") % email, self.request) return token_id, token_url
[docs]def accept_invitation(event): """ Event handler for :class:`AcceptToken` event fired by invitation framework FIXME #1047: this creates a acl user, not a ploneintranet userprofile Additionally, this code makes the heroic assumption that email == username == userid which is very brittle, see #1043. :param event: The event object :type event: :class:`AcceptToken` :return: """ request = getRequest() email = _get_storage().get(event.token_id) if email is None: api.portal.show_message(_("The token has expired."), request) return acl_users = api.portal.get_tool("acl_users") if api.user.get(username=email) is None: api.user.create(email=email, username=email) acl_users.updateCredentials(request, request.response, email, None)
[docs]def _get_storage(clear=False): portal = getUtility(ISiteRoot) annotations = IAnnotations(portal) if ANNOTATION_KEY not in annotations or clear: annotations[ANNOTATION_KEY] = OOBTree() return annotations[ANNOTATION_KEY]
[docs]def _store_invite(token_id, email): storage = _get_storage() storage[token_id] = email
[docs]def _ceil_datetime(days=3, now=datetime.now()): """ Helper method to ceil the datetime to a quarter of a hour and calculate the total_seconds until that moment. :param days: the amount of days from now. :type days: int :param now: The current moment. This is a parameter so we can test. :type now: datetime :return: (datetime and int) """ valid_until = now + timedelta(days=days) valid_until += timedelta(minutes=15) valid_until.replace(minute=valid_until.minute % 15).replace(second=0).replace( microsecond=0 ) expire_seconds = (valid_until - now).total_seconds() return valid_until, expire_seconds