2 rcs_id('$Id: ModeratedPage.php,v 1.4 2005-01-29 19:52:09 rurban Exp $');
4 Copyright 2004,2005 $ThePhpWikiProgrammingTeam
6 This file is part of PhpWiki.
8 PhpWiki is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 PhpWiki is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with PhpWiki; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * This plugin requires an action page (default: ModeratedPage)
25 * and provides delayed execution of restricted actions,
26 * after a special moderators request. Usually by email.
27 * http://mywiki/SomeModeratedPage?action=ModeratedPage&id=kdclcr78431zr43uhrn&pass=approve
29 * Not yet ready! part 3/3 is missing: The moderator approve/reject methods.
31 * See http://phpwiki.org/PageModeration
35 require_once("lib/WikiPlugin.php");
37 class WikiPlugin_ModeratedPage
41 return _("ModeratedPage");
43 function getDescription () {
44 return _("Support moderated pages");
46 function getVersion() {
47 return preg_replace("/[Revision: $]/", '',
50 function getDefaultArguments() {
51 return array('page' => '[pagename]',
52 'moderators' => false,
53 'require_level' => false, // 1=bogo
54 'require_access' => 'edit,remove,change',
60 function run($dbi, $argstr, &$request, $basepage) {
61 $args = $this->getArgs($argstr, $request);
63 // handle moderation request from the email
64 if (!empty($args['id']) and !empty($args['pass'])) {
66 return $this->error("No page specified");
67 $page = $dbi->getPage($args['page']);
68 if ($moderated = $page->get("moderated")) {
69 if (array_key_exists($args['id'], $moderated['data'])) {
70 $moderation = $moderated['data'][$args['id']];
73 if ($args['pass'] == 'approve')
74 return $this->approve($request, $args, $moderation);
75 elseif ($args['pass'] == 'reject')
76 return $this->reject($request, $args, $moderation);
78 return $this->error("Wrong pass ".$args['pass']);
80 return $this->error("Wrong id ".htmlentities($args['id']));
88 * resolve moderators and require_access (not yet) from actionpage plugin argstr
90 function resolve_argstr(&$request, $argstr) {
91 $args = $this->getArgs($argstr);
92 $group = $request->getGroup();
93 if (empty($args['moderators'])) {
94 $admins = $group->getSpecialMembersOf(GROUP_ADMIN);
95 // email or usernames?
96 $args['moderators'] = array_merge($admins, array(ADMIN_USER));
98 // resolve possible group names
99 $moderators = explode(',', $args['moderators']);
100 for ($i=0; $i < count($moderators); $i++) {
101 $members = $group->getMembersOf($moderators[$i]);
102 if (!empty($members)) {
103 array_splice($moderators, $i, 1, $members);
106 if (!$moderators) $moderators = array(ADMIN_USER);
107 $args['moderators'] = $moderators;
109 //resolve email for $args['moderators']
110 $page = $request->getPage();
112 foreach ($args['moderators'] as $userid) {
115 list($args['emails'], $args['moderators']) =
116 $page->getPageChangeEmails(array($page->getName() => $users));
118 if (!empty($args['require_access'])) {
119 $args['require_access'] = preg_split("/\s*,\s*/", $args['require_access']);
120 if (empty($args['require_access']))
121 unset($args['require_access']);
123 if ($args['require_level'] !== false) {
124 $args['require_level'] = (integer) $args['require_level'];
127 unset($args['page']);
128 unset($args['pass']);
133 * Handle client-side moderation change request.
134 * Hook called on the lock action, if moderation metadata already exists.
136 function lock_check(&$request, &$page, $moderated) {
137 $action_page = $request->getPage(_("ModeratedPage"));
138 $status = $this->getSiteStatus($request, $action_page);
139 if (is_array($status)) {
140 if (!empty($status['emails'])) {
141 trigger_error(_("ModeratedPage: No emails for the moderators defined"),
145 $page->set('moderation', array('_status' => $status));
146 return $this->notice(
147 fmt("ModeratedPage status update:\n Moderators: '%s'\n require_access: '%s'",
148 join(',', $status['moderators']), $status['require_access']));
150 $page->set('moderation', false);
151 return $this->notice(HTML($status,
152 fmt("'%s' is no ModeratedPage anymore.", $page->getName())));
157 * Handle client-side moderation change request by the user.
158 * Hook called on the lock action, if moderation metadata should be added.
159 * Need to store the the plugin args (who, when) in the page meta-data
161 function lock_add(&$request, &$page, &$action_page) {
162 $status = $this->getSiteStatus($request, $action_page);
163 if (is_array($status)) {
164 if (!empty($status['emails'])) {
165 trigger_error(_("ModeratedPage: No emails for the moderators defined"),
169 $page->set('moderation', array('_status' => $status));
170 return $this->notice(
171 fmt("ModeratedPage status update: '%s' is now a ModeratedPage.\n Moderators: '%s'\n require_access: '%s'",
172 $page->getName(), join(',', $status['moderators']), $status['require_access']));
179 function notice($msg) {
180 return HTML::div(array('class' => 'wiki-edithelp'), $msg);
183 function generateId() {
186 for ($i = 1; $i <= 16; $i++) {
187 $r = function_exists('mt_rand') ? mt_rand(55, 90) : rand(55, 90);
188 $s .= chr(($r < 65) ? ($r-17) : $r);
194 * Handle client-side POST moderation request on any moderated page.
195 * if ($page->get('moderation')) WikiPlugin_ModeratedPage::handler(...);
196 * return false if not handled (pass through), true if handled and displayed.
198 function handler(&$request, &$page) {
199 $action = $request->getArg('action');
200 $moderated = $page->get('moderated');
201 // cached version, need re-lock of each page to update moderators
202 if (!empty($moderated['_status']))
203 $status = $moderated['_status'];
205 $action_page = $request->getPage(_("ModeratedPage"));
206 $status = $this->getSiteStatus($request, $action_page);
207 $moderated['_status'] = $status;
209 if (!empty($status['emails'])) {
210 trigger_error(_("ModeratedPage: No emails for the moderators defined"), E_USER_WARNING);
214 if (!empty($status['require_access'])
215 and !in_array(action2access($action), $status['require_access']))
216 return false; // allow and fall through, not moderated
217 if (!empty($status['require_level']) and $request->_user->_level >= $status['require_level'])
218 return false; // allow and fall through, not moderated
219 // else all post actions are moderated by default
220 if (1) /* or in_array($action, array('edit','remove','rename')*/ {
221 //$moderated = $page->get('moderated');
222 $id = $this->generateId();
223 while (!empty($moderated[$id])) $id = $this->generateId(); // avoid duplicates
224 $moderated['id'] = $id; // overwrite current id
225 $moderated['data'][$id] = array( // add current request
226 'timestamp' => time(),
227 'userid' => $request->_user->getId(),
228 'args' => $request->getArgs(),
229 'user' => serialize($request->_user),
231 $this->_tokens['CONTENT'] = HTML::div(array('class' => 'wikitext'),
232 fmt("%s: action forwarded to moderator %s",
234 join(", ", $status['moderators'])
237 $pagename = $page->getName();
238 $subject = "[".WIKI_NAME.'] '.$action.': '._("ModeratedPage").' '.$pagename;
239 if (mail(join(",", $status['emails']),
241 $action.': '._("ModeratedPage").' '.$pagename."\n"
242 . serialize($moderated['data'][$id])
243 ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"),
244 'id' => $id, 'pass' => 'approve'), 1).">"
245 ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"),
246 'id' => $id, 'pass' => 'reject'), 1).">\n"
248 $page->set('moderated', $moderated);
249 return false; // pass thru
251 //FIXME: This msg get lost on the edit redirect
252 trigger_error(_("ModeratedPage Notification Error: Couldn't send email"),
261 * Handle admin-side moderation resolve.
262 * We might have to convert the GET to a POST request to continue
263 * with the left-over stored request.
264 * Better we display a post form for verification.
266 function approve(&$request, $args, &$moderation) {
267 // check id, convert to POST, continue
268 if ($request->isPost()) {
269 $this->error("ModeratedPage::approve not yet implemented");
271 return $this->_approval_form($request, $args, $moderation, 'approve');
275 * Handle admin-side moderation resolve.
277 function reject(&$request, $args, &$moderation) {
278 // check id, delete action
279 if ($request->isPost()) {
280 $this->error("ModeratedPage::reject not yet implemented");
282 return $this->_approval_form($request, $args, $moderation, 'reject');
286 function _approval_form(&$request, $args, $moderation, $pass='approve') {
287 $header = HTML::h3(_("Please approve or reject this request:"));
288 $loader = new WikiPluginLoader();
289 $BackendInfo = $loader->getPlugin("_BackendInfo");
290 $content = HTML::table(array('border' => 1,
292 'cellspacing' => 0));
294 $BackendInfo->_fixupData($myargs);
295 $content->pushContent($BackendInfo->_showhash("request args", $myargs));
296 $BackendInfo->_fixupData($moderation);
297 $content->pushContent($BackendInfo->_showhash("moderation data", $moderation));
298 $approve = Button('submit:ModeratedPage[approve]', _("Approve"),
299 $pass == 'approve' ? 'wikiadmin' : 'button');
300 $reject = Button('submit:ModeratedPage[reject]', _("Reject"),
301 $pass == 'reject' ? 'wikiadmin' : 'button');
302 return HTML::form(array('action' => $request->getPostURL(),
307 : HiddenInputs(array('require_authority_for_post' => WIKIAUTH_ADMIN)),
309 $pass == 'approve' ? HTML::p($approve, $reject)
310 : HTML::p($reject, $approve));
314 * Get the side-wide ModeratedPage status, reading the action-page args.
315 * Who are the moderators? What actions should be moderated?
317 function getSiteStatus(&$request, &$action_page) {
318 $loader = new WikiPluginLoader();
319 $rev = $action_page->getCurrentRevision();
320 $content = $rev->getPackedContent();
321 list($pi) = explode("\n", $content, 2); // plugin ModeratedPage must be first line!
322 if ($parsed = $loader->parsePI($pi)) {
323 $plugin =& $parsed[1];
324 if ($plugin->getName() != _("ModeratedPage"))
325 return $this->error(sprintf(_("<?plugin ModeratedPage ... ?> not found in first line of %s"),
326 $action_page->getName()));
327 if (!$action_page->get('locked'))
328 return $this->error(sprintf(_("%s is not locked!"),
329 $action_page->getName()));
330 return $plugin->resolve_argstr($request, $parsed[2]);
332 return $this->error(sprintf(_("<?plugin ModeratedPage ... ?> not found in first line of %s"),
333 $action_page->getName()));
339 // $Log: not supported by cvs2svn $
340 // Revision 1.3 2004/12/06 19:50:05 rurban
341 // enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
342 // renamed delete_page to purge_page.
343 // enable action=edit&version=-1 to force creation of a new version.
344 // added BABYCART_PATH config
345 // fixed magiqc in adodb.inc.php
346 // and some more docs
348 // Revision 1.2 2004/11/30 17:46:49 rurban
349 // added ModeratedPage POST action hook (part 2/3)
351 // Revision 1.1 2004/11/19 19:22:35 rurban
352 // ModeratePage part1: change status
360 // c-hanging-comment-ender-p: nil
361 // indent-tabs-mode: nil