summaryrefslogtreecommitdiff
path: root/inc/subscription.php
diff options
context:
space:
mode:
authorAdrian Lang <lang@cosmocode.de>2010-01-05 14:14:00 +0100
committerAdrian Lang <lang@cosmocode.de>2010-01-20 10:53:18 +0100
commit5b75cd1f5c479ada468fbf62a733c54edad152f1 (patch)
tree7ca6012d892aaef60cee7bc86b2f62ade43e03ce /inc/subscription.php
parentb5ee21aa65a2f380e3b99ff5ea6ced48c1cb720e (diff)
downloadrpg-5b75cd1f5c479ada468fbf62a733c54edad152f1.tar.gz
rpg-5b75cd1f5c479ada468fbf62a733c54edad152f1.tar.bz2
New mail subscription with digest
Diffstat (limited to 'inc/subscription.php')
-rw-r--r--inc/subscription.php342
1 files changed, 342 insertions, 0 deletions
diff --git a/inc/subscription.php b/inc/subscription.php
new file mode 100644
index 000000000..1dcecf6f5
--- /dev/null
+++ b/inc/subscription.php
@@ -0,0 +1,342 @@
+<?php
+/**
+ * Utilities for handling (email) subscriptions
+ *
+ * The public interface of this file consists of the functions
+ * - subscription_find
+ * - subscription_send_digest
+ * - subscription_send_list
+ * - subscription_set
+ * - get_info_subscribed
+ * - subscription_addresslist
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
+ */
+
+require_once DOKU_INC.'/inc/pageutils.php';
+
+/**
+ * Get the name of the metafile tracking subscriptions to target page or
+ * namespace
+ *
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_filename($id) {
+ $meta_fname = '.mlist';
+ if ((substr($id, -1, 1) === ':')) {
+ $meta_froot = getNS($id);
+ if ($meta_froot === false) {
+ $meta_fname = '/' . $meta_fname;
+ }
+ } else {
+ $meta_froot = $id;
+ }
+ return metaFN($meta_froot, $meta_fname);
+}
+
+/**
+ * Set subscription information
+ *
+ * Allows to set subscription informations for permanent storage in meta files.
+ * Subscriptions consist of a target object, a subscribing user, a subscribe
+ * style and optional data.
+ * A subscription may be deleted by specifying an empty subscribe style.
+ * Only one subscription per target and user is allowed.
+ * The function returns false on error, otherwise true. Note that no error is
+ * returned if a subscription should be deleted but the user is not subscribed
+ * and the subscription meta file exists.
+ *
+ * @param string $page The target object (page or namespace), specified by
+ * id; Namespaces are identified by a trailing colon.
+ * @param string $user The user
+ * @param string $style The subscribe style; DokuWiki currently implements
+ * “every”, “digest”, and “list”.
+ * @param bool $overwrite Whether an existing subscription may be overwritten
+ * @param string $data An optional data blob; For list and digest, this
+ * defaults to time().
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_set($page, $user, $style, $overwrite = false, $data = null) {
+ global $lang;
+ if (is_null($style)) {
+ // Delete subscription.
+ $file = subscription_filename($page);
+ if (!@file_exists($file)) {
+ msg(sprintf($lang['subscr_not_subscribed'], $user,
+ prettyprint_id($page)), -1);
+ return false;
+ }
+
+ // io_deleteFromFile does not return false if no line matched.
+ return io_deleteFromFile($file,
+ subscription_regex(array('user' => $user)),
+ true);
+ }
+
+ // Delete subscription if one exists and $overwrite is true. If $overwrite
+ // is false, fail.
+ $subs = subscription_find($page, array('user' => $user));
+ if (count($subs) > 0 && array_pop(array_keys($subs)) === $page) {
+ if (!$overwrite) {
+ msg(sprintf($lang['subscr_already_subscribed'], $user,
+ prettyprint_id($page)), -1);
+ return false;
+ }
+ // Fail if deletion failed, else continue.
+ if (!subscription_set($page, $user, null)) {
+ return false;
+ }
+ }
+
+ $file = subscription_filename($page);
+ $content = auth_nameencode($user) . ' ' . $style;
+ if (in_array($style, array('list', 'digest'))) {
+ $content .= ' ' . (!is_null($data) ? $data : time());
+ }
+ return io_saveFile($file, $content . "\n", true);
+}
+
+/**
+ * Recursively search for matching subscriptions
+ *
+ * This function searches all relevant subscription files for a page or
+ * namespace.
+ *
+ * @param string $page The target object’s (namespace or page) id
+ * @param array $pre A hash of predefined values
+ *
+ * @see function subscription_regex for $pre documentation
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_find($page, $pre) {
+ // Construct list of files which may contain relevant subscriptions.
+ $filenames = array();
+ do {
+ $filenames[$page] = subscription_filename($page);
+ $page = getNS(rtrim($page, ':')) . ':';
+ } while ($page !== ':');
+
+ // Handle files.
+ $matches = array();
+ foreach ($filenames as $cur_page => $filename) {
+ if (!@file_exists($filename)) {
+ continue;
+ }
+ $subscriptions = file($filename);
+ foreach ($subscriptions as $subscription) {
+ if (strpos($subscription, ' ') === false) {
+ // This is an old subscription file.
+ $subscription = trim($subscription) . " every\n";
+ }
+ if (preg_match(subscription_regex($pre), $subscription,
+ &$line_matches) === 0) {
+ continue;
+ }
+ $match = array_slice($line_matches, 1);
+ if (!isset($matches[$cur_page])) {
+ $matches[$cur_page] = array();
+ }
+ $matches[$cur_page][] = $match;
+ }
+ }
+ return array_reverse($matches);
+}
+
+/**
+ * Get data for $INFO['subscribed']
+ *
+ * $INFO['subscribed'] is either false if no subscription for the current page
+ * and user is in effect. Else it contains an array of arrays with the fields
+ * “target”, “style”, and optionally “data”.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function get_info_subscribed() {
+ global $ID;
+ global $conf;
+ if (!$conf['subscribers']) {
+ return false;
+ }
+
+ $subs = subscription_find($ID, array('user' => $_SERVER['REMOTE_USER']));
+ if (count($subs) === 0) {
+ return false;
+ }
+
+ $_ret = array();
+ foreach ($subs as $target => $subs_data) {
+ $new = array('target' => $target,
+ 'style' => $subs_data[0][0]);
+ if (count($subs_data[0]) > 1) {
+ $new['data'] = $subs_data[0][1];
+ }
+ $_ret[] = $new;
+ }
+
+ return $_ret;
+}
+
+/**
+ * Construct a regular expression parsing a subscription definition line
+ *
+ * @param array $pre A hash of predefined values; “user”, “style”, and
+ * “data” may be set to limit the results to
+ * subscriptions matching these parameters. If
+ * “escaped” is true, these fields are inserted into the
+ * regular expression without escaping.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_regex($pre = array()) {
+ if (!isset($pre['escaped']) || $pre['escaped'] === false) {
+ $pre = array_map('preg_quote_cb', $pre);
+ }
+ foreach (array('user', 'style', 'data') as $key) {
+ if (!isset($pre[$key])) {
+ $pre[$key] = '(\S+)';
+ }
+ }
+ return '/^' . $pre['user'] . '(?: ' . $pre['style'] .
+ '(?: ' . $pre['data'] . ')?)?$/';
+}
+
+/**
+ * Return a string with the email addresses of all the
+ * users subscribed to a page
+ *
+ * @param string $id The id of the changed page
+ * @param bool $self Whether a notice should be sent to the editor if he is
+ * subscribed
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ */
+function subscription_addresslist($id, $self=true){
+ global $conf;
+ global $auth;
+
+ if (!$conf['subscribers']) {
+ return '';
+ }
+ $pres = array('style' => 'every', 'escaped' => true);
+ if (!$self && isset($_SERVER['REMOTE_USER'])) {
+ $pres['user'] = '((?:(?!' . preg_quote_cb($_SERVER['REMOTE_USER']) .
+ ')\S?)+)';
+ }
+ $subs = subscription_find($id, $pres);
+ $emails = array();
+ foreach ($subs as $by_targets) {
+ foreach ($by_targets as $sub) {
+ $info = $auth->getUserData($sub[0]);
+ if ($info === false) continue;
+ $level = auth_aclcheck($id, $sub[0], $info['grps']);
+ if ($level >= AUTH_READ) {
+ if (strcasecmp($info['mail'], $conf['notify']) != 0) {
+ $emails[$sub[0]] = $info['mail'];
+ }
+ }
+ }
+ }
+ return implode(',', $emails);
+}
+
+/**
+ * Send a digest mail
+ *
+ * Sends a digest mail showing a bunch of changes.
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $change The newest change
+ * @param int $lastupdate Time of the last notification
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_send_digest($subscriber_mail, $change, $lastupdate) {
+ $id = $change['id'];
+ $n = 0;
+ do {
+ $rev = getRevisions($id, $n++, 1);
+ $rev = (count($rev) > 0) ? $rev[0] : null;
+ } while (!is_null($rev) && $rev > $lastupdate);
+
+ $ip = $change['ip'];
+ $replaces = array('NEWPAGE' => wl($id, '', true, '&'),
+ 'SUBSCRIBE' => wl($id, array('do' => 'subscribe'), true, '&'));
+ if (!is_null($rev)) {
+ $subject = 'changed';
+ $replaces['OLDPAGE'] = wl($id, "rev=$rev", true, '&');
+ require_once DOKU_INC.'inc/DifferenceEngine.php';
+ $df = new Diff(explode("\n", rawWiki($id, $rev)),
+ explode("\n", rawWiki($id)));
+ $dformat = new UnifiedDiffFormatter();
+ $replaces['DIFF'] = $dformat->format($df);
+ } else {
+ $subject = 'newpage';
+ $replaces['OLDPAGE'] = 'none';
+ $replaces['DIFF'] = rawWiki($id);
+ }
+ subscription_send($subscriber_mail, $replaces, $subject, $id,
+ 'subscr_digest');
+}
+
+/**
+ * Send a list mail
+ *
+ * Sends a list mail showing a list of changed pages.
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $changes Array of changes
+ * @param string $id The id of the namespace
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_send_list($subscriber_mail, $changes, $id) {
+ $list = '';
+ foreach ($changes as $change) {
+ $list .= '* ' . $change['id'] . NL;
+ }
+ subscription_send($subscriber_mail,
+ array('DIFF' => rtrim($list),
+ 'SUBSCRIBE' => wl($changes[0]['id'],
+ array('do' => 'subscribe'),
+ true, '&')),
+ 'subscribe_list',
+ prettyprint_id($id),
+ 'subscr_list');
+}
+
+/**
+ * Helper function for sending a mail
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $replaces Predefined parameters used to parse the
+ * template
+ * @param string $subject The lang id of the mail subject (without the
+ * prefix “mail_”)
+ * @param string $id The page or namespace id
+ * @param string $template The name of the mail template
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) {
+ global $conf;
+
+ $text = rawLocale($template);
+ $replaces = array_merge($replaces, array('TITLE' => $conf['title'],
+ 'DOKUWIKIURL' => DOKU_URL,
+ 'PAGE' => $id));
+
+ foreach ($replaces as $key => $substitution) {
+ $text = str_replace('@'.strtoupper($key).'@', $substitution, $text);
+ }
+
+ global $lang;
+ $subject = $lang['mail_' . $subject] . ' ' . $id;
+ mail_send('', '['.$conf['title'].'] '. $subject, $text,
+ $conf['mailfrom'], '', $subscriber_mail);
+}