Attention

This document was written for an unsupported version of Plone, Plone 2.5.x, and was last updated 1602 days ago.

For more information, see the version support policy.

To learn how to upgrade to the current version of Plone, read the upgrade manual.

Send emails to users to remind them of expiring content

by Barry Page last modified Dec 30, 2008 03:03 PM
To keep my site fresh, I have a workflow action that sets ExpirationDate to now + 31 days. This how-to shows a way to send emails in advance of the item expiring.

On state-change of the (custom) workflow to 'public', the following script (python) sets the ExpirationDate:

object = state_change.object
object.setExpirationDate(DateTime() + 31)

Given that we want to let our users know that the item will expire, we will send them an email. The ability to do that requires:

  1. A scheduling capability in Zope/Plone. I have chosen to use PloneMaintenance. Get it from http://plone.org/products/plonemaintenance  and install in the normal way. PloneMaintenance allows you to schedule tasks that can then be activated by visiting a URL on the site. Because it will only run tasks that were sheduled to run anyway, there is no security issue.
  2. A script in PloneMaintenance. Go to portal_maintenance tool in the ZMI, to the 'scripts' tab and add a Script (Python). Replace the contents of the default script with the following, changing the details to suit your situation:
  3. portal_url = context.portal_url
    portal = portal_url.getPortalObject()
    portal_catalog = context.portal_catalog
    expired = []
    # Notify all members of 'event-notification' 7 days and a second time 1 day ahead.

    NotifyDaysAhead = 7,1
    mtool = context.portal_membership
    site_properties = context.portal_properties.site_properties
    envelope_from = site_properties.email_from_address

    # Change the following message to suit your use case
    mMsg = """
    Your item: %s, will expire as of %s.
    If you no longer require this listing beyond that date, no action is required.
    The listing will be deleted after a further 31 days.\n
    If you wish to keep this listing on the site for a further 31 days, you can click the <relist> button on the listing.
    You will need to log in to see the <relist> button. Your listing is at:
    %s
    %s.
    """

    today = DateTime().Date() # returns a string yyyy/mm/dd
    today = DateTime(today)   # converts the string back to a date object
    for days in NotifyDaysAhead:
      # query portal catalog index for expiring items with a date range of 1 day
    # and review_state='public' (this state is peculiar to my workflow)
    # You will need to change the object type in the query to suit
       query = {'portal_type':'IBItem',
                      'expires': {'query': (today+days,today+days+1),
                      'range': 'min:max'},
                      'review_state': 'public',
                      }
       expired_brains = portal_catalog(query)

       for brain in expired_brains:
            obj = brain.getObject()
            if obj is None:
                continue

            title = obj.Title()
            owner = obj.Creator()
            expiry = obj.ExpirationDate()
            userObj = mtool.getMemberById(owner)
            addressee = {'member':userObj,
                   'id':userObj.getId(),
                   'fullname':userObj.getProperty('fullname', 'Fullname missing'),
                   'email':userObj.getProperty('email', None)}
    # This gives you a fullname too, so you could say "Dear Joe Bloggs" if you didn't think it cheesy...
            if addressee['email'] != "":
                mTo = addressee['email']
                mFrom = "info@yourhost.com" #change this
                mSubj = "Your item will expire"
                obj_url = obj.absolute_url()
                comments = "\n\nThank you for your support,\nWeb Site Administration"
                message = mMsg % (title, expiry, obj_url, comments)
                context.MailHost.simple_send(body=message, mto=mTo, mfrom=mFrom, subject=mSubj)
    return 0
  4. Create a 'task' in portal_maintenance. It will run at any time after it was scheduled to run. So if it is scheduled for 01:00 and cron runs every hour, it may run at 01:00 or 02:00. So probably best set it for 12:55 in this instance.
  5. Create a cron job to invoke the PloneMaintenance URL http://mysite.com/runMaintenanceTasks. See instructions from within the portal_maintenance tool - they are quite clear. You can run the cron job say hourly, and the script will only fire after it was due to be run anyway. I'm sure that using Windows Scheduler to go to http://mysite.com/runMaintenanceTasks will work equally well. Note that the URL in the email is relative to the referring URL, so don't use http://localhost:8080/mysite.runMaintenanceTasks, or your users will not be able to click the link.
  6. Test and enjoy.
  7. I have provided a 'relist' button on the items that gives the item another 31 days. This way I keep content fresh and don't inconvenience my users too much.

I'll post that here just for completeness sake:

In the form (in the 'custom' folder within portal_skins):
<div class="formControls">
  <form action="update_expiration_date" method="get"
         tal:condition="python: user.has_permission('Add portal content', here)
and (wtool.getInfoFor(here, 'review_state') =='public')">
          <input class="standalone"
type="submit"
name="form.button.relist"
                 value="Relist"
                 i18n:attributes="value label_relist;" />
  </form>
</div>

Calling a script (python) called update_expiration_date:
from Products.CMFPlone import PloneMessageFactory as _
request=context.REQUEST
if hasattr(request, 'form.button.relist'):   
context.setExpirationDate(DateTime() + 31)
    context.plone_utils.addPortalMessage(_(u'Item has been relisted'))
return request.RESPONSE.redirect(context.absolute_url() +'/view')




Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.