Special:Log: Convert to HTMLForm
authorPrateek Saxena <prtksxna@gmail.com>
Wed, 25 Apr 2018 05:22:19 +0000 (10:52 +0530)
committerPrateek Saxena <prtksxna@gmail.com>
Tue, 10 Jul 2018 11:25:33 +0000 (16:55 +0530)
* All Xml generation code has been removed.

* The LogEventsListGetExtraInputs hook now needs a form
  descriptor array and not plain HTML.
  See I37e0d3e81a46239750465b9299279fbbd7c7f87a.

* LogPager and LogEventList also take the $day parameter
  for 'From date (and earlier)' and pass it to getDateCond
  as well.

* Since FormOptions can't automatically extract the date
  from the request this is being done manually in the
  execute method of Special:Log using MWTimestamp.

Bug: T117737
Change-Id: Iba3c6aa5ac40dcdee0792c2d045b238b02d76521

RELEASE-NOTES-1.32
docs/hooks.txt
includes/logging/LogEventsList.php
includes/logging/LogPager.php
includes/specials/SpecialLog.php
languages/i18n/en.json
languages/i18n/qqq.json

index c41e5b5..00f10a9 100644 (file)
@@ -179,6 +179,8 @@ because of Phabricator reports.
   CapsuleMultiselectWidget. The following methods may no longer be used:
   * setItemsFromData: Use setValue instead
   * getItemsData: Use getItems instead and get the data property
+* The hook 'LogEventsListGetExtraInputs' now needs a form descriptor array
+  and not plain HTML.
 
 === Deprecations in 1.32 ===
 * Use of a StartProfiler.php file is deprecated in favour of placing
index 708456c..ff46503 100644 (file)
@@ -2182,7 +2182,7 @@ $autocreated: Boolean, whether this was an auto-creation
 Special:Log for a specific log type
 $type: String of log type being displayed
 $logEventsList: LogEventsList object for context and access to the WebRequest
-&$input: string HTML of an input element
+&$formDescriptor: array HTMLForm's form descriptor
 
 'LogEventsListShowLogExtract': Called before the string is added to OutputPage.
 Returning false will prevent the string from being added to the OutputPage.
index 17d15d6..fdcaa1b 100644 (file)
@@ -101,99 +101,94 @@ class LogEventsList extends ContextSource {
         * @param int|string $year Use 0 to start with no year preselected.
         * @param int|string $month A month in the 1..12 range. Use 0 to start with no month
         *  preselected.
+        * @param int|string $day A day in the 1..31 range. Use 0 to start with no month
+        *  preselected.
         * @param array|null $filter
         * @param string $tagFilter Tag to select by default
         * @param string|null $action
         */
        public function showOptions( $types = [], $user = '', $page = '', $pattern = false, $year = 0,
-               $month = 0, $filter = null, $tagFilter = '', $action = null
+               $month = 0, $day = 0, $filter = null, $tagFilter = '', $action = null
        ) {
-               global $wgScript, $wgMiserMode;
-
                $title = SpecialPage::getTitleFor( 'Log' );
 
                // For B/C, we take strings, but make sure they are converted...
                $types = ( $types === '' ) ? [] : (array)$types;
 
-               $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter, false, $this->getContext() );
-
-               $html = Html::hidden( 'title', $title->getPrefixedDBkey() );
+               $formDescriptor = [];
 
                // Basic selectors
-               $html .= $this->getTypeMenu( $types ) . "\n";
-               $html .= $this->getUserInput( $user ) . "\n";
-               $html .= $this->getTitleInput( $page ) . "\n";
-               $html .= $this->getExtraInputs( $types ) . "\n";
+               $formDescriptor['type'] = $this->getTypeMenuDesc( $types );
+               $formDescriptor['user'] = $this->getUserInputDesc( $user );
+               $formDescriptor['page'] = $this->getTitleInputDesc( $title );
+
+               // Add extra inputs if any
+               $extraInputsDescriptor = $this->getExtraInputsDesc( $types );
+               if ( !empty( $extraInputsDescriptor ) ) {
+                       $formDescriptor[ 'extra' ] = $extraInputsDescriptor;
+               }
 
                // Title pattern, if allowed
-               if ( !$wgMiserMode ) {
-                       $html .= $this->getTitlePattern( $pattern ) . "\n";
+               if ( !$this->getConfig()->get( 'MiserMode' ) ) {
+                       $formDescriptor['pattern'] = $this->getTitlePatternDesc( $pattern );
                }
 
-               // date menu
-               $html .= Xml::tags( 'p', null, Xml::dateMenu( (int)$year, (int)$month ) );
+               // Date menu
+               $formDescriptor['date'] = [
+                       'type' => 'date',
+                       'label-message' => 'date'
+               ];
 
                // Tag filter
-               if ( $tagSelector ) {
-                       $html .= Xml::tags( 'p', null, implode( "\u{00A0}", $tagSelector ) );
-               }
+               $formDescriptor['tagfilter'] = [
+                       'type' => 'tagfilter',
+                       'name' => 'tagfilter',
+                       'label-raw' => $this->msg( 'tag-filter' )->parse(),
+               ];
 
                // Filter links
                if ( $filter ) {
-                       $html .= Xml::tags( 'p', null, $this->getFilterLinks( $filter ) );
+                       $formDescriptor['filters'] = $this->getFiltersDesc( $filter );
                }
 
                // Action filter
-               if ( $action !== null ) {
-                       $html .= Xml::tags( 'p', null, $this->getActionSelector( $types, $action ) );
+               if (
+                       $action !== null &&
+                       $this->allowedActions !== null &&
+                       count( $this->allowedActions ) > 0
+               ) {
+                       $formDescriptor['subtype'] = $this->getActionSelectorDesc( $types, $action );
                }
 
-               // Submit button
-               $html .= Xml::submitButton( $this->msg( 'logeventslist-submit' )->text() );
-
-               // Fieldset
-               $html = Xml::fieldset( $this->msg( 'log' )->text(), $html );
-
-               // Form wrapping
-               $html = Xml::tags( 'form', [ 'action' => $wgScript, 'method' => 'get' ], $html );
+               $htmlForm = new HTMLForm( $formDescriptor, $this->getContext() );
+               $htmlForm
+                       ->setSubmitText( $this->msg( 'logeventslist-submit' )->text() )
+                       ->setWrapperLegendMsg( 'log' );
 
-               $this->getOutput()->addHTML( $html );
+               $htmlForm->prepareForm()->displayForm( false );
        }
 
        /**
         * @param array $filter
-        * @return string Formatted HTML
+        * @return array Form descriptor
         */
-       private function getFilterLinks( $filter ) {
-               // show/hide links
-               $messages = [ $this->msg( 'show' )->text(), $this->msg( 'hide' )->text() ];
-               // Option value -> message mapping
-               $links = [];
-               $hiddens = ''; // keep track for "go" button
-               $linkRenderer = $this->getLinkRenderer();
+       private function getFiltersDesc( $filter ) {
+               $options = [];
+               $default = [];
                foreach ( $filter as $type => $val ) {
-                       // Should the below assignment be outside the foreach?
-                       // Then it would have to be copied. Not certain what is more expensive.
-                       $query = $this->getDefaultQuery();
-                       $queryKey = "hide_{$type}_log";
+                       $options[ $this->msg( "logeventslist-{$type}-log" )->text() ] = $type;
 
-                       $hideVal = $val ? 0 : 1;
-                       $query[$queryKey] = $hideVal;
-
-                       $link = $linkRenderer->makeKnownLink(
-                               $this->getTitle(),
-                               $messages[$hideVal],
-                               [],
-                               $query
-                       );
-
-                       // Message: log-show-hide-patrol
-                       $links[$type] = $this->msg( "log-show-hide-{$type}" )->rawParams( $link )->escaped();
-                       $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n";
+                       if ( $val === 0 ) {
+                               $default[] = $type;
+                       }
                }
-
-               // Build links
-               return '<small>' . $this->getLanguage()->pipeList( $links ) . '</small>' . $hiddens;
+               return [
+                       'class' => 'HTMLMultiSelectField',
+                       'label-message' => 'logeventslist-more-filters',
+                       'flatlist' => true,
+                       'options' => $options,
+                       'default' => $default,
+               ];
        }
 
        private function getDefaultQuery() {
@@ -213,22 +208,11 @@ class LogEventsList extends ContextSource {
 
        /**
         * @param array $queryTypes
-        * @return string Formatted HTML
+        * @return array Form descriptor
         */
-       private function getTypeMenu( $queryTypes ) {
+       private function getTypeMenuDesc( $queryTypes ) {
                $queryType = count( $queryTypes ) == 1 ? $queryTypes[0] : '';
-               $selector = $this->getTypeSelector();
-               $selector->setDefault( $queryType );
 
-               return $selector->getHTML();
-       }
-
-       /**
-        * Returns log page selector.
-        * @return XmlSelect
-        * @since 1.19
-        */
-       public function getTypeSelector() {
                $typesByName = []; // Temporary array
                // First pass to load the log names
                foreach ( LogPage::validTypes() as $type ) {
@@ -247,62 +231,57 @@ class LogEventsList extends ContextSource {
                unset( $typesByName[''] );
                $typesByName = [ '' => $public ] + $typesByName;
 
-               $select = new XmlSelect( 'type' );
-               foreach ( $typesByName as $type => $name ) {
-                       $select->addOption( $name, $type );
-               }
-
-               return $select;
+               return [
+                       'class' => 'HTMLSelectField',
+                       'name' => 'type',
+                       'options' => array_flip( $typesByName ),
+                       'default' => $queryType,
+               ];
        }
 
        /**
         * @param string $user
-        * @return string Formatted HTML
+        * @return array Form descriptor
         */
-       private function getUserInput( $user ) {
-               $label = Xml::inputLabel(
-                       $this->msg( 'specialloguserlabel' )->text(),
-                       'user',
-                       'mw-log-user',
-                       15,
-                       $user,
-                       [ 'class' => 'mw-autocomplete-user' ]
-               );
-
-               return '<span class="mw-input-with-label">' . $label . '</span>';
+       private function getUserInputDesc( $user ) {
+               return [
+                       'class' => 'HTMLUserTextField',
+                       'label-message' => 'specialloguserlabel',
+                       'name' => 'user',
+               ];
        }
 
        /**
         * @param string $title
-        * @return string Formatted HTML
+        * @return array Form descriptor
         */
-       private function getTitleInput( $title ) {
-               $label = Xml::inputLabel(
-                       $this->msg( 'speciallogtitlelabel' )->text(),
-                       'page',
-                       'mw-log-page',
-                       20,
-                       $title
-               );
-
-               return '<span class="mw-input-with-label">' . $label .  '</span>';
+       private function getTitleInputDesc( $title ) {
+               return [
+                       'class' => 'HTMLTitleTextField',
+                       'label-message' => 'speciallogtitlelabel',
+                       'name' => 'page',
+                       'value' => $title,
+                       'required' => false
+               ];
        }
 
        /**
         * @param bool $pattern
-        * @return string Checkbox
+        * @return array Form descriptor
         */
-       private function getTitlePattern( $pattern ) {
-               return '<span class="mw-input-with-label">' .
-                       Xml::checkLabel( $this->msg( 'log-title-wildcard' )->text(), 'pattern', 'pattern', $pattern ) .
-                       '</span>';
+       private function getTitlePatternDesc( $pattern ) {
+               return [
+                       'type' => 'check',
+                       'label-message' => 'log-title-wildcard',
+                       'name' => 'pattern',
+               ];
        }
 
        /**
         * @param array $types
-        * @return string
+        * @return array Form descriptor
         */
-       private function getExtraInputs( $types ) {
+       private function getExtraInputsDesc( $types ) {
                if ( count( $types ) == 1 ) {
                        if ( $types[0] == 'suppress' ) {
                                $offender = $this->getRequest()->getVal( 'offender' );
@@ -310,42 +289,45 @@ class LogEventsList extends ContextSource {
                                if ( !$user || ( $user->getId() == 0 && !IP::isIPAddress( $offender ) ) ) {
                                        $offender = ''; // Blank field if invalid
                                }
-                               return Xml::inputLabel( $this->msg( 'revdelete-offender' )->text(), 'offender',
-                                       'mw-log-offender', 20, $offender );
+                               return [
+                                       'type' => 'text',
+                                       'label-message' => 'revdelete-offender',
+                                       'name' => 'offender',
+                                       'value' => $offender,
+                               ];
                        } else {
                                // Allow extensions to add their own extra inputs
-                               $input = '';
-                               Hooks::run( 'LogEventsListGetExtraInputs', [ $types[0], $this, &$input ] );
-                               return $input;
+                               $formDescriptor = [];
+                               Hooks::run( 'LogEventsListGetExtraInputs', [ $types[0], $this, &$formDescriptor ] );
+                               return $formDescriptor;
                        }
                }
 
-               return '';
+               return [];
        }
 
        /**
         * Drop down menu for selection of actions that can be used to filter the log
         * @param array $types
         * @param string $action
-        * @return string
-        * @since 1.27
+        * @return array Form descriptor
         */
-       private function getActionSelector( $types, $action ) {
-               if ( $this->allowedActions === null || !count( $this->allowedActions ) ) {
-                       return '';
-               }
-               $html = '';
-               $html .= Xml::label( wfMessage( 'log-action-filter-' . $types[0] )->text(),
-                       'action-filter-' .$types[0] ) . "\n";
-               $select = new XmlSelect( 'subtype' );
-               $select->addOption( wfMessage( 'log-action-filter-all' )->text(), '' );
+       private function getActionSelectorDesc( $types, $action ) {
+               $actionOptions = [];
+               $actionOptions[ 'log-action-filter-all' ] = '';
+
                foreach ( $this->allowedActions as $value ) {
                        $msgKey = 'log-action-filter-' . $types[0] . '-' . $value;
-                       $select->addOption( wfMessage( $msgKey )->text(), $value );
+                       $actionOptions[ $msgKey ] = $value;
                }
-               $select->setDefault( $action );
-               $html .= $select->getHTML();
-               return $html;
+
+               return [
+                       'class' => 'HTMLSelectField',
+                       'name' => 'subtype',
+                       'options-messages' => $actionOptions,
+                       'default' => $action,
+                       'label' => $this->msg( 'log-action-filter-' . $types[0] )->text(),
+               ];
        }
 
        /**
index 6b177ae..2efb462 100644 (file)
@@ -63,13 +63,14 @@ class LogPager extends ReverseChronologicalPager {
         * @param array $conds Extra conditions for the query
         * @param int|bool $year The year to start from. Default: false
         * @param int|bool $month The month to start from. Default: false
+        * @param int|bool $day The day to start from. Default: false
         * @param string $tagFilter Tag
         * @param string $action Specific action (subtype) requested
         * @param int $logId Log entry ID, to limit to a single log entry.
         */
        public function __construct( $list, $types = [], $performer = '', $title = '',
-               $pattern = false, $conds = [], $year = false, $month = false, $tagFilter = '',
-               $action = '', $logId = false
+               $pattern = false, $conds = [], $year = false, $month = false, $day = false,
+               $tagFilter = '', $action = '', $logId = false
        ) {
                parent::__construct( $list->getContext() );
                $this->mConds = $conds;
@@ -80,7 +81,7 @@ class LogPager extends ReverseChronologicalPager {
                $this->limitPerformer( $performer );
                $this->limitTitle( $title, $pattern );
                $this->limitAction( $action );
-               $this->getDateCond( $year, $month );
+               $this->getDateCond( $year, $month, $day );
                $this->mTagFilter = $tagFilter;
                $this->limitLogId( $logId );
 
@@ -91,6 +92,7 @@ class LogPager extends ReverseChronologicalPager {
                $query = parent::getDefaultQuery();
                $query['type'] = $this->typeCGI; // arrays won't work here
                $query['user'] = $this->performer;
+               $query['day'] = $this->mDay;
                $query['month'] = $this->mMonth;
                $query['year'] = $this->mYear;
 
@@ -104,8 +106,12 @@ class LogPager extends ReverseChronologicalPager {
                if ( count( $this->types ) ) {
                        return $filters;
                }
+
+               $request_filters = $this->getRequest()->getArray( "wpfilters" );
+               $request_filters = $request_filters === null ? [] : $request_filters;
+
                foreach ( $wgFilterLogTypes as $type => $default ) {
-                       $hide = $this->getRequest()->getBool( "hide_{$type}_log", $default );
+                       $hide = !in_array( $type, $request_filters );
 
                        $filters[$type] = $hide;
                        if ( $hide ) {
@@ -413,6 +419,10 @@ class LogPager extends ReverseChronologicalPager {
                return $this->mMonth;
        }
 
+       public function getDay() {
+               return $this->mDay;
+       }
+
        public function getTagFilter() {
                return $this->mTagFilter;
        }
index bad1746..359eede 100644 (file)
@@ -46,6 +46,7 @@ class SpecialLog extends SpecialPage {
                $opts->add( 'pattern', false );
                $opts->add( 'year', null, FormOptions::INTNULL );
                $opts->add( 'month', null, FormOptions::INTNULL );
+               $opts->add( 'day', null, FormOptions::INTNULL );
                $opts->add( 'tagfilter', '' );
                $opts->add( 'offset', '' );
                $opts->add( 'dir', '' );
@@ -59,6 +60,17 @@ class SpecialLog extends SpecialPage {
                        $this->parseParams( $opts, (string)$par );
                }
 
+               // Set date values
+               $dateString = $this->getRequest()->getVal( 'wpdate' );
+               if ( !empty( $dateString ) ) {
+                       $dateStamp = MWTimestamp::getInstance( $dateString . ' 00:00:00' );
+                       $dateStamp->setTimezone( $this->getConfig()->get( 'Localtimezone' ) );
+
+                       $opts->setValue( 'year', (int)$dateStamp->format( 'Y' ) );
+                       $opts->setValue( 'month', (int)$dateStamp->format( 'm' ) );
+                       $opts->setValue( 'day', (int)$dateStamp->format( 'd' ) );
+               }
+
                # Don't let the user get stuck with a certain date
                if ( $opts->getValue( 'offset' ) || $opts->getValue( 'dir' ) == 'prev' ) {
                        $opts->setValue( 'year', '' );
@@ -214,6 +226,7 @@ class SpecialLog extends SpecialPage {
                        $extraConds,
                        $opts->getValue( 'year' ),
                        $opts->getValue( 'month' ),
+                       $opts->getValue( 'day' ),
                        $opts->getValue( 'tagfilter' ),
                        $opts->getValue( 'subtype' ),
                        $opts->getValue( 'logid' )
@@ -235,6 +248,7 @@ class SpecialLog extends SpecialPage {
                        $pager->getPattern(),
                        $pager->getYear(),
                        $pager->getMonth(),
+                       $pager->getDay(),
                        $pager->getFilterParams(),
                        $pager->getTagFilter(),
                        $pager->getAction()
index 715f275..8d5bc72 100644 (file)
        "speciallogtitlelabel": "Target (title or {{ns:user}}:username for user):",
        "log": "Logs",
        "logeventslist-submit": "Show",
+       "logeventslist-more-filters": "More filters:",
+       "logeventslist-patrol-log": "Patrol log",
+       "logeventslist-tag-log": "Tag log",
        "all-logs-page": "All public logs",
        "alllogstext": "Combined display of all available logs of {{SITENAME}}.\nYou can narrow down the view by selecting a log type, the username (case-sensitive), or the affected page (also case-sensitive).",
        "logempty": "No matching items in log.",
        "uctop": "(current)",
        "month": "From month (and earlier):",
        "year": "From year (and earlier):",
+       "date": "From date (and earlier):",
        "sp-contributions-newbies": "Show contributions of new accounts only",
        "sp-contributions-newbies-sub": "For new accounts",
        "sp-contributions-newbies-title": "User contributions for new accounts",
index d9fef83..7732a92 100644 (file)
        "speciallogtitlelabel": "Used in [[Special:Log]] as a label for an input field with which the log can be filtered.  This filter selects for pages or users on which a log action was performed.",
        "log": "{{doc-special|Log}}\n{{Identical|Log}}",
        "logeventslist-submit": "Submit button on [[Special:Log]]\n{{Identical|Show}}",
+       "logeventslist-more-filters": "More filters label on [[Special:Log]]",
+       "logeventslist-patrol-log": "Patrol log option label on [[Special:Log]]",
+       "logeventslist-tag-log": "Patrol log option label on [[Special:Log]]",
        "all-logs-page": "{{doc-logpage}}\nTitle of [[Special:Log]].",
        "alllogstext": "Header of [[Special:Log]]",
        "logempty": "Used as warning when there are no items to show.",
        "uctop": "This message is used in [[Special:Contributions]]. It is used to show that a particular edit was the last made to a page. Example: 09:57, 11 February 2008 (hist) (diff) Pagenameā€Ž (edit summary) (current)\n{{Identical|Current}}",
        "month": "Used in [[Special:Contributions]] and history pages ([{{fullurl:Sandbox|action=history}} example]), as label for a dropdown box to select a specific month to view the edits made in that month, and the earlier months. See also {{msg-mw|year}}.",
        "year": "Used in [[Special:Contributions]] and history pages ([{{fullurl:Sandbox|action=history}} example]), as label for an input box to select a specific year to view the edits made in that year, and the earlier years.\n\nSee also:\n* {{msg-mw|month}}",
+       "date": "Used in [[Special:Contributions]] and history pages ([{{fullurl:Sandbox|action=history}} example]), as label for an input box to select a specific date to view the edits made on that date, and earlier.",
        "sp-contributions-newbies": "Text of radio button on special page [[Special:Contributions]].",
        "sp-contributions-newbies-sub": "Note at the top of the page of results for a search on [[Special:Contributions]] where 'Show contributions for new accounts only' has been selected.",
        "sp-contributions-newbies-title": "The page title in your browser bar, but not the page title.\n\nSee also:\n* {{msg-mw|Sp-contributions-newbies-sub}}",