dockerwiki/content/inc/Subscriptions/SubscriberManager.php

291 lines
8.5 KiB
PHP
Raw Normal View History

2021-10-26 13:02:53 +02:00
<?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 objects (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 objects (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);
}
}