Refactor alarm handlers.
authorJan Schneider <jan@horde.org>
Fri, 7 May 2010 15:46:10 +0000 (17:46 +0200)
committerJan Schneider <jan@horde.org>
Fri, 7 May 2010 15:46:10 +0000 (17:46 +0200)
They have been split out into separate classes, are unit tested, and new
handlers can be dropped in.

framework/Alarm/lib/Horde/Alarm.php
framework/Alarm/lib/Horde/Alarm/Handler.php [new file with mode: 0644]
framework/Alarm/lib/Horde/Alarm/Handler/Mail.php [new file with mode: 0644]
framework/Alarm/lib/Horde/Alarm/Handler/Notify.php [new file with mode: 0644]
framework/Alarm/package.xml
framework/Alarm/test/Horde/Alarm/HandlerTest.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Binder/Alarm.php
framework/Core/lib/Horde/Core/Prefs/Ui/Widgets.php
horde/admin/alarms.php
kronolith/index.php

index e7d9518..eac2533 100644 (file)
@@ -34,6 +34,20 @@ abstract class Horde_Alarm
     );
 
     /**
+     * All registered notification handlers.
+     *
+     * @var array
+     */
+    protected $_handlers = array();
+
+    /**
+     * Whether handler classes have been dynamically loaded already.
+     *
+     * @var boolean
+     */
+    protected $_handlersLoaded = false;
+
+    /**
      * Attempts to return a concrete instance based on $driver.
      *
      * @param string $driver  The type of concrete subclass to
@@ -444,91 +458,31 @@ abstract class Horde_Alarm
             return;
         }
 
-        $methods = array_keys($this->notificationMethods());
+        $handlers = $this->handlers();
         foreach ($alarms as $alarm) {
             foreach ($alarm['methods'] as $alarm_method) {
-                if (in_array($alarm_method, $methods) &&
+                if (isset($handlers[$alarm_method]) &&
                     !in_array($alarm_method, $exclude)) {
-                    try {
-                        $this->{'_' . $alarm_method}($alarm);
-                    } catch (Horde_Alarm_Exception $e) {
-                        if ($this->_logger) {
-                            $this->_logger->log($e, 'ERR');
-                        }
-                    }
+                    $handlers[$alarm_method]->notify($alarm);
                 }
             }
         }
     }
 
     /**
-     * Notifies about an alarm through Horde_Notification.
+     * Registers a notification handler.
      *
-     * @param array $alarm  An alarm hash.
+     * @param string $name                  A handler name.
+     * @param Horde_Alarm_Handler $handler  A notification handler.
      */
-    protected function _notify(array $alarm)
+    public function addHandler($name, Horde_Alarm_Handler $handler)
     {
-        static $sound_played;
-
-        $GLOBALS['notification']->push($alarm['title'], 'horde.alarm', array('alarm' => $alarm));
-        if (!empty($alarm['params']['notify']['sound']) &&
-            !isset($sound_played[$alarm['params']['notify']['sound']])) {
-            $GLOBALS['notification']->attach('audio');
-            $GLOBALS['notification']->push($alarm['params']['notify']['sound'], 'audio');
-            $sound_played[$alarm['params']['notify']['sound']] = true;
-        }
+        $this->_handlers[$name] = $handler;
+        $handler->alarm = $this;
     }
 
     /**
-     * Notifies about an alarm by email.
-     *
-     * @param array $alarm  An alarm hash.
-     *
-     * @throws Horde_Mime_Exception
-     * @throws Horde_Alarm_Exception
-     */
-    protected function _mail(array $alarm)
-    {
-        if (!empty($alarm['internal']['mail']['sent'])) {
-            return;
-        }
-
-        if (empty($alarm['params']['mail']['email'])) {
-            if (empty($alarm['user'])) {
-                return;
-            }
-            $identity = $GLOBALS['injector']->getInstance('Horde_Prefs_Identity')->getIdentity($alarm['user']);
-            $email = $identity->getDefaultFromAddress(true);
-        } else {
-            $email = $alarm['params']['mail']['email'];
-        }
-
-        $mail = new Horde_Mime_Mail(array(
-            'subject' => $alarm['title'],
-            'body' => empty($alarm['params']['mail']['body']) ? $alarm['text'] : $alarm['params']['mail']['body'],
-            'to' => $email,
-            'from' => $email,
-            'charset' => Horde_Nls::getCharset()
-        ));
-        $mail->addHeader('Auto-Submitted', 'auto-generated');
-        $mail->addHeader('X-Horde-Alarm', $alarm['title'], Horde_Nls::getCharset());
-        $mail->send($GLOBALS['injector']->getInstance('Mail'));
-
-        $alarm['internal']['mail']['sent'] = true;
-        $this->_internal($alarm['id'], $alarm['user'], $alarm['internal']);
-    }
-
-    /**
-     * Notifies about an alarm with an SMS through the sms/send API method.
-     *
-     * @param array $alarm  An alarm hash.
-     */
-    protected function _sms(array $alarm)
-    {
-    }
-
-    /**
-     * Returns a list of available notification methods and method parameters.
+     * Returns a list of available notification handlers and parameters.
      *
      * The returned list is a hash with method names as the keys and
      * optionally associated parameters as values. The parameters are hashes
@@ -539,40 +493,27 @@ abstract class Horde_Alarm
      *
      * @return array  List of methods and parameters.
      */
-    public function notificationMethods()
+    public function handlers()
     {
-        static $methods;
-
-        if (!isset($methods)) {
-            $methods = array(
-                'notify' => array(
-                    '__desc' => _("Inline"),
-                    'sound' => array(
-                        'type' => 'sound',
-                        'desc' => _("Play a sound?"),
-                        'required' => false
-                    )
-                ),
-                'mail' => array(
-                    '__desc' => _("Email"),
-                    'email' => array(
-                        'type' => 'text',
-                        'desc' => _("Email address (optional)"),
-                        'required' => false
-                    )
-                )
-            );
-            /*
-            if ($GLOBALS['registry']->hasMethod('sms/send')) {
-                $methods['sms'] = array(
-                    'phone' => array('type' => 'text',
-                                     'desc' => _("Cell phone number"),
-                                     'required' => true));
+        if (!$this->_handlersLoaded) {
+            foreach (new GlobIterator(dirname(__FILE__) . '/Alarm/Handler/*.php') as $file) {
+                if (!$file->isFile()) {
+                    continue;
+                }
+                $handler = Horde_String::lower($file->getBasename('.php'));
+                if (isset($this->_handlers[$handler])) {
+                    continue;
+                }
+                require_once $file->getPathname();
+                $class = 'Horde_Alarm_Handler_' . $file->getBasename('.php');
+                if (class_exists($class, false)) {
+                    $this->addHandler($handler, new $class());
+                }
             }
-            */
+            $this->_handlerLoaded = true;
         }
 
-        return $methods;
+        return $this->_handlers;
     }
 
     /**
diff --git a/framework/Alarm/lib/Horde/Alarm/Handler.php b/framework/Alarm/lib/Horde/Alarm/Handler.php
new file mode 100644 (file)
index 0000000..ecf52c1
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+/**
+ * @package Horde_Alarm
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ */
+
+/**
+ * The Horde_Alarm_Handler class is an interface for all Horde_Alarm handlers
+ * that notifies of active alarms.
+ *
+ * @author  Jan Schneider <jan@horde.org>
+ * @package Horde_Alarm
+ */
+abstract class Horde_Alarm_Handler
+{
+    /**
+     * The alarm object to that this handler is attached.
+     *
+     * Horde_Alarm
+     */
+    public $alarm;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Any parameters that the handler might need.
+     */
+    abstract public function __construct(array $params = null);
+
+    /**
+     * Notifies about an alarm.
+     *
+     * @param array $alarm  An alarm hash.
+     */
+    abstract public function notify(array $alarm);
+
+    /**
+     * Returns a human readable description of the handler.
+     *
+     * @return string
+     */
+    abstract public function getDescription();
+
+    /**
+     * Returns a hash of user-configurable parameters for the handler.
+     *
+     * The parameters are hashes with parameter names as keys and parameter
+     * information as values. The parameter information is a hash with the
+     * following keys:
+     * - type: the parameter type as a preference type.
+     * - desc: a parameter description.
+     * - required: whether this parameter is required.
+     *
+     * @return array
+     */
+    public function getParameters()
+    {
+        return array();
+    }
+}
diff --git a/framework/Alarm/lib/Horde/Alarm/Handler/Mail.php b/framework/Alarm/lib/Horde/Alarm/Handler/Mail.php
new file mode 100644 (file)
index 0000000..bff6ef8
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+/**
+ * @package Horde_Alarm
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ */
+
+/**
+ * The Horde_Alarm_Handler_Mail class is a Horde_Alarm handler that notifies
+ * of active alarms by e-mail.
+ *
+ * @author  Jan Schneider <jan@horde.org>
+ * @package Horde_Alarm
+ */
+class Horde_Alarm_Handler_Mail extends Horde_Alarm_Handler
+{
+    /**
+     * An identity factory.
+     *
+     * @var Horde_Core_Factory_Identity
+     */
+    protected $_identity;
+
+    /**
+     * A Mail object.
+     *
+     * @var Mail
+     */
+    protected $_mail;
+
+    /**
+     * The message charset.
+     *
+     * @var string
+     */
+    protected $_charset;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Any parameters that the handler might need.
+     *                       Required parameter:
+     *                       - identity: An identity factory that implements
+     *                                   getIdentity().
+     *                       - mail: A PEAR Mail instance.
+     *                       - charset: The charset of the messages.
+     */
+    public function __construct(array $params = null)
+    {
+        foreach (array('identity', 'mail', 'charset') as $param) {
+            if (!isset($params[$param])) {
+                throw new Horde_Alarm_Exception('Parameter \'' . $param . '\' missing.');
+            }
+        }
+        if (!method_exists($params['identity'], 'getIdentity')) {
+            throw new Horde_Alarm_Exception('Parameter \'identity\' does not implement getIdentity().');
+        }
+        $r = new ReflectionObject($params['mail']);
+        if (!($params['mail'] instanceof Mail)) {
+            throw new Horde_Alarm_Exception('Parameter \'mail\' is not a Mail object.');
+        }
+        $this->_identity = $params['identity'];
+        $this->_mail     = $params['mail'];
+        $this->_charset  = $params['charset'];
+    }
+
+    /**
+     * Notifies about an alarm by e-mail.
+     *
+     * @param array $alarm  An alarm hash.
+     */
+    public function notify(array $alarm)
+    {
+        if (!empty($alarm['internal']['mail']['sent'])) {
+            return;
+        }
+
+        if (empty($alarm['params']['mail']['email'])) {
+            if (empty($alarm['user'])) {
+                return;
+            }
+            $email = $this->_identity
+                ->getIdentity($alarm['user'])
+                ->getDefaultFromAddress(true);
+        } else {
+            $email = $alarm['params']['mail']['email'];
+        }
+
+        $mail = new Horde_Mime_Mail(array(
+            'subject' => $alarm['title'],
+            'body' => empty($alarm['params']['mail']['body']) ? $alarm['text'] : $alarm['params']['mail']['body'],
+            'to' => $email,
+            'from' => $email,
+            'charset' => $this->_charset
+        ));
+        $mail->addHeader('Auto-Submitted', 'auto-generated');
+        $mail->addHeader('X-Horde-Alarm', $alarm['title'], $this->_charset);
+        $mail->send($this->_mail);
+
+        $alarm['internal']['mail']['sent'] = true;
+        $this->alarm->internal($alarm['id'], $alarm['user'], $alarm['internal']);
+    }
+
+    /**
+     * Returns a human readable description of the handler.
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return _("Email");
+    }
+
+    /**
+     * Returns a hash of user-configurable parameters for the handler.
+     *
+     * The parameters are hashes with parameter names as keys and parameter
+     * information as values. The parameter information is a hash with the
+     * following keys:
+     * - type: the parameter type as a preference type.
+     * - desc: a parameter description.
+     * - required: whether this parameter is required.
+     *
+     * @return array
+     */
+    public function getParameters()
+    {
+        return array(
+            'email' => array(
+                'type' => 'text',
+                'desc' => _("Email address (optional)"),
+                'required' => false));
+    }
+}
diff --git a/framework/Alarm/lib/Horde/Alarm/Handler/Notify.php b/framework/Alarm/lib/Horde/Alarm/Handler/Notify.php
new file mode 100644 (file)
index 0000000..d2470ad
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * @package Horde_Alarm
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ */
+
+/**
+ * The Horde_Alarm_Handler_Notification class is a Horde_Alarm handler that
+ * notifies of active alarms over the Horde_Notification system.
+ *
+ * @author  Jan Schneider <jan@horde.org>
+ * @package Horde_Alarm
+ */
+class Horde_Alarm_Handler_Notify extends Horde_Alarm_Handler
+{
+    /**
+     * A notification handler.
+     *
+     * @var Horde_Notification_Handler
+     */
+    protected $_notification;
+
+    /**
+     * Whether a sound already had been played during the page request.
+     *
+     * @var boolean
+     */
+    protected $_soundPlayed = false;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Any parameters that the handler might need.
+     *                       Required parameter:
+     *                       - notification: A Horde_Notification_Handler
+     *                         instance.
+     */
+    public function __construct(array $params = null)
+    {
+        /*
+        if (!isset($params['notification'])) {
+            throw new Horde_Alarm_Exception('Parameter \'notification\' missing.');
+        }
+        if (!($params['notification'] instanceof Horde_Notification_Handler)) {
+            throw new Horde_Alarm_Exception('Parameter \'notification\' is not a Horde_Notification_Handler object.');
+        }
+        $this->_notification = $params['notification'];
+        */
+        $this->_notification = $GLOBALS['injector']->getInstance('Horde_Notification');
+    }
+
+    /**
+     * Notifies about an alarm through Horde_Notification.
+     *
+     * @param array $alarm  An alarm hash.
+     */
+    public function notify(array $alarm)
+    {
+        $this->_notification->push($alarm['title'], 'horde.alarm', array('alarm' => $alarm));
+        if (!empty($alarm['params']['notify']['sound']) &&
+            !isset($this->_soundPlayed[$alarm['params']['notify']['sound']])) {
+            $this->_notification->attach('audio');
+            $this->_notification->push($alarm['params']['notify']['sound'], 'audio');
+            $this->_soundPlayed[$alarm['params']['notify']['sound']] = true;
+        }
+    }
+
+    /**
+     * Returns a human readable description of the handler.
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return _("Inline");
+    }
+
+    /**
+     * Returns a hash of user-configurable parameters for the handler.
+     *
+     * The parameters are hashes with parameter names as keys and parameter
+     * information as values. The parameter information is a hash with the
+     * following keys:
+     * - type: the parameter type as a preference type.
+     * - desc: a parameter description.
+     * - required: whether this parameter is required.
+     *
+     * @return array
+     */
+    public function getParameters()
+    {
+        return array(
+            'sound' => array(
+                'type' => 'sound',
+                'desc' => _("Play a sound?"),
+                'required' => false));
+    }
+}
index ad2bd39..1f5d5b4 100644 (file)
@@ -1,8 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
-http://pear.php.net/dtd/tasks-1.0.xsd
-http://pear.php.net/dtd/package-2.0
-http://pear.php.net/dtd/package-2.0.xsd">
+<package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  <name>Alarm</name>
  <channel>pear.horde.org</channel>
  <summary>Horde alarm libraries</summary>
@@ -16,7 +13,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <email>jan@horde.org</email>
   <active>yes</active>
  </lead>
- <date>2009-02-11</date>
+ <date>2010-05-07</date>
+ <time>17:43:45</time>
  <version>
   <release>0.2.0</release>
   <api>0.2.0</api>
@@ -26,13 +24,20 @@ http://pear.php.net/dtd/package-2.0.xsd">
   <api>beta</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Initial Horde 4 package.</notes>
+ <notes>
+* Initial Horde 4 package.
+ </notes>
  <contents>
-  <dir name="/">
+  <dir baseinstalldir="/" name="/">
    <dir name="lib">
     <dir name="Horde">
      <dir name="Alarm">
+      <dir name="Handler">
+       <file name="Mail.php" role="php" />
+       <file name="Notify.php" role="php" />
+      </dir> <!-- /lib/Horde/Alarm/Handler -->
       <file name="Exception.php" role="php" />
+      <file name="Handler.php" role="php" />
       <file name="Null.php" role="php" />
       <file name="Object.php" role="php" />
       <file name="Sql.php" role="php" />
@@ -40,12 +45,18 @@ http://pear.php.net/dtd/package-2.0.xsd">
      <file name="Alarm.php" role="php" />
     </dir> <!-- /lib/Horde -->
    </dir> <!-- /lib -->
+   <dir name="migrations">
+    <file name="1_horde_alarms_table.php" role="php" />
+   </dir> <!-- /migrations -->
    <dir name="test">
     <dir name="Horde">
      <dir name="Alarm">
       <file name="AllTests.php" role="test" />
-      <file name="SqlTest.php" role="test" />
       <file name="conf.php.dist" role="test" />
+      <file name="HandlerTest.php" role="test" />
+      <file name="NullTest.php" role="test" />
+      <file name="ObjectTest.php" role="test" />
+      <file name="SqlTest.php" role="test" />
      </dir> <!-- /test/Horde/Alarm -->
     </dir> <!-- /test/Horde -->
    </dir> <!-- /test -->
@@ -74,18 +85,39 @@ http://pear.php.net/dtd/package-2.0.xsd">
   </required>
   <optional>
    <package>
+    <name>Mime</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
+    <name>Notification</name>
+    <channel>pear.horde.org</channel>
+   </package>
+   <package>
     <name>Log</name>
     <channel>pear.horde.org</channel>
    </package>
+   <package>
+    <name>Prefs</name>
+    <channel>pear.horde.org</channel>
+   </package>
   </optional>
  </dependencies>
  <phprelease>
   <filelist>
-   <install name="lib/Horde/Alarm/Exception.php" as="Horde/Alarm/Exception.php" />
-   <install name="lib/Horde/Alarm/Null.php" as="Horde/Alarm/Null.php" />
-   <install name="lib/Horde/Alarm/Object.php" as="Horde/Alarm/Object.php" />
-   <install name="lib/Horde/Alarm/Sql.php" as="Horde/Alarm/Sql.php" />
-   <install name="lib/Horde/Alarm.php" as="Horde/Alarm.php" />
+   <install as="Horde/Alarm.php" name="lib/Horde/Alarm.php" />
+   <install as="Horde/Alarm/Exception.php" name="lib/Horde/Alarm/Exception.php" />
+   <install as="Horde/Alarm/Handler.php" name="lib/Horde/Alarm/Handler.php" />
+   <install as="Horde/Alarm/Null.php" name="lib/Horde/Alarm/Null.php" />
+   <install as="Horde/Alarm/Object.php" name="lib/Horde/Alarm/Object.php" />
+   <install as="Horde/Alarm/Sql.php" name="lib/Horde/Alarm/Sql.php" />
+   <install as="Horde/Alarm/Handler/Mail.php" name="lib/Horde/Alarm/Handler/Mail.php" />
+   <install as="Horde/Alarm/Handler/Notify.php" name="lib/Horde/Alarm/Handler/Notify.php" />
+   <install as="Horde/Alarm/AllTests.php" name="test/Horde/Alarm/AllTests.php" />
+   <install as="Horde/Alarm/conf.php.dist" name="test/Horde/Alarm/conf.php.dist" />
+   <install as="Horde/Alarm/HandlerTest.php" name="test/Horde/Alarm/HandlerTest.php" />
+   <install as="Horde/Alarm/NullTest.php" name="test/Horde/Alarm/NullTest.php" />
+   <install as="Horde/Alarm/ObjectTest.php" name="test/Horde/Alarm/ObjectTest.php" />
+   <install as="Horde/Alarm/SqlTest.php" name="test/Horde/Alarm/SqlTest.php" />
   </filelist>
  </phprelease>
  <changelog>
@@ -100,7 +132,24 @@ http://pear.php.net/dtd/package-2.0.xsd">
     <api>beta</api>
    </stability>
    <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
-   <notes>* Initial release.</notes>
+   <notes>
+* Initial release.
+   </notes>
+  </release>
+  <release>
+   <version>
+    <release>0.2.0</release>
+    <api>0.2.0</api>
+   </version>
+   <stability>
+    <release>beta</release>
+    <api>beta</api>
+   </stability>
+   <date>2010-05-07</date>
+   <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+   <notes>
+* Initial Horde 4 package.
+   </notes>
   </release>
  </changelog>
 </package>
diff --git a/framework/Alarm/test/Horde/Alarm/HandlerTest.php b/framework/Alarm/test/Horde/Alarm/HandlerTest.php
new file mode 100644 (file)
index 0000000..9790592
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+/**
+ * @author     Jan Schneider <jan@horde.org>
+ * @license    http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @category   Horde
+ * @package    Horde_Alarm
+ * @subpackage UnitTests
+ */
+
+class Horde_Alarm_HandlerTest extends PHPUnit_Framework_TestCase
+{
+    protected static $alarm;
+
+    public function setUp()
+    {
+        if (!class_exists('Horde_Notification')) {
+            $this->markTestSkipped('Horde_Notification not installed');
+            return;
+        }
+        self::$alarm = Horde_Alarm::factory('Object');
+        $now = time();
+        $hash = array('id' => 'personalalarm',
+                      'user' => 'john',
+                      'start' => new Horde_Date($now),
+                      'end' => new Horde_Date($now + 3600),
+                      'methods' => array(),
+                      'params' => array(),
+                      'title' => 'This is a personal alarm.',
+                      'text' => 'Action is required.');
+        self::$alarm->set($hash);
+    }
+
+    public function testNotification()
+    {
+        $alarm = self::$alarm->get('personalalarm', 'john');
+        $alarm['methods'] = array('notification');
+        self::$alarm->set($alarm);
+        $storage = new Horde_Notification_Storage_Object();
+        $handler = new Horde_Alarm_Handler_Notification(array('notification' => new Horde_Notification_Handler($storage)));
+        self::$alarm->addHandler('notification', $handler);
+        self::$alarm->notify('john', false);
+
+        $this->assertEquals(1, count($storage->notifications['_unattached']));
+        $this->assertEquals('This is a personal alarm.', $storage->notifications['_unattached'][0]->message);
+        $this->assertEquals('horde.alarm', $storage->notifications['_unattached'][0]->type);
+    }
+
+    public function testMail()
+    {
+        $alarm = self::$alarm->get('personalalarm', 'john');
+        $alarm['methods'] = array('mail');
+        self::$alarm->set($alarm);
+        $mail = new Horde_Alarm_HandlerTest_Mail();
+        $factory = new Horde_Alarm_HandlerTest_IdentityFactory();
+        $handler = new Horde_Alarm_Handler_Mail(array('mail' => $mail, 'identity' => $factory, 'charset' => 'us-ascii'));
+        self::$alarm->addHandler('mail', $handler);
+        self::$alarm->notify('john', false);
+        $regexp = <<<EOR
+Subject: This is a personal alarm\.
+To: john@example\.com
+From: john@example\.com
+Auto-Submitted: auto-generated
+X-Horde-Alarm: This is a personal alarm\.
+Message-ID: <\d{14}\.Horde\.\w+@\w+>
+User-Agent: Horde Application Framework 4
+Date: \w{3}, \d\d \w{3} \d{4} \d\d:\d\d:\d\d [+-]\d{4}
+Content-Type: text\/plain; charset=us-ascii; format=flowed; DelSp=Yes
+MIME-Version: 1\.0
+
+Action is required\.
+
+EOR;
+
+        $this->assertRegExp('/' . trim(str_replace("\r\n", "\n", $regexp)) . '/', trim(str_replace("\r\n", "\n", $mail->sentOutput)));
+        $mail->sentOutput = null;
+        self::$alarm->notify('john', false);
+        $this->assertNull($mail->sentOutput);
+    }
+}
+
+class Horde_Alarm_HandlerTest_IdentityFactory
+{
+    public function getIdentity()
+    {
+        return new Horde_Alarm_HandlerTest_Identity();
+    }
+}
+
+class Horde_Alarm_HandlerTest_Identity
+{
+    public function getDefaultFromAddress()
+    {
+        return 'john@example.com';
+    }
+}
+
+class Horde_Alarm_HandlerTest_Mail extends Mail
+{
+    public $sentOutput;
+
+    public function send($recipients, $headers, $body)
+    {
+        list(, $textHeaders) = Mail::prepareHeaders($headers);
+        $this->sentOutput = $textHeaders . "\n\n" . $body;
+    }
+}
index 3e36c1d..658cba8 100644 (file)
@@ -13,10 +13,24 @@ class Horde_Core_Binder_Alarm implements Horde_Injector_Binder
             $driver = $GLOBALS['conf']['alarms']['driver'];
             $params = Horde::getDriverConfig('alarms', $driver);
         }
-
         $params['logger'] = $injector->getInstance('Horde_Log_Logger');
 
-        return Horde_Alarm::factory($driver, $params);
+        $alarm = Horde_Alarm::factory($driver, $params);
+
+        /* Add those handlers that need configuration and can't be auto-loaded
+         * through Horde_Alarms::handlers(). */
+        /*
+        $handler_params = array(
+            'notification' => $injector->getInstance('Horde_Notification'));
+        $alarm->addHandler('notify', new Horde_Alarm_Handler_Notification($handler_params));
+        */
+        $handler_params = array(
+            'identity' => $injector->getInstance('Horde_Prefs_Identity'),
+            'mail' => $injector->getInstance('Mail'),
+            'charset' => Horde_Nls::getCharset());
+        $alarm->addHandler('mail', new Horde_Alarm_Handler_Mail($handler_params));
+
+        return $alarm;
     }
 
     public function equals(Horde_Injector_Binder $binder)
index 41f9203..bce7bb0 100644 (file)
@@ -294,68 +294,62 @@ class Horde_Core_Prefs_Ui_Widgets
 
         $param_list = $select_list = array();
 
-        foreach (Horde_Alarm::notificationMethods() as $method => $params) {
+        foreach ($GLOBALS['injector']->getInstance('Horde_Alarm')->handlers() as $method => $handler) {
             $select_list[] = array(
-                'l' => $params['__desc'],
+                'l' => $handler->getDescription(),
                 's' => in_array($method, $selected),
                 'v' => $method
             );
 
-            if (count($params > 1)) {
-                $tmp = array(
-                    'method' => $method,
-                    'param' => array()
-                );
-
-                foreach ($params as $name => $param) {
-                    if (substr($name, 0, 2) == '__') {
-                        continue;
-                    }
+            $tmp = array(
+                'method' => $method,
+                'param' => array()
+            );
 
-                    switch ($param['type']) {
-                    case 'text':
-                        $tmp['param'][] = array(
-                            'label' => Horde::label($pref . '_' . $name, $param['desc']),
-                            'name' => $pref . '_' . $name,
-                            'text' => true,
-                            'value' => empty($alarm_pref[$method][$name]) ? '' : htmlspecialchars($alarm_pref[$method][$name])
-                            );
-                        break;
-
-                    case 'bool':
-                        $tmp['param'][] = array(
-                            'bool' => true,
-                            'checked' => !empty($alarm_pref[$method][$name]),
-                            'label' => Horde::label($pref . '_' . $name, $param['desc']),
-                            'name' => $pref . '_' . $name
+            foreach ($handler->getParameters() as $name => $param) {
+                switch ($param['type']) {
+                case 'text':
+                    $tmp['param'][] = array(
+                        'label' => Horde::label($pref . '_' . $name, $param['desc']),
+                        'name' => $pref . '_' . $name,
+                        'text' => true,
+                        'value' => empty($alarm_pref[$method][$name]) ? '' : htmlspecialchars($alarm_pref[$method][$name])
                         );
-                        break;
-
-                    case 'sound':
-                        $current_sound = empty($alarm_pref[$method][$name])
-                            ? ''
-                            : $alarm_pref[$method][$name];
-                        $sounds = array();
-                        foreach (Horde_Themes::soundList() as $key => $val) {
-                            $sounds[] = array(
-                                'c' => ($current_sound == $key),
-                                'uri' => htmlspecialchars($val->uri),
-                                'val' => htmlspecialchars($key)
-                            );
-                        }
-                        $t->set('sounds', $sounds);
-
-                        $tmp['param'][] = array(
-                            'sound' => true,
-                            'checked' => !$current_sound,
-                            'name' => $pref . '_' . $name
+                    break;
+
+                case 'bool':
+                    $tmp['param'][] = array(
+                        'bool' => true,
+                        'checked' => !empty($alarm_pref[$method][$name]),
+                        'label' => Horde::label($pref . '_' . $name, $param['desc']),
+                        'name' => $pref . '_' . $name
+                    );
+                    break;
+
+                case 'sound':
+                    $current_sound = empty($alarm_pref[$method][$name])
+                        ? ''
+                        : $alarm_pref[$method][$name];
+                    $sounds = array();
+                    foreach (Horde_Themes::soundList() as $key => $val) {
+                        $sounds[] = array(
+                            'c' => ($current_sound == $key),
+                            'uri' => htmlspecialchars($val->uri),
+                            'val' => htmlspecialchars($key)
                         );
-                        break;
                     }
+                    $t->set('sounds', $sounds);
+
+                    $tmp['param'][] = array(
+                        'sound' => true,
+                        'checked' => !$current_sound,
+                        'name' => $pref . '_' . $name
+                    );
+                    break;
                 }
-
-                $param_list[] = $tmp;
             }
+
+            $param_list[] = $tmp;
         }
 
         $t->set('desc', Horde::label($pref, $data['label']));
@@ -383,7 +377,7 @@ class Horde_Core_Prefs_Ui_Widgets
     static public function alarmUpdate($ui, $data)
     {
         $pref = $data['pref'];
-        $methods = Horde_Alarm::notificationMethods();
+        $methods = $GLOBALS['injector']->getInstance('Horde_Alarm')->handlers();
         $val = (isset($ui->vars->$pref) && is_array($ui->vars->$pref))
             ? $ui->vars->$pref
             : array();
@@ -392,12 +386,10 @@ class Horde_Core_Prefs_Ui_Widgets
         foreach ($val as $method) {
             $value[$method] = array();
             if (!empty($methods[$method])) {
-                foreach (array_keys($methods[$method]) as $param) {
+                foreach ($methods[$method]->getParameters() as $param => $info) {
                     $value[$method][$param] = $ui->vars->get($pref . '_' . $param, '');
-                    if (is_array($methods[$method][$param]) &&
-                        $methods[$method][$param]['required'] &&
-                        ($value[$method][$param] === '')) {
-                        $GLOBALS['notification']->push(sprintf(_("You must provide a setting for \"%s\"."), $methods[$method][$param]['desc']), 'horde.error');
+                    if ($info['required'] && ($value[$method][$param] === '')) {
+                        $GLOBALS['notification']->push(sprintf(_("You must provide a setting for \"%s\"."), $methods[$method]->getDescription()), 'horde.error');
                         return null;
                     }
                 }
index 865274d..ebd5446 100644 (file)
@@ -13,8 +13,8 @@ Horde_Registry::appInit('horde', array('admin' => true));
 
 $horde_alarm = $injector->getInstance('Horde_Alarm');
 $methods = array();
-foreach ($horde_alarm->notificationMethods() as $name => $method) {
-    $methods[$name] = $method['__desc'];
+foreach ($horde_alarm->handlers() as $name => $method) {
+    $methods[$name] = $method->getDescription();
 }
 
 $vars = Horde_Variables::getDefaultVariables();
@@ -26,15 +26,13 @@ $form->addVariable(_("Alarm start"), 'start', 'datetime', true);
 $form->addVariable(_("Alarm end"), 'end', 'datetime', false);
 $form->addVariable(_("Alarm text"), 'text', 'longtext', false);
 $form->addVariable(_("Alarm methods"), 'methods', 'multienum', true, false, null, array($methods, min(5, count($methods))));
-foreach ($horde_alarm->notificationMethods() as $name => $method) {
-    if (count($method) < 2) {
+foreach ($horde_alarm->handlers() as $name => $method) {
+    $params = $method->getParameters();
+    if (!count($params)) {
         continue;
     }
-    $form->addVariable($method['__desc'], '', 'header', false);
-    foreach ($method as $param => $param_info) {
-        if (substr($param, 0, 2) == '__') {
-            continue;
-        }
+    $form->addVariable($method->getDescription(), '', 'header', false);
+    foreach ($params as $param => $param_info) {
         $form->addVariable($param_info['desc'], $name . '_' . $param, $param_info['type'], false);
     }
 }
index 7951b13..4213a2f 100644 (file)
@@ -37,16 +37,14 @@ $_SESSION['horde_notification']['override'] = array(
 );
 
 $alarm_methods = $alarm_params = '';
-foreach (Horde_Alarm::notificationMethods() as $method => $params) {
-    $alarm_methods .= ' <input type="checkbox" name="event_alarms[]" id="kronolithEventAlarm' . $method . '" value="' . $method . '" /> <label for="kronolithEventAlarm' . $method . '">' . $params['__desc'] . '</label>';
-    if (count($params) < 2) {
+foreach ($injector->getInstance('Horde_Alarm')->handlers() as $method => $handler) {
+    $alarm_methods .= ' <input type="checkbox" name="event_alarms[]" id="kronolithEventAlarm' . $method . '" value="' . $method . '" /> <label for="kronolithEventAlarm' . $method . '">' . $handler->getDescription() . '</label>';
+    $params = $handler->getParameters();
+    if (!count($params)) {
         continue;
     }
     $alarm_params .= ' <div id="kronolithEventAlarm' . $method . 'Params" style="display:none">';
     foreach ($params as $name => $param) {
-        if (substr($name, 0, 2) == '__') {
-            continue;
-        }
         $alarm_params .= ' <label for="kronolithEventAlarmParam' . $name
             . '">' . $param['desc'] . '</label> ';
         $name_att = 'name="event_alarms_' . $name . '"';