569 lines
17 KiB
PHP
569 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2007-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Alarm
|
|
*/
|
|
|
|
/**
|
|
* The Horde_Alarm class provides an interface to deal with reminders, alarms
|
|
* and notifications through a standardized API.
|
|
*
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @category Horde
|
|
* @copyright 2007-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Alarm
|
|
*/
|
|
abstract class Horde_Alarm
|
|
{
|
|
/**
|
|
* Logger.
|
|
*
|
|
* @var Horde_Log_Logger
|
|
*/
|
|
protected $_logger;
|
|
|
|
/**
|
|
* Alarm loader callback.
|
|
*
|
|
* @var mixed
|
|
*/
|
|
protected $_loader;
|
|
|
|
/**
|
|
* Hash containing connection parameters.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_params = array(
|
|
'ttl' => 300
|
|
);
|
|
|
|
/**
|
|
* All registered notification handlers.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_handlers = array();
|
|
|
|
/**
|
|
* Whether handler classes have been dynamically loaded already.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_handlersLoaded = false;
|
|
|
|
/**
|
|
* A list of errors, exceptions etc. that occured during notify() calls.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_errors = array();
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $params Configuration parameters:
|
|
* <pre>
|
|
* 'logger' - (Horde_Log_Logger) A logger instance.
|
|
* 'ttl' - (integer) Time to live value, in seconds.
|
|
* </pre>
|
|
*/
|
|
public function __construct(array $params = array())
|
|
{
|
|
if (isset($params['logger'])) {
|
|
$this->_logger = $params['logger'];
|
|
unset($params['logger']);
|
|
}
|
|
if (isset($params['loader'])) {
|
|
$this->_loader = $params['loader'];
|
|
unset($params['loader']);
|
|
}
|
|
$this->_params = array_merge($this->_params, $params);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of alarms from the backend.
|
|
*
|
|
* @param string $user Return alarms for this user, all users if
|
|
* null, or global alarms if empty.
|
|
* @param Horde_Date $time The time when the alarms should be active.
|
|
* Defaults to now.
|
|
* @param boolean $load Update active alarms from all applications?
|
|
* @param boolean $preload Preload alarms that go off within the next
|
|
* ttl time span?
|
|
*
|
|
* @return array A list of alarm hashes.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function listAlarms($user = null, Horde_Date $time = null,
|
|
$load = false, $preload = true)
|
|
{
|
|
if (empty($time)) {
|
|
$time = new Horde_Date(time());
|
|
}
|
|
if ($load && is_callable($this->_loader)) {
|
|
call_user_func($this->_loader, $user, $preload);
|
|
}
|
|
|
|
$alarms = $this->_list($user, $time);
|
|
|
|
foreach (array_keys($alarms) as $alarm) {
|
|
if (isset($alarms[$alarm]['mail']['body'])) {
|
|
$alarms[$alarm]['mail']['body'] = $this->_fromDriver($alarms[$alarm]['mail']['body']);
|
|
}
|
|
}
|
|
return $alarms;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of alarms from the backend.
|
|
*
|
|
* @param Horde_Date $time The time when the alarms should be active.
|
|
* @param string $user Return alarms for this user, all users if
|
|
* null, or global alarms if empty.
|
|
*
|
|
* @return array A list of alarm hashes.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _list($user, Horde_Date $time);
|
|
|
|
/**
|
|
* Returns a list of all global alarms from the backend.
|
|
*
|
|
* @return array A list of alarm hashes.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function globalAlarms()
|
|
{
|
|
$alarms = $this->_global();
|
|
foreach (array_keys($alarms) as $alarm) {
|
|
if (isset($alarms[$alarm]['mail']['body'])) {
|
|
$alarms[$alarm]['mail']['body'] = $this->_fromDriver($alarms[$alarm]['mail']['body']);
|
|
}
|
|
}
|
|
return $alarms;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of all global alarms from the backend.
|
|
*
|
|
* @return array A list of alarm hashes.
|
|
*/
|
|
abstract protected function _global();
|
|
|
|
/**
|
|
* Returns an alarm hash from the backend.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
*
|
|
* @return array An alarm hash. Contains the following:
|
|
* <pre>
|
|
* id: Unique alarm id.
|
|
* user: The alarm's user. Empty if a global alarm.
|
|
* start: The alarm start as a Horde_Date.
|
|
* end: The alarm end as a Horde_Date.
|
|
* methods: The notification methods for this alarm.
|
|
* params: The paramters for the notification methods.
|
|
* title: The alarm title.
|
|
* text: An optional alarm description.
|
|
* snooze: The snooze time (next time) of the alarm as a Horde_Date.
|
|
* internal: Holds internally used data.
|
|
* instanceid: Holds an instance identifier for recurring alarms.
|
|
* (@since 2.2.0)
|
|
* </pre>
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function get($id, $user)
|
|
{
|
|
$alarm = $this->_get($id, $user);
|
|
|
|
if (isset($alarm['mail']['body'])) {
|
|
$alarm['mail']['body'] = $this->_fromDriver($alarm['mail']['body']);
|
|
}
|
|
|
|
return $alarm;
|
|
}
|
|
|
|
/**
|
|
* Returns an alarm hash from the backend.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
*
|
|
* @return array An alarm hash.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _get($id, $user);
|
|
|
|
/**
|
|
* Stores an alarm hash in the backend.
|
|
*
|
|
* The alarm will be added if it doesn't exist, and updated otherwise.
|
|
*
|
|
* @param array $alarm An alarm hash. See self::get() for format.
|
|
* @param boolean $keep Whether to keep the snooze value and notification
|
|
* status unchanged. If true, the alarm will get
|
|
* "un-snoozed", and notifications (like mails) are
|
|
* sent again.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function set(array $alarm, $keep = false)
|
|
{
|
|
if (isset($alarm['mail']['body'])) {
|
|
$alarm['mail']['body'] = $this->_toDriver($alarm['mail']['body']);
|
|
}
|
|
|
|
// If this is a recurring alarm and we have a new instanceid,
|
|
// remove the previous entry regardless of the value of $keep.
|
|
// Otherwise, the alarm will never be reset. @since 2.2.0
|
|
if (!empty($alarm['instanceid']) &&
|
|
!$this->exists($alarm['id'], isset($alarm['user']) ? $alarm['user'] : '', !empty($alarm['instanceid']) ? $alarm['instanceid'] : null)) {
|
|
$this->delete($alarm['id'], isset($alarm['user']) ? $alarm['user'] : '');
|
|
}
|
|
|
|
if ($this->exists($alarm['id'], isset($alarm['user']) ? $alarm['user'] : '')) {
|
|
$this->_update($alarm, $keep);
|
|
if (!$keep) {
|
|
foreach ($this->_handlers as &$handler) {
|
|
$handler->reset($alarm);
|
|
}
|
|
}
|
|
} else {
|
|
$this->_add($alarm);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds an alarm hash to the backend.
|
|
*
|
|
* @param array $alarm An alarm hash.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _add(array $alarm);
|
|
|
|
/**
|
|
* Updates an alarm hash in the backend.
|
|
*
|
|
* @param array $alarm An alarm hash.
|
|
* @param boolean $keepsnooze Whether to keep the snooze value unchanged.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _update(array $alarm, $keepsnooze = false);
|
|
|
|
/**
|
|
* Updates internal alarm properties, i.e. properties not determined by
|
|
* the application setting the alarm.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param array $internal A hash with the internal data.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract public function internal($id, $user, array $internal);
|
|
|
|
/**
|
|
* Returns whether an alarm with the given id exists already.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param string $instanceid An optional instanceid to check for.
|
|
* @since 2.2.0
|
|
*
|
|
* @return boolean True if the specified alarm exists.
|
|
*/
|
|
public function exists($id, $user, $instanceid = null)
|
|
{
|
|
try {
|
|
return $this->_exists($id, $user, $instanceid);
|
|
} catch (Horde_Alarm_Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether an alarm with the given id exists already.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param string $instanceid An optional instanceid to match.
|
|
*
|
|
* @return boolean True if the specified alarm exists.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _exists($id, $user, $instanceid = null);
|
|
|
|
/**
|
|
* Delays (snoozes) an alarm for a certain period.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The notified user.
|
|
* @param integer $minutes The delay in minutes. A negative value
|
|
* dismisses the alarm completely.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function snooze($id, $user, $minutes)
|
|
{
|
|
if (empty($user)) {
|
|
throw new Horde_Alarm_Exception('This alarm cannot be snoozed.');
|
|
}
|
|
|
|
$alarm = $this->get($id, $user);
|
|
|
|
if ($alarm) {
|
|
if ($minutes > 0) {
|
|
$alarm['snooze'] = new Horde_Date(time());
|
|
$alarm['snooze']->min += $minutes;
|
|
$this->_snooze($id, $user, $alarm['snooze']);
|
|
return;
|
|
}
|
|
|
|
$this->_dismiss($id, $user);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delays (snoozes) an alarm for a certain period.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param Horde_Date $snooze The snooze time.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _snooze($id, $user, Horde_Date $snooze);
|
|
|
|
/**
|
|
* Returns whether an alarm is snoozed.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param Horde_Date $time The time when the alarm may be snoozed.
|
|
* Defaults to now.
|
|
*
|
|
* @return boolean True if the alarm is snoozed.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function isSnoozed($id, $user, Horde_Date $time = null)
|
|
{
|
|
if (is_null($time)) {
|
|
$time = new Horde_Date(time());
|
|
}
|
|
return (bool)$this->_isSnoozed($id, $user, $time);
|
|
}
|
|
|
|
/**
|
|
* Returns whether an alarm is snoozed.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
* @param Horde_Date $time The time when the alarm may be snoozed.
|
|
*
|
|
* @return boolean True if the alarm is snoozed.
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _isSnoozed($id, $user, Horde_Date $time);
|
|
|
|
/**
|
|
* Dismisses an alarm.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _dismiss($id, $user);
|
|
|
|
/**
|
|
* Deletes an alarm from the backend.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user. All users' alarms if null.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
function delete($id, $user = null)
|
|
{
|
|
$this->_delete($id, $user);
|
|
}
|
|
|
|
/**
|
|
* Deletes an alarm from the backend.
|
|
*
|
|
* @param string $id The alarm's unique id.
|
|
* @param string $user The alarm's user. All users' alarms if null.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _delete($id, $user = null);
|
|
|
|
/**
|
|
* Notifies the user about any active alarms.
|
|
*
|
|
* @param string $user Notify this user, all users if null, or guest
|
|
* users if empty.
|
|
* @param boolean $load Update active alarms from all applications?
|
|
* @param boolean $preload Preload alarms that go off within the next
|
|
* ttl time span?
|
|
* @param array $exclude Don't notify with these methods.
|
|
*
|
|
* @throws Horde_Alarm_Exception if loading of alarms fails, but not if
|
|
* notifying of individual alarms fails.
|
|
*/
|
|
public function notify($user = null, $load = true, $preload = true,
|
|
array $exclude = array())
|
|
{
|
|
try {
|
|
$alarms = $this->listAlarms($user, null, $load, $preload);
|
|
} catch (Horde_Alarm_Exception $e) {
|
|
if ($this->_logger) {
|
|
$this->_logger->log($e, 'ERR');
|
|
}
|
|
throw $e;
|
|
}
|
|
|
|
if (empty($alarms)) {
|
|
return;
|
|
}
|
|
|
|
$handlers = $this->handlers();
|
|
foreach ($alarms as $alarm) {
|
|
foreach ($alarm['methods'] as $key => $alarm_method) {
|
|
if (isset($handlers[$alarm_method]) &&
|
|
!in_array($alarm_method, $exclude)) {
|
|
try {
|
|
$handlers[$alarm_method]->notify($alarm);
|
|
} catch (Horde_Alarm_Exception $e) {
|
|
$this->_errors[$alarm['id'] . "\0" . $key] = $e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a notification handler.
|
|
*
|
|
* @param string $name A handler name.
|
|
* @param Horde_Alarm_Handler $handler A notification handler.
|
|
*/
|
|
public function addHandler($name, Horde_Alarm_Handler $handler)
|
|
{
|
|
$this->_handlers[$name] = $handler;
|
|
$handler->alarm = $this;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* again with parameter names as keys and parameter information as
|
|
* values. The parameter information is hash with the following keys:
|
|
* 'desc' contains a parameter description; 'required' specifies whether
|
|
* this parameter is required.
|
|
*
|
|
* @return array List of methods and parameters.
|
|
*/
|
|
public function handlers()
|
|
{
|
|
if (!$this->_handlersLoaded) {
|
|
foreach (new DirectoryIterator(__DIR__ . '/Alarm/Handler') as $file) {
|
|
if (!$file->isFile() || substr($file->getFilename(), -4) != '.php') {
|
|
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 $this->_handlers;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of errors, exceptions etc. that occured during notify()
|
|
* calls.
|
|
*
|
|
* @since Horde_Alarm 2.1.0
|
|
* @since Horde_Alarm 2.2.9 the keys consist of the alarm id concatenated
|
|
* with a NUL character and an alarm method key.
|
|
*
|
|
* @return array Error list.
|
|
*/
|
|
public function getErrors()
|
|
{
|
|
return $this->_errors;
|
|
}
|
|
|
|
/**
|
|
* Garbage collects old alarms in the backend.
|
|
*
|
|
* @param boolean $force Force garbace collection? If false, GC happens
|
|
* with a 1% chance.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
public function gc($force = false)
|
|
{
|
|
/* A 1% chance we will run garbage collection during a call. */
|
|
if ($force || rand(0, 99) == 0) {
|
|
$this->_gc();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Garbage collects old alarms in the backend.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract protected function _gc();
|
|
|
|
/**
|
|
* Attempts to initialize the backend.
|
|
*
|
|
* @throws Horde_Alarm_Exception
|
|
*/
|
|
abstract public function initialize();
|
|
|
|
/**
|
|
* Converts a value from the driver's charset.
|
|
*
|
|
* @param mixed $value Value to convert.
|
|
*
|
|
* @return mixed Converted value.
|
|
*/
|
|
abstract protected function _fromDriver($value);
|
|
|
|
/**
|
|
* Converts a value to the driver's charset.
|
|
*
|
|
* @param mixed $value Value to convert.
|
|
*
|
|
* @return mixed Converted value.
|
|
*/
|
|
abstract protected function _toDriver($value);
|
|
|
|
}
|