summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_test/tests/inc/subscription.test.php121
-rw-r--r--inc/load.php1
-rw-r--r--inc/subscription.php724
3 files changed, 497 insertions, 349 deletions
diff --git a/_test/tests/inc/subscription.test.php b/_test/tests/inc/subscription.test.php
new file mode 100644
index 000000000..77bf3e830
--- /dev/null
+++ b/_test/tests/inc/subscription.test.php
@@ -0,0 +1,121 @@
+<?php
+
+class subscription_test extends DokuWikiTest {
+
+ function test_regexp(){
+ // data to test against
+ $data = array(
+ "casper every\n",
+ "Andreas digest 1344689733",
+ "Cold%20Fusion every",
+ "zioth list 1344691369\n",
+ "nlights digest",
+ "rikblok\tdigest \t 1344716803",
+ );
+
+ // user, style, data, expected number of results
+ $tests = array(
+ array('Cold Fusion', null, null, 1),
+ array('casper', null, null, 1),
+ array('nope', null, null, 0),
+ array('lights', null, null, 0),
+ array(array('Cold Fusion','casper','nope'), null, null, 2),
+ array(null, 'list', null, 1),
+ array(null, 'every', null, 2),
+ array(null, 'digest', null, 3),
+ array(null, array('list', 'every'), null, 3),
+ array('casper', 'digest', null, 0),
+ array('casper', array('digest','every'), null, 1),
+ array('zioth', 'list', '1344691369', 1),
+ array('zioth', null, '1344691369', 1),
+ array('zioth', 'digest', '1344691369', 0),
+ );
+
+ $sub = new MockupSubscription();
+
+ $row = 0;
+ foreach($tests as $test){
+ $re = $sub->buildregex($test[0], $test[1], $test[2]);
+ $this->assertFalse(empty($re), "test line $row");
+ $result = preg_grep($re, $data);
+ $this->assertEquals($test[3], count($result), "test line $row. $re got\n".print_r($result, true));
+
+ $row++;
+ }
+ }
+
+ function test_addremove(){
+ $sub = new MockupSubscription();
+
+ // no subscriptions
+ $this->assertArrayNotHasKey(
+ 'wiki:dokuwiki',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // add page subscription
+ $sub->add('wiki:dokuwiki', 'testuser', 'every');
+
+ // one subscription
+ $this->assertArrayHasKey(
+ 'wiki:dokuwiki',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // remove page subscription
+ $sub->remove('wiki:dokuwiki', 'testuser');
+
+ // no subscription
+ $this->assertArrayNotHasKey(
+ 'wiki:dokuwiki',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // add namespace subscription
+ $sub->add('wiki:', 'testuser', 'every');
+
+ // one subscription
+ $this->assertArrayHasKey(
+ 'wiki:',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // remove (non existing) page subscription
+ $sub->remove('wiki:dokuwiki', 'testuser');
+
+ // still one subscription
+ $this->assertArrayHasKey(
+ 'wiki:',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // change namespace subscription
+ $sub->add('wiki:', 'testuser', 'digest', '1234567');
+
+ // still one subscription
+ $this->assertArrayHasKey(
+ 'wiki:',
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+
+ // check contents
+ $this->assertEquals(
+ array('wiki:' => array('testuser' => array('digest', '1234567'))),
+ $sub->subscribers('wiki:dokuwiki', null, array('every','list','digest'))
+ );
+ }
+
+
+
+}
+
+/**
+ * makes protected methods visible for testing
+ */
+class MockupSubscription extends Subscription {
+ public function buildregex($user = null, $style = null, $data = null) {
+ return parent::buildregex($user, $style, $data);
+ }
+}
+
+//Setup VIM: ex: et ts=4 :
diff --git a/inc/load.php b/inc/load.php
index b676518e7..bd6d39814 100644
--- a/inc/load.php
+++ b/inc/load.php
@@ -81,6 +81,7 @@ function load_autoload($name){
'Mailer' => DOKU_INC.'inc/Mailer.class.php',
'RemoteAPI' => DOKU_INC.'inc/remote.php',
'RemoteAPICore' => DOKU_INC.'inc/RemoteAPICore.php',
+ 'Subscription' => DOKU_INC.'inc/subscription.php',
'DokuWiki_Action_Plugin' => DOKU_PLUGIN.'action.php',
'DokuWiki_Admin_Plugin' => DOKU_PLUGIN.'admin.php',
diff --git a/inc/subscription.php b/inc/subscription.php
index 1f1aedfa4..856836cd5 100644
--- a/inc/subscription.php
+++ b/inc/subscription.php
@@ -12,399 +12,425 @@
* - subscription_lock
* - subscription_unlock
*
+ * @fixme handle $conf['subscribers'] and disable actions
+ *
* @author Adrian Lang <lang@cosmocode.de>
+ * @author Andreas Gohr <andi@splitbrain.org>
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
-/**
- * Get the name of the metafile tracking subscriptions to target page or
- * namespace
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return string
- */
-function subscription_filename($id) {
- $meta_fname = '.mlist';
- if ((substr($id, -1, 1) === ':')) {
- $meta_froot = getNS($id);
- $meta_fname = '/' . $meta_fname;
- } else {
- $meta_froot = $id;
+class Subscription {
+
+ /**
+ * Return the subscription meta file for the given ID
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return string
+ */
+ protected function file($id) {
+ $meta_fname = '.mlist';
+ if((substr($id, -1, 1) === ':')) {
+ $meta_froot = getNS($id);
+ $meta_fname = '/'.$meta_fname;
+ } else {
+ $meta_froot = $id;
+ }
+ return metaFN((string) $meta_froot, $meta_fname);
}
- return metaFN((string) $meta_froot, $meta_fname);
-}
-/**
- * Lock subscription info for an ID
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return string
- */
-function subscription_lock_filename ($id){
- global $conf;
- return $conf['lockdir'].'/_subscr_' . md5($id) . '.lock';
-}
+ /**
+ * Lock subscription info
+ *
+ * We don't use io_lock() her because we do not wait for the lock and use a larger stale time
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return bool true, if you got a succesful lock
+ */
+ protected function lock($id) {
+ global $conf;
-/**
- * Creates a lock file for writing subscription data
- *
- * @todo add lock time parameter to io_lock() and use this instead
- * @param $id
- * @return bool
- */
-function subscription_lock($id) {
- global $conf;
- $lock = subscription_lock_filename($id);
+ $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
- if (is_dir($lock) && time()-@filemtime($lock) > 60*5) {
- // looks like a stale lock - remove it
- @rmdir($lock);
+ if(is_dir($lock) && time() - @filemtime($lock) > 60 * 5) {
+ // looks like a stale lock - remove it
+ @rmdir($lock);
+ }
+
+ // try creating the lock directory
+ if(!@mkdir($lock, $conf['dmode'])) {
+ return false;
+ }
+
+ if($conf['dperm']) chmod($lock, $conf['dperm']);
+ return true;
}
- // try creating the lock directory
- if (!@mkdir($lock,$conf['dmode'])) {
- return false;
+ /**
+ * Unlock subscription info
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @return bool
+ */
+ protected function unlock($id) {
+ global $conf;
+ $lock = $conf['lockdir'].'/_subscr_'.md5($id).'.lock';
+ return @rmdir($lock);
}
- if($conf['dperm']) chmod($lock, $conf['dperm']);
- return true;
-}
+ /**
+ * Construct a regular expression for parsing a subscription definition line
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ *
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return string complete regexp including delimiters
+ * @throws Exception when no data is passed
+ */
+ protected function buildregex($user = null, $style = null, $data = null) {
+ // always work with arrays
+ $user = (array) $user;
+ $style = (array) $style;
+ $data = (array) $data;
-/**
- * Unlock subscription info for an ID
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @param string $id The target page or namespace, specified by id; Namespaces
- * are identified by appending a colon.
- * @return bool
- */
-function subscription_unlock($id) {
- $lockf = subscription_lock_filename($id);
- return @rmdir($lockf);
-}
+ // clean
+ $user = array_filter(array_map('trim', $user));
+ $style = array_filter(array_map('trim', $style));
+ $data = array_filter(array_map('trim', $data));
-/**
- * Set subscription information
- *
- * Allows to set subscription information 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.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $user The subscriber or unsubscriber
- * @param string $page The target object (page or namespace), specified by
- * id; Namespaces are identified by a trailing colon.
- * @param string $style The subscribe style; DokuWiki currently implements
- * “every”, “digest”, and “list”.
- * @param string $data An optional data blob
- * @param bool $overwrite Whether an existing subscription may be overwritten
- * @return bool
- */
-function subscription_set($user, $page, $style, $data = null,
- $overwrite = false) {
- 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;
- }
+ // user names are encoded
+ $user = array_map('auth_nameencode', $user);
- // io_deleteFromFile does not return false if no line matched.
- return io_deleteFromFile($file,
- subscription_regex(array('user' => auth_nameencode($user))),
- true);
- }
+ // quote
+ $user = array_map('preg_quote_cb', $user);
+ $style = array_map('preg_quote_cb', $style);
+ $data = array_map('preg_quote_cb', $data);
- // 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;
+ // join
+ $user = join('|', $user);
+ $style = join('|', $style);
+ $data = join('|', $data);
+
+ // any data at all?
+ if($user.$style.$data === '') throw new Exception('no data passed');
+
+ // replace empty values, set which ones are optional
+ $sopt = '';
+ $dopt = '';
+ if($user === '') {
+ $user = '\S+';
}
- // Fail if deletion failed, else continue.
- if (!subscription_set($user, $page, null)) {
- return false;
+ if($style === '') {
+ $style = '\S+';
+ $sopt = '?';
+ }
+ if($data === '') {
+ $data = '\S+';
+ $dopt = '?';
}
- }
- $file = subscription_filename($page);
- $content = auth_nameencode($user) . ' ' . $style;
- if (!is_null($data)) {
- $content .= ' ' . $data;
+ // assemble
+ return "/^($user)(?:\\s+($style))$sopt(?:\\s+($data))$dopt$/";
}
- return io_saveFile($file, $content . "\n", true);
-}
-/**
- * Recursively search for matching subscriptions
- *
- * This function searches all relevant subscription files for a page or
- * namespace.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- * @see function subscription_regex for $pre documentation
- *
- * @param string $page The target object’s (namespace or page) id
- * @param array $pre A hash of predefined values
- * @return array
- */
-function subscription_find($page, $pre) {
- // Construct list of files which may contain relevant subscriptions.
- $filenames = array(':' => subscription_filename(':'));
- 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";
- }
+ /**
+ * Recursively search for matching subscriptions
+ *
+ * This function searches all relevant subscription files for a page or
+ * namespace.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $page The target object’s (namespace or page) id
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return array
+ */
+ public function subscribers($page, $user = null, $style = null, $data = null) {
+ // Construct list of files which may contain relevant subscriptions.
+ $files = array(':' => $this->file(':'));
+ do {
+ $files[$page] = $this->file($page);
+ $page = getNS(rtrim($page, ':')).':';
+ } while($page !== ':');
- list($user, $rest) = explode(' ', $subscription, 2);
- $subscription = rawurldecode($user) . " " . $rest;
+ $re = $this->buildregex($user, $style, $data);
- 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();
+ // Handle files.
+ $result = array();
+ foreach($files as $target => $file) {
+ if(!@file_exists($file)) continue;
+
+ $lines = file($file);
+ foreach($lines as $line) {
+ // fix old style subscription files
+ if(strpos($line, ' ') === false) $line = trim($line)." every\n";
+
+ // check for matching entries
+ if(!preg_match($re, $line, $m)) continue;
+
+ $u = rawurldecode($m[1]); // decode the user name
+ if(!isset($result[$target])) $result[$target] = array();
+ $result[$target][$u] = array($m[2], $m[3]); // add to result
}
- $matches[$cur_page][] = $match;
}
+ return array_reverse($result);
}
- 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;
- }
+ /**
+ * Adds a new subscription for the given page or namespace
+ *
+ * This will automatically overwrite any existent subscription for the given user on this
+ * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
+ *
+ * @param string $id The target page or namespace, specified by id; Namespaces
+ * are identified by appending a colon.
+ * @param string $user
+ * @param string $style
+ * @param string $data
+ * @throws Exception when user or style is empty
+ * @return bool
+ */
+ public function add($id, $user, $style, $data = '') {
+ // delete any existing subscription
+ $this->remove($id, $user);
- $subs = subscription_find($ID, array('user' => $_SERVER['REMOTE_USER']));
- if (count($subs) === 0) {
- return false;
- }
+ $user = auth_nameencode(trim($user));
+ $style = trim($style);
+ $data = trim($data);
- $_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;
+ if(!$user) throw new Exception('no subscription user given');
+ if(!$style) throw new Exception('no subscription style given');
+
+ $line = "$user $style";
+ if($data) $line .= " $data";
+ $line .= "\n";
+
+ $file = $this->file($id);
+
+ return io_saveFile($file, $line, true);
}
- return $_ret;
-}
+ /**
+ * Removes a subscription for the given page or namespace
+ *
+ * This removes all subscriptions matching the given criteria on the given page or
+ * namespace. It will *not* modify any subscriptions that may exist in higher
+ * namespaces.
+ *
+ * @param string $id The target object’s (namespace or page) id
+ * @param string|array $user
+ * @param string|array $style
+ * @param string|array $data
+ * @return bool
+ */
+ public function remove($id, $user = null, $style = null, $data = null) {
+ $file = $this->file($id);
+ if(!file_exists($file)) return true;
-/**
- * Construct a regular expression parsing a subscription definition line
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @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.
- *
- * @return string complete regexp including delimiters
- */
-function subscription_regex($pre = array()) {
- if (!isset($pre['escaped']) || $pre['escaped'] === false) {
- $pre = array_map('preg_quote_cb', $pre);
+ $re = $this->buildregex($user, $style, $data);
+ return io_deleteFromFile($file, $re, true);
}
- foreach (array('user', 'style', 'data') as $key) {
- if (!isset($pre[$key])) {
- $pre[$key] = '(\S+)';
+
+ /**
+ * 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”.
+ *
+ * @param string $id Page ID, defaults to global $ID
+ * @param string $user User, defaults to $_SERVER['REMOTE_USER']
+ * @return array
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+ function user_subscription($id='', $user='') {
+ global $ID;
+ global $conf;
+ if(!$conf['subscribers']) return false;
+
+ if(!$id) $id = $ID;
+ if(!$user) $user = $_SERVER['REMOTE_USER'];
+
+
+ $subs = $this->subscribers($id, $user);
+ if(!count($subs)) return false;
+
+ $result = array();
+ foreach($subs as $target => $data) {
+ $result[] = array(
+ 'target' => $target,
+ 'style' => $data[$user][0],
+ 'data' => $data[$user][1]
+ );
}
+
+ return $result;
}
- return '/^' . $pre['user'] . '(?: ' . $pre['style'] .
- '(?: ' . $pre['data'] . ')?)?$/';
-}
-/**
- * Return a string with the email addresses of all the
- * users subscribed to a page
- *
- * This is the default action for COMMON_NOTIFY_ADDRESSLIST.
- *
- * @author Steven Danz <steven-danz@kc.rr.com>
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @todo this does NOT return a string but uses a reference to write back, either fix function or docs
- * @param array $data Containing $id (the page id), $self (whether the author
- * should be notified, $addresslist (current email address
- * list)
- * @return string
- */
-function subscription_addresslist(&$data){
- global $conf;
- /** @var auth_basic $auth */
- global $auth;
+ /**
+ * Return a string with the email addresses of all the
+ * users subscribed to a page
+ *
+ * This is the default action for COMMON_NOTIFY_ADDRESSLIST.
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @todo this does NOT return a string but uses a reference to write back, either fix function or docs
+ * @param array $data Containing $id (the page id), $self (whether the author
+ * should be notified, $addresslist (current email address
+ * list)
+ * @return string
+ */
+ function subscription_addresslist(&$data) {
+ global $conf;
+ /** @var auth_basic $auth */
+ global $auth;
- $id = $data['id'];
- $self = $data['self'];
- $addresslist = $data['addresslist'];
+ $id = $data['id'];
+ $self = $data['self'];
+ $addresslist = $data['addresslist'];
- if (!$conf['subscribers'] || $auth === null) {
- 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'];
+ if(!$conf['subscribers'] || $auth === null) {
+ 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'];
+ }
}
}
}
+ $data['addresslist'] = trim($addresslist.','.implode(',', $emails), ',');
}
- $data['addresslist'] = trim($addresslist . ',' . implode(',', $emails), ',');
-}
-/**
- * Send a digest mail
- *
- * Sends a digest mail showing a bunch of changes.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $subscriber_mail The target mail address
- * @param array $id The ID
- * @param int $lastupdate Time of the last notification
- */
-function subscription_send_digest($subscriber_mail, $id, $lastupdate) {
- $n = 0;
- do {
- $rev = getRevisions($id, $n++, 1);
- $rev = (count($rev) > 0) ? $rev[0] : null;
- } while (!is_null($rev) && $rev > $lastupdate);
-
- $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, '&');
- $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);
+ /**
+ * Send a digest mail
+ *
+ * Sends a digest mail showing a bunch of changes.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $id The ID
+ * @param int $lastupdate Time of the last notification
+ */
+ function subscription_send_digest($subscriber_mail, $id, $lastupdate) {
+ $n = 0;
+ do {
+ $rev = getRevisions($id, $n++, 1);
+ $rev = (count($rev) > 0) ? $rev[0] : null;
+ } while(!is_null($rev) && $rev > $lastupdate);
+
+ $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, '&');
+ $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'
+ );
}
- subscription_send($subscriber_mail, $replaces, $subject, $id,
- 'subscr_digest');
-}
-/**
- * Send a list mail
- *
- * Sends a list mail showing a list of changed pages.
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @param string $subscriber_mail The target mail address
- * @param array $ids Array of ids
- * @param string $ns_id The id of the namespace
- */
-function subscription_send_list($subscriber_mail, $ids, $ns_id) {
- if (count($ids) === 0) return;
- global $conf;
- $list = '';
- foreach ($ids as $id) {
- $list .= '* ' . wl($id, array(), true) . NL;
+ /**
+ * Send a list mail
+ *
+ * Sends a list mail showing a list of changed pages.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @param string $subscriber_mail The target mail address
+ * @param array $ids Array of ids
+ * @param string $ns_id The id of the namespace
+ */
+ function subscription_send_list($subscriber_mail, $ids, $ns_id) {
+ if(count($ids) === 0) return;
+ global $conf;
+ $list = '';
+ foreach($ids as $id) {
+ $list .= '* '.wl($id, array(), true).NL;
+ }
+ subscription_send(
+ $subscriber_mail,
+ array(
+ 'DIFF' => rtrim($list),
+ 'SUBSCRIBE' => wl(
+ $ns_id.$conf['start'],
+ array('do' => 'subscribe'),
+ true, '&'
+ )
+ ),
+ 'subscribe_list',
+ prettyprint_id($ns_id),
+ 'subscr_list'
+ );
}
- subscription_send($subscriber_mail,
- array('DIFF' => rtrim($list),
- 'SUBSCRIBE' => wl($ns_id . $conf['start'],
- array('do' => 'subscribe'),
- true, '&')),
- 'subscribe_list',
- prettyprint_id($ns_id),
- 'subscr_list');
-}
-/**
- * Helper function for sending a mail
- *
- * @author Adrian Lang <lang@cosmocode.de>
- *
- * @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
- * @return bool
- */
-function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) {
- global $lang;
-
- $text = rawLocale($template);
- $trep = array_merge($replaces, array('PAGE' => $id));
-
- $subject = $lang['mail_' . $subject] . ' ' . $id;
- $mail = new Mailer();
- $mail->bcc($subscriber_mail);
- $mail->subject($subject);
- $mail->setBody($text,$trep);
- $mail->setHeader(
- 'List-Unsubscribe',
- '<'.wl($id,array('do'=>'subscribe'),true,'&').'>',
- false
- );
- return $mail->send();
-}
+ /**
+ * Helper function for sending a mail
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ *
+ * @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
+ * @return bool
+ */
+ function subscription_send($subscriber_mail, $replaces, $subject, $id, $template) {
+ global $lang;
+
+ $text = rawLocale($template);
+ $trep = array_merge($replaces, array('PAGE' => $id));
+
+ $subject = $lang['mail_'.$subject].' '.$id;
+ $mail = new Mailer();
+ $mail->bcc($subscriber_mail);
+ $mail->subject($subject);
+ $mail->setBody($text, $trep);
+ $mail->setHeader(
+ 'List-Unsubscribe',
+ '<'.wl($id, array('do'=> 'subscribe'), true, '&').'>',
+ false
+ );
+ return $mail->send();
+ }
+
+} \ No newline at end of file