/**
  * Builds the full form for edit filters.
  * Loads data either from the database or from the HTTP request.
  * The request takes precedence over the database
  * @param $error string An error message to show above the filter box.
  * @param $filter int The filter ID
  * @param $history_id int The history ID of the filter, if applicable. Otherwise null
  * @return bool|string False if there is a failure building the editor, otherwise the HTML text for the editor.
  */
 function buildFilterEditor($error, $filter, $history_id = null)
 {
     if ($filter === null) {
         return false;
     }
     // Build the edit form
     $out = $this->getOutput();
     $lang = $this->getLanguage();
     $user = $this->getUser();
     // Load from request OR database.
     list($row, $actions) = $this->loadRequest($filter, $history_id);
     if (!$row) {
         $out->addWikiMsg('abusefilter-edit-badfilter');
         $out->addHTML(Linker::link($this->getTitle(), $this->msg('abusefilter-return')->text()));
         return false;
     }
     $out->addSubtitle($this->msg($filter === 'new' ? 'abusefilter-edit-subtitle-new' : 'abusefilter-edit-subtitle', $this->getLanguage()->formatNum($filter), $history_id)->text());
     // Hide hidden filters.
     if ((isset($row->af_hidden) && $row->af_hidden || AbuseFilter::filterHidden($filter)) && !$this->canViewPrivate()) {
         return $this->msg('abusefilter-edit-denied')->text();
     }
     $output = '';
     if ($error) {
         $out->addHTML("<span class=\"error\">{$error}</span>");
     }
     // Read-only attribute
     $readOnlyAttrib = array();
     $cbReadOnlyAttrib = array();
     // For checkboxes
     if (!$this->canEditFilter($row)) {
         $readOnlyAttrib['readonly'] = 'readonly';
         $cbReadOnlyAttrib['disabled'] = 'disabled';
     }
     $fields = array();
     $fields['abusefilter-edit-id'] = $this->mFilter == 'new' ? $this->msg('abusefilter-edit-new')->text() : $lang->formatNum($filter);
     $fields['abusefilter-edit-description'] = Xml::input('wpFilterDescription', 45, isset($row->af_public_comments) ? $row->af_public_comments : '', $readOnlyAttrib);
     global $wgAbuseFilterValidGroups;
     if (count($wgAbuseFilterValidGroups) > 1) {
         $groupSelector = new XmlSelect('wpFilterGroup', 'mw-abusefilter-edit-group-input', 'default');
         if (isset($row->af_group) && $row->af_group) {
             $groupSelector->setDefault($row->af_group);
         }
         foreach ($wgAbuseFilterValidGroups as $group) {
             $groupSelector->addOption(AbuseFilter::nameGroup($group), $group);
         }
         $fields['abusefilter-edit-group'] = $groupSelector->getHTML();
     }
     // Hit count display
     if (!empty($row->af_hit_count)) {
         $count_display = $this->msg('abusefilter-hitcount')->numParams((int) $row->af_hit_count)->escaped();
         $hitCount = Linker::linkKnown(SpecialPage::getTitleFor('AbuseLog'), $count_display, array(), array('wpSearchFilter' => $row->af_id));
         $fields['abusefilter-edit-hitcount'] = $hitCount;
     }
     if ($filter !== 'new') {
         // Statistics
         global $wgMemc;
         $matches_count = $wgMemc->get(AbuseFilter::filterMatchesKey($filter));
         $total = $wgMemc->get(AbuseFilter::filterUsedKey($row->af_group));
         if ($total > 0) {
             $matches_percent = sprintf('%.2f', 100 * $matches_count / $total);
             list($timeProfile, $condProfile) = AbuseFilter::getFilterProfile($filter);
             $fields['abusefilter-edit-status-label'] = $this->msg('abusefilter-edit-status')->numParams($total, $matches_count, $matches_percent, $timeProfile, $condProfile)->escaped();
         }
     }
     $fields['abusefilter-edit-rules'] = AbuseFilter::buildEditBox($row->af_pattern, 'wpFilterRules', true, $this->canEditFilter($row));
     $fields['abusefilter-edit-notes'] = Xml::textarea('wpFilterNotes', isset($row->af_comments) ? $row->af_comments . "\n" : "\n", 40, 15, $readOnlyAttrib);
     // Build checkboxen
     $checkboxes = array('hidden', 'enabled', 'deleted');
     $flags = '';
     global $wgAbuseFilterIsCentral;
     if ($wgAbuseFilterIsCentral) {
         $checkboxes[] = 'global';
     }
     if (isset($row->af_throttled) && $row->af_throttled) {
         global $wgAbuseFilterEmergencyDisableThreshold;
         // determine emergency disable value for this action
         $emergencyDisableThreshold = AbuseFilter::getEmergencyValue($wgAbuseFilterEmergencyDisableThreshold, $row->af_group);
         $threshold_percent = sprintf('%.2f', $emergencyDisableThreshold * 100);
         $flags .= $out->parse($this->msg('abusefilter-edit-throttled')->numParams($threshold_percent)->text());
     }
     foreach ($checkboxes as $checkboxId) {
         // Messages that can be used here:
         // * abusefilter-edit-enabled
         // * abusefilter-edit-deleted
         // * abusefilter-edit-hidden
         // * abusefilter-edit-global
         $message = "abusefilter-edit-{$checkboxId}";
         $dbField = "af_{$checkboxId}";
         $postVar = 'wpFilter' . ucfirst($checkboxId);
         if ($checkboxId == 'global' && !$this->canEditGlobal()) {
             $cbReadOnlyAttrib['disabled'] = 'disabled';
         }
         $checkbox = Xml::checkLabel($this->msg($message)->text(), $postVar, $postVar, isset($row->{$dbField}) ? $row->{$dbField} : false, $cbReadOnlyAttrib);
         $checkbox = Xml::tags('p', null, $checkbox);
         $flags .= $checkbox;
     }
     $fields['abusefilter-edit-flags'] = $flags;
     $tools = '';
     if ($filter != 'new' && $user->isAllowed('abusefilter-revert')) {
         $tools .= Xml::tags('p', null, Linker::link($this->getTitle('revert/' . $filter), $this->msg('abusefilter-edit-revert')->text()));
     }
     if ($filter != 'new') {
         // Test link
         $tools .= Xml::tags('p', null, Linker::link($this->getTitle("test/{$filter}"), $this->msg('abusefilter-edit-test-link')->parse()));
         // Last modification details
         $userLink = Linker::userLink($row->af_user, $row->af_user_text) . Linker::userToolLinks($row->af_user, $row->af_user_text);
         $userName = $row->af_user_text;
         $fields['abusefilter-edit-lastmod'] = $this->msg('abusefilter-edit-lastmod-text')->rawParams($lang->timeanddate($row->af_timestamp, true), $userLink, $lang->date($row->af_timestamp, true), $lang->time($row->af_timestamp, true), $userName)->parse();
         $history_display = $this->msg('abusefilter-edit-viewhistory')->parse();
         $fields['abusefilter-edit-history'] = Linker::linkKnown($this->getTitle('history/' . $filter), $history_display);
     }
     // Add export
     $exportText = FormatJson::encode(array('row' => $row, 'actions' => $actions));
     $tools .= Xml::tags('a', array('href' => '#', 'id' => 'mw-abusefilter-export-link'), $this->msg('abusefilter-edit-export')->parse());
     $tools .= Xml::element('textarea', array('readonly' => 'readonly', 'id' => 'mw-abusefilter-export'), $exportText);
     $fields['abusefilter-edit-tools'] = $tools;
     $form = Xml::buildForm($fields);
     $form = Xml::fieldset($this->msg('abusefilter-edit-main')->text(), $form);
     $form .= Xml::fieldset($this->msg('abusefilter-edit-consequences')->text(), $this->buildConsequenceEditor($row, $actions));
     if ($this->canEditFilter($row)) {
         $form .= Xml::submitButton($this->msg('abusefilter-edit-save')->text(), array('accesskey' => 's'));
         $form .= Html::hidden('wpEditToken', $user->getEditToken(array('abusefilter', $filter)));
     }
     $form = Xml::tags('form', array('action' => $this->getTitle($filter)->getFullURL(), 'method' => 'post'), $form);
     $output .= $form;
     return $output;
 }