SendEmail.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • workflow/
  • workflows/
  • actions/
  • SendEmail.php
  • View
  • Commits
  • Open Download .zip Download (9 KB)
<?php
/**
 * A workflow action that sends an email when transition occurs. Email is sent as a plain text,
 * where the body contains transition label, from- and to-state labels and, in the case of
 * provided record is an instance of P4Cms_Content class, content's url.
 *
 * Recognized email parameters specified via action options are:
 *
 * to       - (required if 'toRole' option is not specified) email recipients, can be a
 *            string or an array of strings; every value specifies either email address or
 *            username that will be expanded to the user email address; value can also
 *            contain coma-separated list of emails/users
 * toRole   - (required if 'to' option is not specified) role or list of roles that will be
 *            expanded to list of email addresses of the member users; value can also
 *            contain coma-separated list of roles
 * subject  - (optional) email subject, action provides default value if user doesn't specify
 *            this parameter
 * template - (optional) name of the template (may include the path relative to BASE_DIR)
 *            that will be rendered into email html body. Template will have access to
 *            the transition, record and instance of this class via 'transition', 'record' and
 *            'action' variables set to the template's view. If template is not provided by the
 *            user, then workflow module provides default one (see _renderTemplate() method
 *            for more details).
 * message  - (optional) if set, then it will be prepended to the email body
 *
 * In the case that email cannot be sent from some reason, error message is logged and the action
 * execution is silently terminated.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Workflow_Workflow_Action_SendEmail extends Workflow_ActionAbstract
{
    const   DEFAULT_TEMPLATE    = 'application/workflow/views/scripts/send-email-template.phtml';

    /**
     * Invoke this action for the given transition and record.
     *
     * @param   Workflow_Model_Transition   $transition     transition to invoke this action for.
     * @param   P4Cms_Record                $record         record to invoke this action for.
     * @return  Workflow_ActionInterface    provides fluent interface.
     */
    public function invoke(Workflow_Model_Transition $transition, P4Cms_Record $record)
    {
        // collect email options
        $to         = $this->_extract($this->getOption('to'));
        $toRole     = $this->_extract($this->getOption('toRole'));
        $subject    = $this->getOption('subject');
        $message    = $this->getOption('message');

        // list of email recipients
        $recipients = array();

        // get users from 'to' option (if set)
        if (is_array($to)) {
            // extract email addresses
            $emailValidator = new Zend_Validate_EmailAddress;
            $recipients     = array_filter($to, array($emailValidator, 'isValid'));

            // for other items (representing usernames) get emails from users details
            $usernames = array_diff($to, $recipients);
            if (count($usernames)) {
                foreach (P4Cms_User::fetchAll(array(P4Cms_User::FETCH_BY_NAME => $usernames)) as $user) {
                    $recipients[$user->getFullName()] = $user->getEmail();
                }
            }
        }

        // if toRole is specified, add member users to the recipient list
        if (is_array($toRole)) {
            foreach (P4Cms_User::fetchByRole($toRole) as $user) {
                $recipients[$user->getFullName()] = $user->getEmail();
            }
        }

        // early exit if recipients list is empty
        if (!count($recipients)) {
            P4Cms_Log::log("Cannot send email: no recipients specified.");
            return $this;
        }

        // early exit if from- or to-state is invalid
        try {
            $fromState = $transition->getFromState();
            $toState   = $transition->getToState();
        } catch (Workflow_Exception $e) {
            P4Cms_Log::log("Cannot send email: invalid 'from' or 'to' state.");
            return $this;
        }

        // provide default subject if not set by user
        if (!$subject) {
            $subject = 'Workflow Transition';
            // append record title if possible
            if ($record->hasField('title')) {
                $subject .= ': ' . $record->getValue('title');
            }
        }

        // compose html part of the email body
        try {
            $bodyHtml = $this->_renderTemplate($transition, $record);
        } catch (Exception $exception) {
            P4Cms_Log::logException("Cannot render email template.", $exception);
            return $this;
        }

        // if custom message is set, prepend it to the body
        if ($message) {
            $bodyHtml = '<p>' . $message . '</p>' . $bodyHtml;
        }

        // compose text part of the email body
        $htmlToText = new P4Cms_Filter_HtmlToText;
        $bodyText   = $htmlToText->filter($bodyHtml);

        // send email
        $this->_sendEmail($recipients, $subject, $bodyText, $bodyHtml);

        return $this;
    }

    /**
     * Send an email.
     * 
     * @param string|array  $to         email recipient.
     * @param string        $subject    email subject.
     * @param string        $bodyText   email body in the text format.
     * @param string        $bodyHtml   email body in the html format.
     */
    protected function _sendEmail($to, $subject, $bodyText, $bodyHtml)
    {
        $mail = new Zend_Mail();
        $mail->addTo($to)
             ->setSubject($subject);

        // set email body
        if ($bodyText) {
            $mail->setBodyText($bodyText);
        }
        if ($bodyHtml) {
            $mail->setBodyHtml($bodyHtml);
        }

        // set active user (if there is any) as sender
        if (P4Cms_User::hasActive()) {
            $user = P4Cms_User::fetchActive();
            $mail->setFrom($user->getEmail(), $user->getFullName());
        }

        // send the email
        try {
            $mail->send();
        } catch (Exception $exception) {
            P4Cms_Log::logException("Error when sending email.", $exception);
        }
    }

    /**
     * Convert a string or list of strings with separated items into an array of items.
     * Returns null for empty input.
     * 
     * @param   null|string|array   $input      string or list of strings to extract values from.
     * @param   string              $separator  items separator.
     * @return  null|array          array with string values extracted from input or null of
     *                              no input is given.
     */
    protected function _extract($input, $separator = ',')
    {
        // early exit if no input is given
        if (!$input) {
            return null;
        }

        $input  = (array) $input;
        $output = array();
        foreach ($input as $value) {
            // convert separated list of values into an array
            $values = explode($separator, $value);
            $values = array_map('trim', $values);

            // add values into output list
            $output = array_merge($output, $values);            
        }

        return $output;
    }

    /**
     * Return rendered template.
     *
     * @param   Workflow_Model_Transition   $transition     transition to invoke this action for.
     * @param   P4Cms_Record                $record         record to invoke this action for.
     * @return  string                      rendered template.
     */
    protected function _renderTemplate(Workflow_Model_Transition $transition, P4Cms_Record $record)
    {
        // assemble list with template candidates; first one that can be rendered
        // by the view script will be returned
        $candidates   = (array) $this->getOption('template');
        $candidates[] = static::DEFAULT_TEMPLATE;

        // we'll render template by the view cloned from MVC instance
        $view = clone Zend_Layout::getMvcInstance()->getView();

        // loop through template candidates and render first one that view
        // script can find in the script paths
        foreach ($candidates as $template) {
            // if template contains path, add path to the view script paths
            $templateDir = dirname($template);
            $name        = basename($template);
            if (strlen($templateDir) && $templateDir !== '.') {
                $path = BASE_PATH . '/' . $templateDir;
                if (is_dir($path) && is_readable($path . '/' . $name)) {
                    $view->addScriptPath($path);
                }
            }

            // return rendered template if view script can find it
            if ($view->getScriptPath(basename($name))) {                
                $view->transition = $transition;
                $view->record     = $record;
                $view->action     = $this;

                return $view->render($name);
            }
        }

        throw new Workflow_Exception("Cannot render template.");
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/workflow/workflows/actions/SendEmail.php
#1 8972 Matt Attaway Initial add of the Chronicle source code