1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
|
<?php
namespace dokuwiki\Subscriptions;
use dokuwiki\Input\Input;
use DokuWiki_Auth_Plugin;
use Exception;
class SubscriberManager
{
/**
* Check if subscription system is enabled
*
* @return bool
*/
public function isenabled()
{
return actionOK('subscribe');
}
/**
* 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.
*
* @throws Exception when user or style is empty
*
* @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
*
* @return bool
*/
public function add($id, $user, $style, $data = '')
{
if (!$this->isenabled()) {
return false;
}
// delete any existing subscription
$this->remove($id, $user);
$user = auth_nameencode(trim($user));
$style = trim($style);
$data = trim($data);
if (!$user) {
throw new Exception('no subscription user given');
}
if (!$style) {
throw new Exception('no subscription style given');
}
if (!$data) {
$data = time();
} //always add current time for new subscriptions
$line = "$user $style $data\n";
$file = $this->file($id);
return io_saveFile($file, $line, true);
}
/**
* 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)
{
if (!$this->isenabled()) {
return false;
}
$file = $this->file($id);
if (!file_exists($file)) {
return true;
}
$regexBuilder = new SubscriberRegexBuilder();
$re = $regexBuilder->buildRegex($user, $style, $data);
return io_deleteFromFile($file, $re, true);
}
/**
* 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>
*
* @param string $id Page ID, defaults to global $ID
* @param string $user User, defaults to $_SERVER['REMOTE_USER']
*
* @return array|false
*/
public function userSubscription($id = '', $user = '')
{
if (!$this->isenabled()) {
return false;
}
global $ID;
/** @var Input $INPUT */
global $INPUT;
if (!$id) {
$id = $ID;
}
if (!$user) {
$user = $INPUT->server->str('REMOTE_USER');
}
if (empty($user)) {
// not logged in
return false;
}
$subs = $this->subscribers($id, $user);
if (!count($subs)) {
return false;
}
$result = [];
foreach ($subs as $target => $info) {
$result[] = [
'target' => $target,
'style' => $info[$user][0],
'data' => $info[$user][1],
];
}
return $result;
}
/**
* 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)
{
if (!$this->isenabled()) {
return [];
}
// Construct list of files which may contain relevant subscriptions.
$files = [':' => $this->file(':')];
do {
$files[$page] = $this->file($page);
$page = getNS(rtrim($page, ':')) . ':';
} while ($page !== ':');
$regexBuilder = new SubscriberRegexBuilder();
$re = $regexBuilder->buildRegex($user, $style, $data);
// Handle files.
$result = [];
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] = [];
}
$result[$target][$u] = [$m[2], $m[3]]; // add to result
}
}
return array_reverse($result);
}
/**
* Default callback for COMMON_NOTIFY_ADDRESSLIST
*
* Aggregates all email addresses of user who have subscribed the given page with 'every' style
*
* @author Adrian Lang <lang@cosmocode.de>
* @author Steven Danz <steven-danz@kc.rr.com>
*
* @todo move the whole functionality into this class, trigger SUBSCRIPTION_NOTIFY_ADDRESSLIST instead,
* use an array for the addresses within it
*
* @param array &$data Containing the entries:
* - $id (the page id),
* - $self (whether the author should be notified,
* - $addresslist (current email address list)
* - $replacements (array of additional string substitutions, @KEY@ to be replaced by value)
*/
public function notifyAddresses(&$data)
{
if (!$this->isenabled()) {
return;
}
/** @var DokuWiki_Auth_Plugin $auth */
global $auth;
global $conf;
/** @var \Input $INPUT */
global $INPUT;
$id = $data['id'];
$self = $data['self'];
$addresslist = $data['addresslist'];
$subscriptions = $this->subscribers($id, null, 'every');
$result = [];
foreach ($subscriptions as $target => $users) {
foreach ($users as $user => $info) {
$userinfo = $auth->getUserData($user);
if ($userinfo === false) {
continue;
}
if (!$userinfo['mail']) {
continue;
}
if (!$self && $user == $INPUT->server->str('REMOTE_USER')) {
continue;
} //skip our own changes
$level = auth_aclcheck($id, $user, $userinfo['grps']);
if ($level >= AUTH_READ) {
if (strcasecmp($userinfo['mail'], $conf['notify']) != 0) { //skip user who get notified elsewhere
$result[$user] = $userinfo['mail'];
}
}
}
}
$data['addresslist'] = trim($addresslist . ',' . implode(',', $result), ',');
}
/**
* 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);
}
}
|