public static function AjaxReview()
 {
     global $wgUser, $wgOut, $wgRequest;
     $args = func_get_args();
     if (wfReadOnly()) {
         return '<err#>' . wfMsgExt('revreview-failed', 'parseinline') . wfMsgExt('revreview-submission-invalid', 'parseinline');
     }
     $tags = FlaggedRevs::getTags();
     // Make review interface object
     $form = new RevisionReviewForm($wgUser);
     $title = null;
     // target page
     $editToken = '';
     // edit token
     // Each ajax url argument is of the form param|val.
     // This means that there is no ugly order dependance.
     foreach ($args as $arg) {
         $set = explode('|', $arg, 2);
         if (count($set) != 2) {
             return '<err#>' . wfMsgExt('revreview-failed', 'parseinline') . wfMsgExt('revreview-submission-invalid', 'parseinline');
         }
         list($par, $val) = $set;
         switch ($par) {
             case "target":
                 $title = Title::newFromURL($val);
                 break;
             case "oldid":
                 $form->setOldId($val);
                 break;
             case "refid":
                 $form->setRefId($val);
                 break;
             case "validatedParams":
                 $form->setValidatedParams($val);
                 break;
             case "templateParams":
                 $form->setTemplateParams($val);
                 break;
             case "imageParams":
                 $form->setFileParams($val);
                 break;
             case "fileVersion":
                 $form->setFileVersion($val);
                 break;
             case "wpApprove":
                 $form->setApprove($val);
                 break;
             case "wpUnapprove":
                 $form->setUnapprove($val);
                 break;
             case "wpReject":
                 $form->setReject($val);
                 break;
             case "wpReason":
                 $form->setComment($val);
                 break;
             case "changetime":
                 $form->setLastChangeTime($val);
                 break;
             case "wpEditToken":
                 $editToken = $val;
                 break;
             default:
                 $p = preg_replace('/^wp/', '', $par);
                 // kill any "wp" prefix
                 if (in_array($p, $tags)) {
                     $form->setDim($p, $val);
                 }
                 break;
         }
     }
     # Valid target title?
     if (!$title) {
         return '<err#>' . wfMsgExt('notargettext', 'parseinline');
     }
     $form->setPage($title);
     $form->setSessionKey($wgRequest->getSessionData('wsFlaggedRevsKey'));
     $status = $form->ready();
     // all params loaded
     # Check session via user token
     if (!$wgUser->matchEditToken($editToken)) {
         return '<err#>' . wfMsgExt('sessionfailure', 'parseinline');
     }
     # Basic permission checks...
     $permErrors = $title->getUserPermissionsErrors('review', $wgUser, false);
     if (!$permErrors) {
         $permErrors = $title->getUserPermissionsErrors('edit', $wgUser, false);
     }
     if ($permErrors) {
         return '<err#>' . $wgOut->parse($wgOut->formatPermissionsErrorMessage($permErrors, 'review'));
     }
     # Try submission...
     $status = $form->submit();
     # Success...
     if ($status === true) {
         # Sent new lastChangeTime TS to client for later submissions...
         $changeTime = $form->getNewLastChangeTime();
         if ($form->getAction() === 'approve') {
             // approve
             return "<suc#><lct#{$changeTime}>";
         } elseif ($form->getAction() === 'unapprove') {
             // de-approve
             return "<suc#><lct#{$changeTime}>";
         } elseif ($form->getAction() === 'reject') {
             // revert
             return "<suc#><lct#{$changeTime}>";
         }
         # Failure...
     } else {
         return '<err#>' . wfMsgExt('revreview-failed', 'parse') . '<p>' . wfMsgHtml($status) . '</p>';
     }
 }