/** * 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 <*****@*****.**> */ 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); }
/** * Send digest and list mails for all subscriptions which are in effect for the * current page * * @author Adrian Lang <*****@*****.**> */ function sendDigest() { echo 'sendDigest(): started' . NL; global $ID; global $conf; if (!$conf['subscribers']) { echo 'sendDigest(): disabled' . NL; return false; } $subscriptions = subscription_find($ID, array('style' => '(digest|list)', 'escaped' => true)); /** @var auth_basic $auth */ global $auth; global $lang; global $conf; global $USERINFO; // remember current user info $olduinfo = $USERINFO; $olduser = $_SERVER['REMOTE_USER']; foreach ($subscriptions as $id => $users) { if (!subscription_lock($id)) { continue; } foreach ($users as $data) { list($user, $style, $lastupdate) = $data; $lastupdate = (int) $lastupdate; if ($lastupdate + $conf['subscribe_time'] > time()) { // Less than the configured time period passed since last // update. continue; } // Work as the user to make sure ACLs apply correctly $USERINFO = $auth->getUserData($user); $_SERVER['REMOTE_USER'] = $user; if ($USERINFO === false) { continue; } if (substr($id, -1, 1) === ':') { // The subscription target is a namespace $changes = getRecentsSince($lastupdate, null, getNS($id)); } else { if (auth_quickaclcheck($id) < AUTH_READ) { continue; } $meta = p_get_metadata($id); $changes = array($meta['last_change']); } // Filter out pages only changed in small and own edits $change_ids = array(); foreach ($changes as $rev) { $n = 0; while (!is_null($rev) && $rev['date'] >= $lastupdate && ($_SERVER['REMOTE_USER'] === $rev['user'] || $rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) { $rev = getRevisions($rev['id'], $n++, 1); $rev = count($rev) > 0 ? $rev[0] : null; } if (!is_null($rev) && $rev['date'] >= $lastupdate) { // Some change was not a minor one and not by myself $change_ids[] = $rev['id']; } } if ($style === 'digest') { foreach ($change_ids as $change_id) { subscription_send_digest($USERINFO['mail'], $change_id, $lastupdate); } } elseif ($style === 'list') { subscription_send_list($USERINFO['mail'], $change_ids, $id); } // TODO: Handle duplicate subscriptions. // Update notification time. subscription_set($user, $id, $style, time(), true); } subscription_unlock($id); } // restore current user info $USERINFO = $olduinfo; $_SERVER['REMOTE_USER'] = $olduser; echo 'sendDigest(): finished' . NL; return true; }
/** * Handle page 'subscribe' * * Throws exception on error. * * @author Adrian Lang <*****@*****.**> */ function act_subscription($act) { global $lang; global $INFO; global $ID; // 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'] || !checkSecurityToken()) { 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'; }
/** * Send digest and list mails for all subscriptions which are in effect for the * current page * * @author Adrian Lang <*****@*****.**> */ function sendDigest() { echo 'sendDigest(): start' . NL; global $ID; global $conf; if (!$conf['subscribers']) { return; } $subscriptions = subscription_find($ID, array('style' => '(digest|list)', 'escaped' => true)); global $auth; global $lang; global $conf; global $USERINFO; // remember current user info $olduinfo = $USERINFO; $olduser = $_SERVER['REMOTE_USER']; foreach ($subscriptions as $id => $users) { if (!subscription_lock($id)) { continue; } foreach ($users as $data) { list($user, $style, $lastupdate) = $data; $lastupdate = (int) $lastupdate; if ($lastupdate + $conf['subscribe_time'] > time()) { // Less than the configured time period passed since last // update. continue; } // Work as the user to make sure ACLs apply correctly $USERINFO = $auth->getUserData($user); $_SERVER['REMOTE_USER'] = $user; if ($USERINFO === false) { continue; } if (substr($id, -1, 1) === ':') { // The subscription target is a namespace $changes = getRecentsSince($lastupdate, null, getNS($id)); if (count($changes) === 0) { continue; } if ($style === 'digest') { foreach ($changes as $change) { subscription_send_digest($USERINFO['mail'], $change, $lastupdate); } } elseif ($style === 'list') { subscription_send_list($USERINFO['mail'], $changes, $id); } // TODO: Handle duplicate subscriptions. } else { if (auth_quickaclcheck($id) < AUTH_READ) { continue; } $meta = p_get_metadata($id); $rev = $meta['last_change']['date']; if ($rev < $lastupdate) { // There is no new revision. continue; } subscription_send_digest($USERINFO['mail'], $meta['last_change'], $lastupdate); } // Update notification time. subscription_set($user, $id, $style, time(), true); } subscription_unlock($id); } // restore current user info $USERINFO = $olduinfo; $_SERVER['REMOTE_USER'] = $olduser; }