'[pagename]', 'moderators' => false, 'require_level' => false, // 1=bogo 'require_access' => 'edit,remove,change', 'id' => '', 'pass' => '', ); } function run($dbi, $argstr, &$request, $basepage) { $args = $this->getArgs($argstr, $request); // Handle moderation request from urls sent by email if (!empty($args['id']) and !empty($args['pass'])) { if (!$args['page']) return $this->error("No page specified"); $page = $dbi->getPage($args['page']); if ($moderated = $page->get("moderated")) { if (array_key_exists($args['id'], $moderated['data'])) { $moderation = $moderated['data'][$args['id']]; // handle defaults: // approve or reject if ($request->isPost()) { $button = $request->getArg('ModeratedPage'); if (isset($button['reject'])) return $this->reject($request, $args, $moderation); elseif (isset($button['approve'])) return $this->approve($request, $args, $moderation); else return $this->error("Wrong button pressed"); } if ($args['pass'] == 'approve') return $this->approve($request, $args, $moderation); elseif ($args['pass'] == 'reject') return $this->reject($request, $args, $moderation); else return $this->error("Wrong pass ".$args['pass']); } else { return $this->error("Wrong id ".htmlentities($args['id'])); } } } return ''; } /** * resolve moderators and require_access (not yet) from actionpage plugin argstr */ function resolve_argstr(&$request, $argstr) { $args = $this->getArgs($argstr); $group = $request->getGroup(); if (empty($args['moderators'])) { $admins = $group->getSpecialMembersOf(GROUP_ADMIN); // email or usernames? $args['moderators'] = array_merge($admins, array(ADMIN_USER)); } else { // resolve possible group names $moderators = explode(',', $args['moderators']); for ($i=0; $i < count($moderators); $i++) { $members = $group->getMembersOf($moderators[$i]); if (!empty($members)) { array_splice($moderators, $i, 1, $members); } } if (!$moderators) $moderators = array(ADMIN_USER); $args['moderators'] = $moderators; } //resolve email for $args['moderators'] $page = $request->getPage(); $users = array(); foreach ($args['moderators'] as $userid) { $users[$userid] = 0; } require_once("lib/MailNotify.php"); $mail = new MailNotify($page->getName()); list($args['emails'], $args['moderators']) = $mail->getPageChangeEmails(array($page->getName() => $users)); if (!empty($args['require_access'])) { $args['require_access'] = preg_split("/\s*,\s*/", $args['require_access']); if (empty($args['require_access'])) unset($args['require_access']); } if ($args['require_level'] !== false) { $args['require_level'] = (integer) $args['require_level']; } unset($args['id']); unset($args['page']); unset($args['pass']); return $args; } /** * Handle client-side moderation change request. * Hook called on the lock action, if moderation metadata already exists. */ function lock_check(&$request, &$page, $moderated) { $action_page = $request->getPage(_("ModeratedPage")); $status = $this->getSiteStatus($request, $action_page); if (is_array($status)) { if (empty($status['emails'])) { trigger_error(_("ModeratedPage: No emails for the moderators defined"), E_USER_WARNING); return false; } $page->set('moderation', array('status' => $status)); return $this->notice( fmt("ModeratedPage status update:\n Moderators: '%s'\n require_access: '%s'", join(',', $status['moderators']), $status['require_access'])); } else { $page->set('moderation', false); return $this->notice(HTML($status, fmt("'%s' is no ModeratedPage anymore.", $page->getName()))); } } /** * Handle client-side moderation change request by the user. * Hook called on the lock action, if moderation metadata should be added. * Need to store the the plugin args (who, when) in the page meta-data */ function lock_add(&$request, &$page, &$action_page) { $status = $this->getSiteStatus($request, $action_page); if (is_array($status)) { if (empty($status['emails'])) { // We really should present such warnings prominently. trigger_error(_("ModeratedPage: No emails for the moderators defined"), E_USER_WARNING); return false; } $page->set('moderation', array('status' => $status)); return $this->notice( fmt("ModeratedPage status update: '%s' is now a ModeratedPage.\n Moderators: '%s'\n require_access: '%s'", $page->getName(), join(',', $status['moderators']), $status['require_access'])); } else { // error return $status; } } function notice($msg) { return HTML::div(array('class' => 'wiki-edithelp'), $msg); } function generateId() { better_srand(); $s = ""; for ($i = 1; $i <= 25; $i++) { $r = function_exists('mt_rand') ? mt_rand(55, 90) : rand(55, 90); $s .= chr(($r < 65) ? ($r-17) : $r); } $len = $r = function_exists('mt_rand') ? mt_rand(15, 25) : rand(15,25); return substr(base64_encode($s),3,$len); } /** * Handle client-side POST moderation request on any moderated page. * if ($page->get('moderation')) WikiPlugin_ModeratedPage::handler(...); * return false if not handled (pass through), true if handled and displayed. */ function handler(&$request, &$page) { $action = $request->getArg('action'); $moderated = $page->get('moderated'); // cached version, need re-lock of each page to update moderators if (!empty($moderated['status'])) $status = $moderated['status']; else { $action_page = $request->getPage(_("ModeratedPage")); $status = $this->getSiteStatus($request, $action_page); $moderated['status'] = $status; } if (empty($status['emails'])) { trigger_error(_("ModeratedPage: No emails for the moderators defined"), E_USER_WARNING); return true; } // which action? if (!empty($status['require_access']) and !in_array(action2access($action), $status['require_access'])) return false; // allow and fall through, not moderated if (!empty($status['require_level']) and $request->_user->_level >= $status['require_level']) return false; // allow and fall through, not moderated // else all post actions are moderated by default if (1) /* or in_array($action, array('edit','remove','rename')*/ { $id = $this->generateId(); while (!empty($moderated['data'][$id])) $id = $this->generateId(); // avoid duplicates $moderated['id'] = $id; // overwrite current id $tempuser = $request->_user; unset($tempuser->_HomePagehandle); $moderated['data'][$id] = array( // add current request 'timestamp' => time(), 'userid' => $request->_user->getId(), 'args' => $request->getArgs(), 'user' => serialize( $tempuser ), ); $this->_tokens['CONTENT'] = HTML::div(array('class' => 'wikitext'), fmt("%s: action forwarded to moderator %s", $action, join(", ", $status['moderators']) )); // Send email require_once("lib/MailNotify.php"); $pagename = $page->getName(); $mailer = new MailNotify($pagename); $subject = "[".WIKI_NAME.'] '.$action.': '._("ModeratedPage").' '.$pagename; $content = "You are approved as Moderator of the ".WIKI_NAME. " wiki.\n". "Someone wanted to edit a moderated page, which you have to approve or reject.\n\n". $action.': '._("ModeratedPage").' '.$pagename."\n" //. serialize($moderated['data'][$id]) ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"), 'id' => $id, 'pass' => 'approve'), 1).">" ."\n<".WikiURL($pagename, array('action' => _("ModeratedPage"), 'id' => $id, 'pass' => 'reject'), 1).">\n"; $mailer->emails = $mailer->userids = $status['emails']; $mailer->from = $request->_user->_userid; if ($mailer->sendMail($subject, $content, "Moderation notice")) { $page->set('moderated', $moderated); return false; // pass thru } else { //DELETEME! $page->set('moderated', $moderated); //FIXME: This msg gets lost on the edit redirect trigger_error(_("ModeratedPage Notification Error: Couldn't send email"), E_USER_ERROR); return true; } } return false; } /** * Handle admin-side moderation resolve. * We might have to convert the GET to a POST request to continue * with the left-over stored request. * Better we display a post form for verification. */ function approve(&$request, $args, &$moderation) { if ($request->isPost()) { // this is unsafe because we dont know if it will succeed. but we tried. $this->cleanup_and_notify($request, $args, $moderation); // start from scratch, dispatch the action as in lib/main to the action handler $request->discardOutput(); $oldargs = $request->args; $olduser = $request->_user; $request->args = $moderation['args']; $request->_user->_userid = $moderation['userid']; // keep current perms but fake the id. // TODO: fake author ip also extract($request->args); $method = "action_$action"; if (method_exists($request, $method)) { $request->{$method}(); } elseif ($page = $this->findActionPage($action)) { $this->actionpage($page); } else { $this->finish(fmt("%s: Bad action", $action)); } // now we are gone and nobody brings us back here. //$moderated['data'][$id]->args->action+edit(array)+... // timestamp,user(obj)+userid // handle $moderated['data'][$id]['args']['action'] } else { return $this->_approval_form($request, $args, $moderation, 'approve'); } } /** * Handle admin-side moderation resolve. */ function reject(&$request, $args, &$moderation) { // check id, delete action if ($request->isPost()) { // clean up and notify the requestor. Mabye: store and revert to have a diff later on? $this->cleanup_and_notify($request, $args, $moderation); } else { return $this->_approval_form($request, $args, $moderation, 'reject'); } } function cleanup_and_notify (&$request, $args, &$moderation) { $pagename = $moderation['args']['pagename']; $page = $request->_dbi->getPage($pagename); $pass = $args['pass']; // accept or reject $reason = $args['reason']; // summary why $user = $moderation['args']['user']; $action = $moderation['args']['action']; $id = $args['id']; unset($moderation['data'][$id]); unset($moderation['id']); $page->set('moderation', $moderation); // TODO: Notify the user, only if the user has an email: if ($email = $user->getPref('email')) { $action_page = $request->getPage(_("ModeratedPage")); $status = $this->getSiteStatus($request, $action_page); require_once("lib/MailNotify.php"); $mailer = new MailNotify($pagename); $subject = "[".WIKI_NAME."] $pass $action "._("ModeratedPage").': '.$pagename; $mailer->from = $request->_user->UserFrom(); $content = sprintf(_("%s approved your wiki action from %s"), $mailer->from,CTime($moderation['timestamp'])) ."\n\n" ."Decision: ".$pass ."Reason: ".$reason ."\n<".WikiURL($pagename).">\n"; $mailer->emails = $mailer->userids = $email; $mailer->sendMail($subject, $content, "Approval notice"); } } function _approval_form(&$request, $args, $moderation, $pass='approve') { $header = HTML::h3(_("Please approve or reject this request:")); $loader = new WikiPluginLoader(); $BackendInfo = $loader->getPlugin("_BackendInfo"); $table = HTML::table(array('border' => 1, 'cellpadding' => 2, 'cellspacing' => 0)); $content = $table; $diff = ''; if ($moderation['args']['action'] == 'edit') { $pagename = $moderation['args']['pagename']; $p = $request->_dbi->getPage($pagename); $rev = $p->getCurrentRevision(true); $curr_content = $rev->getPackedContent(); $new_content = $moderation['args']['edit']['content']; include_once("lib/difflib.php"); $diff2 = new Diff($curr_content, $new_content); $fmt = new UnifiedDiffFormatter(/*$context_lines*/); $diff = $pagename . " Current Version " . Iso8601DateTime($p->get('mtime')) . "\n"; $diff .= $pagename . " Edited Version " . Iso8601DateTime($moderation['timestamp']) . "\n"; $diff .= $fmt->format($diff2); } $content->pushContent($BackendInfo->_showhash("Request", array('User' => $moderation['userid'], 'When' => CTime($moderation['timestamp']), 'Pagename' => $pagename, 'Action' => $moderation['args']['action'], 'Diff' => HTML::pre($diff)))); $content_dbg = $table; $myargs = $args; $BackendInfo->_fixupData($myargs); $content_dbg->pushContent($BackendInfo->_showhash("raw request args", $myargs)); $BackendInfo->_fixupData($moderation); $content_dbg->pushContent($BackendInfo->_showhash("raw moderation data", $moderation)); $reason = HTML::div(_("Reason: "), HTML::textarea(array('name' => 'reason'))); $approve = Button('submit:ModeratedPage[approve]', _("Approve"), $pass == 'approve' ? 'wikiadmin' : 'button'); $reject = Button('submit:ModeratedPage[reject]', _("Reject"), $pass == 'reject' ? 'wikiadmin' : 'button'); $args['action'] = _("ModeratedPage"); return HTML::form(array('action' => $request->getPostURL(), 'method' => 'post'), $header, $content, HTML::p(""), $content_dbg, $reason, ENABLE_PAGEPERM ? '' : HiddenInputs(array('require_authority_for_post' => WIKIAUTH_ADMIN)), HiddenInputs($args), $pass == 'approve' ? HTML::p($approve, $reject) : HTML::p($reject, $approve)); } /** * Get the side-wide ModeratedPage status, reading the action-page args. * Who are the moderators? What actions should be moderated? */ function getSiteStatus(&$request, &$action_page) { $loader = new WikiPluginLoader(); $rev = $action_page->getCurrentRevision(); $content = $rev->getPackedContent(); list($pi) = explode("\n", $content, 2); // plugin ModeratedPage must be first line! if ($parsed = $loader->parsePI($pi)) { $plugin =& $parsed[1]; if ($plugin->getName() != _("ModeratedPage")) return $this->error(sprintf(_(" not found in first line of %s"), $action_page->getName())); if (!$action_page->get('locked')) return $this->error(sprintf(_("%s is not locked!"), $action_page->getName())); return $plugin->resolve_argstr($request, $parsed[2]); } else { return $this->error(sprintf(_(" not found in first line of %s"), $action_page->getName())); } } }; // $Log: not supported by cvs2svn $ // Revision 1.5 2006/08/15 13:41:08 rurban // just aesthetics // // Revision 1.4 2005/01/29 19:52:09 rurban // more work on the last part // // Revision 1.3 2004/12/06 19:50:05 rurban // enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now. // renamed delete_page to purge_page. // enable action=edit&version=-1 to force creation of a new version. // added BABYCART_PATH config // fixed magiqc in adodb.inc.php // and some more docs // // Revision 1.2 2004/11/30 17:46:49 rurban // added ModeratedPage POST action hook (part 2/3) // // Revision 1.1 2004/11/19 19:22:35 rurban // ModeratePage part1: change status // // For emacs users // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: ?>