summaryrefslogtreecommitdiff
path: root/inc
diff options
context:
space:
mode:
authorAdrian Lang <lang@cosmocode.de>2010-01-20 10:56:01 +0100
committerAdrian Lang <lang@cosmocode.de>2010-01-20 10:56:01 +0100
commit7d723f3d24511105ce37006f9439632c3d8f915e (patch)
tree605809aace3e368d953466f76425b41d56fa1b38 /inc
parent1c64eaeaa3295062b0a4e9b20b772274cd86ca42 (diff)
parenteee44352b2c73d56818600770b87f891b88a545f (diff)
downloadrpg-7d723f3d24511105ce37006f9439632c3d8f915e.tar.gz
rpg-7d723f3d24511105ce37006f9439632c3d8f915e.tar.bz2
Merge branch 'subscription'
Diffstat (limited to 'inc')
-rw-r--r--inc/actions.php138
-rw-r--r--inc/common.php132
-rw-r--r--inc/confutils.php1
-rw-r--r--inc/form.php21
-rw-r--r--inc/lang/de/lang.php24
-rw-r--r--inc/lang/en/lang.php29
-rw-r--r--inc/lang/en/subscr_digest.txt20
-rw-r--r--inc/lang/en/subscr_form.txt3
-rw-r--r--inc/lang/en/subscr_list.txt17
-rw-r--r--inc/lang/en/subscr_single.txt (renamed from inc/lang/en/subscribermail.txt)0
-rw-r--r--inc/pageutils.php17
-rw-r--r--inc/subscription.php349
-rw-r--r--inc/template.php140
13 files changed, 655 insertions, 236 deletions
diff --git a/inc/actions.php b/inc/actions.php
index 92f817133..1fda0584e 100644
--- a/inc/actions.php
+++ b/inc/actions.php
@@ -47,12 +47,13 @@ function act_dispatch(){
}
//check if user is asking to (un)subscribe a page
- if($ACT == 'subscribe' || $ACT == 'unsubscribe')
- $ACT = act_subscription($ACT);
-
- //check if user is asking to (un)subscribe a namespace
- if($ACT == 'subscribens' || $ACT == 'unsubscribens')
- $ACT = act_subscriptionns($ACT);
+ if($ACT == 'subscribe') {
+ try {
+ $ACT = act_subscription($ACT);
+ } catch (Exception $e) {
+ msg($e->getMessage(), -1);
+ }
+ }
//check permissions
$ACT = act_permcheck($ACT);
@@ -550,81 +551,102 @@ function act_export($act){
}
/**
- * Handle page 'subscribe', 'unsubscribe'
+ * Handle page 'subscribe'
*
- * @author Steven Danz <steven-danz@kc.rr.com>
- * @todo localize
+ * Throws exception on error.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
*/
function act_subscription($act){
- global $ID;
- global $INFO;
global $lang;
+ global $INFO;
+ global $ID;
- $file=metaFN($ID,'.mlist');
- if ($act=='subscribe' && !$INFO['subscribed']){
- if ($INFO['userinfo']['mail']){
- if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) {
- $INFO['subscribed'] = true;
- msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1);
- } else {
- msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1);
- }
- } else {
- msg($lang['subscribe_noaddress']);
- }
- } elseif ($act=='unsubscribe' && $INFO['subscribed']){
- if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) {
- $INFO['subscribed'] = false;
- msg(sprintf($lang[$act.'_success'], $INFO['userinfo']['name'], $ID),1);
- } else {
- msg(sprintf($lang[$act.'_error'], $INFO['userinfo']['name'], $ID),1);
+ // get and preprocess data.
+ $params = array();
+ foreach(array('target', 'style', 'action') as $param) {
+ if (isset($_REQUEST["sub_$param"])) {
+ $params[$param] = $_REQUEST["sub_$param"];
}
}
+ // any action given? if not just return and show the subscription page
+ if(!$params['action']) return $act;
+
+ // Handle POST data, may throw exception.
+ trigger_event('ACTION_HANDLE_SUBSCRIBE', $params, 'subscription_handle_post');
+
+ $target = $params['target'];
+ $style = $params['style'];
+ $data = $params['data'];
+ $action = $params['action'];
+
+ // Perform action.
+ require_once DOKU_INC . 'inc/subscription.php';
+ if (!subscription_set($_SERVER['REMOTE_USER'], $target, $style, $data)) {
+ throw new Exception(sprintf($lang["subscr_{$action}_error"],
+ hsc($INFO['userinfo']['name']),
+ prettyprint_id($target)));
+ }
+ msg(sprintf($lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
+ prettyprint_id($target)), 1);
+ act_redirect($ID, $act);
+
+ // Assure that we have valid data if act_redirect somehow fails.
+ $INFO['subscribed'] = get_info_subscribed();
return 'show';
}
/**
- * Handle namespace 'subscribe', 'unsubscribe'
+ * Validate POST data
+ *
+ * Validates POST data for a subscribe or unsubscribe request. This is the
+ * default action for the event ACTION_HANDLE_SUBSCRIBE.
*
+ * @author Adrian Lang <lang@cosmocode.de>
*/
-function act_subscriptionns($act){
- global $ID;
+function subscription_handle_post(&$params) {
global $INFO;
global $lang;
- if(!getNS($ID)) {
- $file = metaFN(getNS($ID),'.mlist');
- $ns = "root";
- } else {
- $file = metaFN(getNS($ID),'/.mlist');
- $ns = getNS($ID);
+ // Get and validate parameters.
+ if (!isset($params['target'])) {
+ throw new Exception('no subscription target given');
}
-
- // reuse strings used to display the status of the subscribe action
- $act_msg = rtrim($act, 'ns');
-
- if ($act=='subscribens' && !$INFO['subscribedns']){
- if ($INFO['userinfo']['mail']){
- if (io_saveFile($file,$_SERVER['REMOTE_USER']."\n",true)) {
- $INFO['subscribedns'] = true;
- msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1);
- } else {
- msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1);
+ $target = $params['target'];
+ $valid_styles = array('every', 'digest');
+ if (substr($target, -1, 1) === ':') {
+ // Allow “list” subscribe style since the target is a namespace.
+ $valid_styles[] = 'list';
+ }
+ $style = valid_input_set('style', $valid_styles, $params,
+ 'invalid subscription style given');
+ $action = valid_input_set('action', array('subscribe', 'unsubscribe'),
+ $params, 'invalid subscription action given');
+
+ // Check other conditions.
+ if ($action === 'subscribe') {
+ if ($INFO['userinfo']['mail'] === '') {
+ throw new Exception($lang['subscr_subscribe_noaddress']);
+ }
+ } elseif ($action === 'unsubscribe') {
+ $is = false;
+ foreach($INFO['subscribed'] as $subscr) {
+ if ($subscr['target'] === $target) {
+ $is = true;
}
- } else {
- msg($lang['subscribe_noaddress']);
}
- } elseif ($act=='unsubscribens' && $INFO['subscribedns']){
- if (io_deleteFromFile($file,$_SERVER['REMOTE_USER']."\n")) {
- $INFO['subscribedns'] = false;
- msg(sprintf($lang[$act_msg.'_success'], $INFO['userinfo']['name'], $ns),1);
- } else {
- msg(sprintf($lang[$act_msg.'_error'], $INFO['userinfo']['name'], $ns),1);
+ if ($is === false) {
+ throw new Exception(sprintf($lang['subscr_not_subscribed'],
+ $_SERVER['REMOTE_USER'],
+ prettyprint_id($target)));
}
+ // subscription_set deletes a subscription if style = null.
+ $style = null;
}
- return 'show';
+ $data = in_array($style, array('list', 'digest')) ? time() : null;
+ $params = compact('target', 'style', 'data', 'action');
}
//Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/common.php b/inc/common.php
index 85187f16d..9cadb56fd 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -13,6 +13,7 @@ require_once(DOKU_INC.'inc/utf8.php');
require_once(DOKU_INC.'inc/mail.php');
require_once(DOKU_INC.'inc/parserutils.php');
require_once(DOKU_INC.'inc/infoutils.php');
+require_once DOKU_INC.'inc/subscription.php';
/**
* These constants are used with the recents function
@@ -117,8 +118,7 @@ function pageinfo(){
if(isset($_SERVER['REMOTE_USER'])){
$info['userinfo'] = $USERINFO;
$info['perm'] = auth_quickaclcheck($ID);
- $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER'],false);
- $info['subscribedns'] = is_subscribed($ID,$_SERVER['REMOTE_USER'],true);
+ $info['subscribed'] = get_info_subscribed();
$info['client'] = $_SERVER['REMOTE_USER'];
if($info['perm'] == AUTH_ADMIN){
@@ -1061,10 +1061,13 @@ function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){
}elseif($who == 'subscribers'){
if(!$conf['subscribers']) return; //subscribers enabled?
if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
- $bcc = subscriber_addresslist($id,false);
+ $data = array('id' => $id, 'addresslist' => '', 'self' => false);
+ trigger_event('COMMON_NOTIFY_ADDRESSLIST', $data,
+ 'subscription_addresslist');
+ $bcc = $data['addresslist'];
if(empty($bcc)) return;
$to = '';
- $text = rawLocale('subscribermail');
+ $text = rawLocale('subscr_single');
}elseif($who == 'register'){
if(empty($conf['registernotify'])) return;
$text = rawLocale('registermail');
@@ -1097,7 +1100,7 @@ function notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){
$text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true,'&'),$text);
require_once(DOKU_INC.'inc/DifferenceEngine.php');
$df = new Diff(explode("\n",rawWiki($id,$rev)),
- explode("\n",rawWiki($id)));
+ explode("\n",rawWiki($id)));
$dformat = new UnifiedDiffFormatter();
$diff = $dformat->format($df);
}else{
@@ -1273,97 +1276,6 @@ function obfuscate($email) {
}
/**
- * Let us know if a user is tracking a page or a namespace
- *
- * @author Andreas Gohr <andi@splitbrain.org>
- */
-function is_subscribed($id,$uid,$ns=false){
- if(!$ns) {
- $file=metaFN($id,'.mlist');
- } else {
- if(!getNS($id)) {
- $file = metaFN(getNS($id),'.mlist');
- } else {
- $file = metaFN(getNS($id),'/.mlist');
- }
- }
- if (@file_exists($file)) {
- $mlist = file($file);
- $pos = array_search($uid."\n",$mlist);
- return is_int($pos);
- }
-
- return false;
-}
-
-/**
- * Return a string with the email addresses of all the
- * users subscribed to a page
- *
- * @author Steven Danz <steven-danz@kc.rr.com>
- */
-function subscriber_addresslist($id,$self=true){
- global $conf;
- global $auth;
-
- if (!$conf['subscribers']) return '';
-
- $users = array();
- $emails = array();
-
- // load the page mlist file content
- $mlist = array();
- $file=metaFN($id,'.mlist');
- if (@file_exists($file)) {
- $mlist = file($file);
- foreach ($mlist as $who) {
- $who = rtrim($who);
- if(!$self && $who == $_SERVER['REMOTE_USER']) continue;
- $users[$who] = true;
- }
- }
-
- // load also the namespace mlist file content
- $ns = getNS($id);
- while ($ns) {
- $nsfile = metaFN($ns,'/.mlist');
- if (@file_exists($nsfile)) {
- $mlist = file($nsfile);
- foreach ($mlist as $who) {
- $who = rtrim($who);
- if(!$self && $who == $_SERVER['REMOTE_USER']) continue;
- $users[$who] = true;
- }
- }
- $ns = getNS($ns);
- }
- // root namespace
- $nsfile = metaFN('','.mlist');
- if (@file_exists($nsfile)) {
- $mlist = file($nsfile);
- foreach ($mlist as $who) {
- $who = rtrim($who);
- if(!$self && $who == $_SERVER['REMOTE_USER']) continue;
- $users[$who] = true;
- }
- }
- if(!empty($users)) {
- foreach (array_keys($users) as $who) {
- $info = $auth->getUserData($who);
- if($info === false) continue;
- $level = auth_aclcheck($id,$who,$info['grps']);
- if ($level >= AUTH_READ) {
- if (strcasecmp($info['mail'],$conf['notify']) != 0) {
- $emails[] = $info['mail'];
- }
- }
- }
- }
-
- return implode(',',$emails);
-}
-
-/**
* Removes quoting backslashes
*
* @author Andreas Gohr <andi@splitbrain.org>
@@ -1545,4 +1457,30 @@ function send_redirect($url){
exit;
}
-//Setup VIM: ex: et ts=4 enc=utf-8 :
+/**
+ * Validate a value using a set of valid values
+ *
+ * This function checks whether a specified value is set and in the array
+ * $valid_values. If not, the function returns a default value or, if no
+ * default is specified, throws an exception.
+ *
+ * @param string $param The name of the parameter
+ * @param array $valid_values A set of valid values; Optionally a default may
+ * be marked by the key “default”.
+ * @param array $array The array containing the value (typically $_POST
+ * or $_GET)
+ * @param string $exc The text of the raised exception
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function valid_input_set($param, $valid_values, $array, $exc = '') {
+ if (isset($array[$param]) && in_array($array[$param], $valid_values)) {
+ return $array[$param];
+ } elseif (isset($valid_values['default'])) {
+ return $valid_values['default'];
+ } else {
+ throw new Exception($exc);
+ }
+}
+
+//Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/confutils.php b/inc/confutils.php
index abfde8a80..de63846de 100644
--- a/inc/confutils.php
+++ b/inc/confutils.php
@@ -248,7 +248,6 @@ function actionOK($action){
if(isset($conf['resendpasswd']) && !$conf['resendpasswd']) $disabled[] = 'resendpwd';
if(isset($conf['subscribers']) && !$conf['subscribers']) {
$disabled[] = 'subscribe';
- $disabled[] = 'subscribens';
}
$disabled = array_unique($disabled);
}
diff --git a/inc/form.php b/inc/form.php
index 6d496f414..0a6bc2bba 100644
--- a/inc/form.php
+++ b/inc/form.php
@@ -283,6 +283,27 @@ class Doku_Form {
echo $this->getForm();
}
+ /**
+ * Add a radio set
+ *
+ * This function adds a set of radio buttons to the form. If $_POST[$name]
+ * is set, this radio is preselected, else the first radio button.
+ *
+ * @param string $name The HTML field name
+ * @param array $entries An array of entries $value => $caption
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+
+ function addRadioSet($name, $entries) {
+ $value = (isset($_POST[$name]) && isset($entries[$_POST[$name]])) ?
+ $_POST[$name] : key($entries);
+ foreach($entries as $val => $cap) {
+ $data = ($value === $val) ? array('checked' => 'checked') : array();
+ $this->addElement(form_makeRadioField($name, $val, $cap, '', '', $data));
+ }
+ }
+
}
/**
diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php
index d0c1f8b70..c3fefd926 100644
--- a/inc/lang/de/lang.php
+++ b/inc/lang/de/lang.php
@@ -203,11 +203,25 @@ $lang['img_copyr'] = 'Copyright';
$lang['img_format'] = 'Format';
$lang['img_camera'] = 'Kamera';
$lang['img_keywords'] = 'Schlagwörter';
-$lang['subscribe_success'] = '%s hat nun Änderungen der Seite %s abonniert';
-$lang['subscribe_error'] = '%s kann die Änderungen der Seite %s nicht abonnieren';
-$lang['subscribe_noaddress'] = 'Weil Ihre E-Mail-Adresse fehlt, können Sie das Thema nicht abonnieren';
-$lang['unsubscribe_success'] = 'Das Abonnement von %s für die Seite %s wurde aufgelöst';
-$lang['unsubscribe_error'] = 'Das Abonnement von %s für die Seite %s konnte nicht aufgelöst werden';
+
+$lang['subscr_subscribe_success'] = '%s hat nun Änderungen der Seite %s abonniert';
+$lang['subscr_subscribe_error'] = '%s kann die Änderungen der Seite %s nicht abonnieren';
+$lang['subscr_subscribe_noaddress']= 'Weil Ihre E-Mail-Adresse fehlt, können Sie das Thema nicht abonnieren';
+$lang['subscr_unsubscribe_success']= 'Das Abonnement von %s für die Seite %s wurde aufgelöst';
+$lang['subscr_unsubscribe_error'] = 'Das Abonnement von %s für die Seite %s konnte nicht aufgelöst werden';
+$lang['subscr_already_subscribed'] = '%s hat %s bereits abonniert';
+$lang['subscr_not_subscribed'] = '%s hat %s nicht abonniert';
+// Manage page for subscriptions
+$lang['subscr_m_not_subscribed'] = 'Sie haben die aktuelle Seite und ihre Namensräume nicht abonniert.';
+$lang['subscr_m_new_header'] = 'Abonnement hinzufügen';
+$lang['subscr_m_current_header'] = 'Aktuelle Abonnements';
+$lang['subscr_m_unsubscribe'] = 'Löschen';
+$lang['subscr_m_subscribe'] = 'Abonnieren';
+
+$lang['subscr_style_every'] = 'Email bei jeder Bearbeitung';
+$lang['subscr_style_digest'] = 'Übersichtsemail für jede veränderte Seite';
+$lang['subscr_style_list'] = 'Email mit Liste der geänderten Seiten';
+
$lang['authmodfailed'] = 'Benutzerüberprüfung nicht möglich. Bitte wenden Sie sich an den Systembetreuer.';
$lang['authtempfail'] = 'Benutzerüberprüfung momentan nicht möglich. Falls das Problem andauert, wenden Sie sich an den Systembetreuer.';
$lang['i_chooselang'] = 'Wählen Sie Ihre Sprache';
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index cf5173d05..98ded12ca 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -39,10 +39,7 @@ $lang['btn_delete'] = 'Delete';
$lang['btn_back'] = 'Back';
$lang['btn_backlink'] = "Backlinks";
$lang['btn_backtomedia'] = 'Back to Mediafile Selection';
-$lang['btn_subscribe'] = 'Subscribe Page Changes';
-$lang['btn_unsubscribe'] = 'Unsubscribe Page Changes';
-$lang['btn_subscribens'] = 'Subscribe Namespace Changes';
-$lang['btn_unsubscribens'] = 'Unsubscribe Namespace Changes';
+$lang['btn_subscribe'] = 'Manage Subscriptions';
$lang['btn_profile'] = 'Update Profile';
$lang['btn_reset'] = 'Reset';
$lang['btn_resendpwd'] = 'Send new password';
@@ -158,6 +155,7 @@ $lang['download'] = 'Download Snippet';
$lang['mail_newpage'] = 'page added:';
$lang['mail_changed'] = 'page changed:';
+$lang['mail_subscribe_list'] = 'pages changed in namespace:';
$lang['mail_new_user'] = 'new user:';
$lang['mail_upload'] = 'file uploaded:';
@@ -212,11 +210,24 @@ $lang['img_format'] = 'Format';
$lang['img_camera'] = 'Camera';
$lang['img_keywords']= 'Keywords';
-$lang['subscribe_success'] = 'Added %s to subscription list for %s';
-$lang['subscribe_error'] = 'Error adding %s to subscription list for %s';
-$lang['subscribe_noaddress']= 'There is no address associated with your login, you cannot be added to the subscription list';
-$lang['unsubscribe_success']= 'Removed %s from subscription list for %s';
-$lang['unsubscribe_error'] = 'Error removing %s from subscription list for %s';
+$lang['subscr_subscribe_success'] = 'Added %s to subscription list for %s';
+$lang['subscr_subscribe_error'] = 'Error adding %s to subscription list for %s';
+$lang['subscr_subscribe_noaddress']= 'There is no address associated with your login, you cannot be added to the subscription list';
+$lang['subscr_unsubscribe_success']= 'Removed %s from subscription list for %s';
+$lang['subscr_unsubscribe_error'] = 'Error removing %s from subscription list for %s';
+$lang['subscr_already_subscribed'] = '%s is already subscribed to %s';
+$lang['subscr_not_subscribed'] = '%s is not subscribed to %s';
+// Manage page for subscriptions
+$lang['subscr_m_not_subscribed'] = 'You are currently not subscribed to the current page or namespace.';
+$lang['subscr_m_new_header'] = 'Add subscription';
+$lang['subscr_m_current_header'] = 'Current subscriptions';
+$lang['subscr_m_unsubscribe'] = 'Unsubscribe';
+$lang['subscr_m_subscribe'] = 'Subscribe';
+$lang['subscr_m_receive'] = 'Receive';
+$lang['subscr_style_every'] = 'email on every change';
+$lang['subscr_style_digest'] = 'digest email of changes for each page';
+$lang['subscr_style_list'] = 'list of changed pages since last email';
+
/* auth.class language support */
$lang['authmodfailed'] = 'Bad user authentication configuration. Please inform your Wiki Admin.';
diff --git a/inc/lang/en/subscr_digest.txt b/inc/lang/en/subscr_digest.txt
new file mode 100644
index 000000000..35011b6e6
--- /dev/null
+++ b/inc/lang/en/subscr_digest.txt
@@ -0,0 +1,20 @@
+Hello!
+
+The page @PAGE@ in the @TITLE@ wiki changed.
+Here are the changes:
+
+--------------------------------------------------------
+@DIFF@
+--------------------------------------------------------
+
+Old Revision: @OLDPAGE@
+New Revision: @NEWPAGE@
+
+To cancel the page notifications, log into the wiki at
+@DOKUWIKIURL@ then visit
+@SUBSCRIBE@
+and unsubscribe page and/or namespace changes.
+
+--
+This mail was generated by DokuWiki at
+@DOKUWIKIURL@
diff --git a/inc/lang/en/subscr_form.txt b/inc/lang/en/subscr_form.txt
new file mode 100644
index 000000000..d606508c6
--- /dev/null
+++ b/inc/lang/en/subscr_form.txt
@@ -0,0 +1,3 @@
+====== Subscription Management ======
+
+This page allows you to manage your subscriptions for the current page and namespace.
diff --git a/inc/lang/en/subscr_list.txt b/inc/lang/en/subscr_list.txt
new file mode 100644
index 000000000..efe27d866
--- /dev/null
+++ b/inc/lang/en/subscr_list.txt
@@ -0,0 +1,17 @@
+Hello!
+
+Pages in the namespace @PAGE@ of the @TITLE@ wiki changed.
+Here are the changed pages:
+
+--------------------------------------------------------
+@DIFF@
+--------------------------------------------------------
+
+To cancel the page notifications, log into the wiki at
+@DOKUWIKIURL@ then visit
+@SUBSCRIBE@
+and unsubscribe page and/or namespace changes.
+
+--
+This mail was generated by DokuWiki at
+@DOKUWIKIURL@
diff --git a/inc/lang/en/subscribermail.txt b/inc/lang/en/subscr_single.txt
index 673c4c32a..673c4c32a 100644
--- a/inc/lang/en/subscribermail.txt
+++ b/inc/lang/en/subscr_single.txt
diff --git a/inc/pageutils.php b/inc/pageutils.php
index 9c192e5e6..239ff41c5 100644
--- a/inc/pageutils.php
+++ b/inc/pageutils.php
@@ -534,4 +534,21 @@ function isVisiblePage($id){
return !isHiddenPage($id);
}
+/**
+ * Format an id for output to a user
+ *
+ * Namespaces are denoted by a trailing “:*”. The root namespace is
+ * “*”. Output is escaped.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function prettyprint_id($id) {
+ if (!$id || $id === ':') {
+ return '*';
+ }
+ if ((substr($id, -1, 1) === ':')) {
+ $id .= '*';
+ }
+ return hsc($id);
+}
diff --git a/inc/subscription.php b/inc/subscription.php
new file mode 100644
index 000000000..12f0a4b82
--- /dev/null
+++ b/inc/subscription.php
@@ -0,0 +1,349 @@
+<?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 $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
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+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;
+ }
+
+ // 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($user, $page, null)) {
+ return false;
+ }
+ }
+
+ $file = subscription_filename($page);
+ $content = auth_nameencode($user) . ' ' . $style;
+ if (!is_null($data)) {
+ $content .= ' ' . $data;
+ }
+ 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(':' => 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";
+ }
+ 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
+ *
+ * This is the default action for COMMON_NOTIFY_ADDRESSLIST.
+ *
+ * @param array $data Containing $id (the page id), $self (whether the author
+ * should be notified, $addresslist (current email address
+ list)
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function subscription_addresslist($data){
+ global $conf;
+ global $auth;
+
+ $id = $data['id'];
+ $self = $data['self'];
+ $addresslist = $data['addresslist'];
+
+ 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'];
+ }
+ }
+ }
+ }
+ $data['addresslist'] = trim($addresslist . ',' . 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);
+}
diff --git a/inc/template.php b/inc/template.php
index 4681300eb..84fbda051 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -126,6 +126,9 @@ function tpl_content_core(){
case 'admin':
tpl_admin();
break;
+ case 'subscribe':
+ tpl_subscribe();
+ break;
default:
$evt = new Doku_Event('TPL_ACT_UNKNOWN',$ACT);
if ($evt->advise_before())
@@ -540,31 +543,10 @@ function tpl_button($type,$return=false){
}
break;
case 'subscribe':
- case 'subscription':
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
- if($_SERVER['REMOTE_USER']){
- if($INFO['subscribed']){
- if(actionOK('unsubscribe'))
- $out .= html_btn('unsubscribe',$ID,'',array('do' => 'unsubscribe',));
- } else {
- if(actionOK('subscribe'))
- $out .= html_btn('subscribe',$ID,'',array('do' => 'subscribe',));
- }
- }
- }
- if($type == 'subscribe') break;
- // else: fall through for backward compatibility
- case 'subscribens':
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
- if($_SERVER['REMOTE_USER']){
- if($INFO['subscribedns']){
- if(actionOK('unsubscribens'))
- $out .= html_btn('unsubscribens',$ID,'',array('do' => 'unsubscribens',));
- } else {
- if(actionOK('subscribens'))
- $out .= html_btn('subscribens',$ID,'',array('do' => 'subscribens',));
- }
- }
+ if ($conf['useacl'] && $auth && $ACT == 'show' &&
+ $conf['subscribers'] && isset($_SERVER['REMOTE_USER']) &&
+ actionOK('subscribe')) {
+ $out .= html_btn('subscribe',$ID,'',array('do' => 'subscribe',));
}
break;
case 'backlink':
@@ -712,37 +694,12 @@ function tpl_actionlink($type,$pre='',$suf='',$inner='',$return=false){
break;
case 'subscribe':
case 'subscription':
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
+ if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers']) {
if($_SERVER['REMOTE_USER']){
- if($INFO['subscribed']) {
- if(actionOK('unsubscribe'))
- $out .= tpl_link(wl($ID,'do=unsubscribe'),
- $pre.(($inner)?$inner:$lang['btn_unsubscribe']).$suf,
- 'class="action unsubscribe" rel="nofollow"',1);
- } else {
if(actionOK('subscribe'))
$out .= tpl_link(wl($ID,'do=subscribe'),
$pre.(($inner)?$inner:$lang['btn_subscribe']).$suf,
'class="action subscribe" rel="nofollow"',1);
- }
- }
- }
- if($type == 'subscribe') break;
- // else: fall through for backward compatibility
- case 'subscribens':
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
- if($_SERVER['REMOTE_USER']){
- if($INFO['subscribedns']) {
- if(actionOK('unsubscribens'))
- $out .= tpl_link(wl($ID,'do=unsubscribens'),
- $pre.(($inner)?$inner:$lang['btn_unsubscribens']).$suf,
- 'class="action unsubscribens" rel="nofollow"',1);
- } else {
- if(actionOK('subscribens'))
- $out .= tpl_link(wl($ID,'do=subscribens'),
- $pre.(($inner)?$inner:$lang['btn_subscribens']).$suf,
- 'class="action subscribens" rel="nofollow"',1);
- }
}
}
break;
@@ -1323,23 +1280,9 @@ function tpl_actiondropdown($empty='',$button='&gt;'){
echo '<option value="profile">'.$lang['btn_profile'].'</option>';
}
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
+ if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers']){
if($_SERVER['REMOTE_USER']){
- if($INFO['subscribed']) {
- echo '<option value="unsubscribe">'.$lang['btn_unsubscribe'].'</option>';
- } else {
echo '<option value="subscribe">'.$lang['btn_subscribe'].'</option>';
- }
- }
- }
-
- if($conf['useacl'] && $auth && $ACT == 'show' && $conf['subscribers'] == 1){
- if($_SERVER['REMOTE_USER']){
- if($INFO['subscribedns']) {
- echo '<option value="unsubscribens">'.$lang['btn_unsubscribens'].'</option>';
- } else {
- echo '<option value="subscribens">'.$lang['btn_subscribens'].'</option>';
- }
}
}
@@ -1406,5 +1349,70 @@ function tpl_include_page($pageid,$print=true){
echo $html;
}
+/**
+ * Display the subscribe form
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ */
+function tpl_subscribe() {
+ global $INFO;
+ global $ID;
+ global $lang;
+
+ echo p_locale_xhtml('subscr_form');
+ echo '<h2>' . $lang['subscr_m_current_header'] . '</h2>';
+ echo '<div class="level2">';
+ if ($INFO['subscribed'] === false) {
+ echo '<p>' . $lang['subscr_m_not_subscribed'] . '</p>';
+ } else {
+ echo '<ul>';
+ foreach($INFO['subscribed'] as $sub) {
+ echo '<li><div class="li">';
+ if ($sub['target'] !== $ID) {
+ echo '<code class="ns">'.hsc(prettyprint_id($sub['target'])).'</code>';
+ } else {
+ echo '<code class="page">'.hsc(prettyprint_id($sub['target'])).'</code>';
+ }
+ $sstl = $lang['subscr_style_'.$sub['style']];
+ if(!$sstl) $sstl = hsc($sub['style']);
+ echo ' ('.$sstl.') ';
+
+ echo '<a href="'.wl($ID,array('do'=>'subscribe','sub_target'=>$sub['target'],'sub_style'=>$sub['style'],'sub_action'=>'unsubscribe')).'" class="unsubscribe">'.$lang['subscr_m_unsubscribe'].'</a>';
+
+
+ echo '</div></li>';
+ }
+ echo '</ul>';
+ }
+ echo '</div>';
+
+ // Add new subscription form
+ echo '<h2>' . $lang['subscr_m_new_header'] . '</h2>';
+ echo '<div class="level2">';
+ $ns = getNS($ID).':';
+ $targets = array(
+ $ID => '<code class="page">'.prettyprint_id($ID).'</code>',
+ $ns => '<code class="ns">'.prettyprint_id($ns).'</code>',
+ );
+ $styles = array(
+ 'every' => $lang['subscr_style_every'],
+ 'digest' => $lang['subscr_style_digest'],
+ 'list' => $lang['subscr_style_list'],
+ );
+
+ $form = new Doku_Form(array('id' => 'subscribe__form'));
+ $form->startFieldset($lang['subscr_m_subscribe']);
+ $form->addRadioSet('sub_target', $targets);
+ $form->startFieldset($lang['subscr_m_receive']);
+ $form->addRadioSet('sub_style', $styles);
+ $form->addHidden('sub_action', 'subscribe');
+ $form->addHidden('do', 'subscribe');
+ $form->addHidden('id', $ID);
+ $form->endFieldset();
+ $form->addElement(form_makeButton('submit', 'subscribe', $lang['subscr_m_subscribe']));
+ html_form('SUBSCRIBE', $form);
+ echo '</div>';
+}
+
//Setup VIM: ex: et ts=4 enc=utf-8 :