]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/ModeratedPage.php
just aesthetics
[SourceForge/phpwiki.git] / lib / plugin / ModeratedPage.php
1 <?php // -*-php-*-
2 rcs_id('$Id: ModeratedPage.php,v 1.5 2006-08-15 13:41:08 rurban Exp $');
3 /*
4  Copyright 2004,2005 $ThePhpWikiProgrammingTeam
5  
6  This file is part of PhpWiki.
7
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.
12
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.
17
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
21  */
22
23 /**
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
28  *
29  * Not yet ready! part 3/3 is missing: The moderator approve/reject methods.
30  *
31  * See http://phpwiki.org/PageModeration
32  * Author: ReiniUrban
33  */
34
35 require_once("lib/WikiPlugin.php");
36
37 class WikiPlugin_ModeratedPage
38 extends WikiPlugin
39 {
40     function getName () {
41         return _("ModeratedPage");
42     }
43     function getDescription () {
44         return _("Support moderated pages");
45     }
46     function getVersion() {
47         return preg_replace("/[Revision: $]/", '',
48                             "\$Revision: 1.5 $");
49     }
50     function getDefaultArguments() {
51         return array('page'          => '[pagename]',
52                      'moderators'    => false,
53                      'require_level' => false,   // 1=bogo
54                      'require_access' => 'edit,remove,change',
55                      'id'   => '',
56                      'pass' => '',
57                     );
58     }
59
60     function run($dbi, $argstr, &$request, $basepage) {
61         $args = $this->getArgs($argstr, $request);
62
63         // handle moderation request from the email
64         if (!empty($args['id']) and !empty($args['pass'])) {
65             if (!$args['page'])
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']];
71                     // handle defaults:
72                     //   approve or reject
73                     if ($args['pass'] == 'approve')
74                         return $this->approve($request, $args, $moderation);
75                     elseif ($args['pass'] == 'reject')
76                         return $this->reject($request, $args, $moderation);
77                     else
78                         return $this->error("Wrong pass ".$args['pass']);
79                 } else {
80                     return $this->error("Wrong id ".htmlentities($args['id']));
81                 }
82             }
83         }
84         return '';
85     }
86
87     /**
88      * resolve moderators and require_access (not yet) from actionpage plugin argstr
89      */
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));
97         } else { 
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);
104                 }
105             }
106             if (!$moderators) $moderators = array(ADMIN_USER);
107             $args['moderators'] = $moderators;
108         }
109         //resolve email for $args['moderators']
110         $page = $request->getPage();
111         $users = array();
112         foreach ($args['moderators'] as $userid) {
113             $users[$userid] = 0;
114         }
115         list($args['emails'], $args['moderators']) = 
116             $page->getPageChangeEmails(array($page->getName() => $users));
117
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']);
122         }
123         if ($args['require_level'] !== false) {
124             $args['require_level'] = (integer) $args['require_level'];
125         }
126         unset($args['id']);
127         unset($args['page']);
128         unset($args['pass']);
129         return $args;
130     }
131     
132     /**
133      * Handle client-side moderation change request.
134      * Hook called on the lock action, if moderation metadata already exists.
135      */
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"), 
142                               E_USER_WARNING);
143                 return false;
144             }
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']));
149         } else {
150             $page->set('moderation', false);
151             return $this->notice(HTML($status,
152                         fmt("'%s' is no ModeratedPage anymore.", $page->getName()))); 
153         }
154     }
155
156     /**
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
160      */
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"), 
166                               E_USER_WARNING);
167                 return false;
168             }
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']));
173         }
174         else { // error
175             return $status;
176         }
177     }
178     
179     function notice($msg) {
180         return HTML::div(array('class' => 'wiki-edithelp'), $msg);
181     }
182
183     function generateId() {
184         better_srand();
185         $s = "";
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);
189         }
190         return $s;
191     }
192
193     /** 
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.
197      */
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'];
204         else {
205             $action_page = $request->getPage(_("ModeratedPage"));
206             $status = $this->getSiteStatus($request, $action_page);
207             $moderated['_status'] = $status;
208         }
209         if (!empty($status['emails'])) {
210             trigger_error(_("ModeratedPage: No emails for the moderators defined"),
211                           E_USER_WARNING);
212             return true;
213         }
214         // which action?
215         if (!empty($status['require_access']) 
216             and !in_array(action2access($action), $status['require_access']))
217             return false; // allow and fall through, not moderated
218         if (!empty($status['require_level']) 
219             and $request->_user->_level >= $status['require_level'])
220             return false; // allow and fall through, not moderated
221         // else all post actions are moderated by default
222         if (1) /* or in_array($action, array('edit','remove','rename')*/ {
223             //$moderated = $page->get('moderated');
224             $id = $this->generateId();
225             while (!empty($moderated[$id])) $id = $this->generateId(); // avoid duplicates
226             $moderated['id'] = $id;             // overwrite current id
227             $moderated['data'][$id] = array(    // add current request
228                                             'timestamp' => time(),
229                                             'userid' => $request->_user->getId(),
230                                             'args' => $request->getArgs(),
231                                             'user'   => serialize($request->_user),
232                                             );
233             $this->_tokens['CONTENT'] = 
234                 HTML::div(array('class' => 'wikitext'),
235                           fmt("%s: action forwarded to moderator %s", 
236                               $action, 
237                               join(", ", $status['moderators'])
238                               ));
239             // send email
240             $pagename = $page->getName();
241             $subject = "[".WIKI_NAME.'] '.$action.': '._("ModeratedPage").' '.$pagename;
242             if (mail(join(",", $status['emails']), 
243                      $subject, 
244                      $action.': '._("ModeratedPage").' '.$pagename."\n"
245                      . serialize($moderated['data'][$id])
246                      ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"), 
247                                                      'id' => $id, 'pass' => 'approve'), 1).">"
248                      ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"), 
249                                                      'id' => $id, 'pass' => 'reject'), 1).">\n"
250                      )) {
251                 $page->set('moderated', $moderated);
252                 return false; // pass thru
253             } else {
254                 //FIXME: This msg gets lost on the edit redirect
255                 trigger_error(_("ModeratedPage Notification Error: Couldn't send email"), 
256                               E_USER_WARNING);
257                 return true;
258             }
259         }
260         return false;
261     }
262
263     /** 
264      * Handle admin-side moderation resolve.
265      * We might have to convert the GET to a POST request to continue 
266      * with the left-over stored request.
267      * Better we display a post form for verification.
268      */
269     function approve(&$request, $args, &$moderation) {
270         // check id, convert to POST, continue
271         if ($request->isPost()) {
272             $this->error("ModeratedPage::approve not yet implemented");
273         } else {
274             return $this->_approval_form($request, $args, $moderation, 'approve');
275         }
276     }
277     /** 
278      * Handle admin-side moderation resolve.
279      */
280     function reject(&$request, $args, &$moderation) {
281         // check id, delete action
282         if ($request->isPost()) {
283             $this->error("ModeratedPage::reject not yet implemented");
284         } else {
285             return $this->_approval_form($request, $args, $moderation, 'reject');
286         }
287     }
288
289     function _approval_form(&$request, $args, $moderation, $pass='approve') {
290         $header = HTML::h3(_("Please approve or reject this request:"));
291         $loader = new WikiPluginLoader();
292         $BackendInfo = $loader->getPlugin("_BackendInfo");
293         $content = HTML::table(array('border' => 1,
294                                      'cellpadding' => 2,
295                                      'cellspacing' => 0));
296         $myargs  = $args;
297         $BackendInfo->_fixupData($myargs);
298         $content->pushContent($BackendInfo->_showhash("request args", $myargs));
299         $BackendInfo->_fixupData($moderation);
300         $content->pushContent($BackendInfo->_showhash("moderation data", $moderation));
301         $approve = Button('submit:ModeratedPage[approve]', _("Approve"), 
302                           $pass == 'approve' ? 'wikiadmin' : 'button');
303         $reject  = Button('submit:ModeratedPage[reject]', _("Reject"),
304                           $pass == 'reject' ? 'wikiadmin' : 'button');
305         return HTML::form(array('action' => $request->getPostURL(),
306                                 'method' => 'post'),
307                           $header,
308                           $content,
309                           ENABLE_PAGEPERM 
310                             ? ''
311                             : HiddenInputs(array('require_authority_for_post' => WIKIAUTH_ADMIN)),
312                           HiddenInputs($args),
313                           $pass == 'approve' ? HTML::p($approve, $reject) 
314                                              : HTML::p($reject, $approve));
315     }
316     
317     /**
318      * Get the side-wide ModeratedPage status, reading the action-page args.
319      * Who are the moderators? What actions should be moderated?
320      */
321     function getSiteStatus(&$request, &$action_page) {
322         $loader = new WikiPluginLoader();
323         $rev = $action_page->getCurrentRevision();
324         $content = $rev->getPackedContent();
325         list($pi) = explode("\n", $content, 2); // plugin ModeratedPage must be first line!
326         if ($parsed = $loader->parsePI($pi)) {
327             $plugin =& $parsed[1];
328             if ($plugin->getName() != _("ModeratedPage"))
329                 return $this->error(sprintf(_("<?plugin ModeratedPage ... ?> not found in first line of %s"),
330                                             $action_page->getName()));
331             if (!$action_page->get('locked'))
332                 return $this->error(sprintf(_("%s is not locked!"),
333                                             $action_page->getName()));
334             return $plugin->resolve_argstr($request, $parsed[2]);
335         } else {
336             return $this->error(sprintf(_("<?plugin ModeratedPage ... ?> not found in first line of %s"),
337                                         $action_page->getName()));
338         }
339     }
340     
341 };
342
343 // $Log: not supported by cvs2svn $
344 // Revision 1.4  2005/01/29 19:52:09  rurban
345 // more work on the last part
346 //
347 // Revision 1.3  2004/12/06 19:50:05  rurban
348 // enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
349 // renamed delete_page to purge_page.
350 // enable action=edit&version=-1 to force creation of a new version.
351 // added BABYCART_PATH config
352 // fixed magiqc in adodb.inc.php
353 // and some more docs
354 //
355 // Revision 1.2  2004/11/30 17:46:49  rurban
356 // added ModeratedPage POST action hook (part 2/3)
357 //
358 // Revision 1.1  2004/11/19 19:22:35  rurban
359 // ModeratePage part1: change status
360 //
361
362 // For emacs users
363 // Local Variables:
364 // mode: php
365 // tab-width: 8
366 // c-basic-offset: 4
367 // c-hanging-comment-ender-p: nil
368 // indent-tabs-mode: nil
369 // End:
370 ?>