Special:Log: Convert to HTMLForm

* 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
This commit is contained in:
Prateek Saxena 2018-04-25 10:52:19 +05:30
parent 1d17626dc2
commit 38756eae46
7 changed files with 148 additions and 132 deletions

View 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

View 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.

View 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() );
$htmlForm = new HTMLForm( $formDescriptor, $this->getContext() );
$htmlForm
->setSubmitText( $this->msg( 'logeventslist-submit' )->text() )
->setWrapperLegendMsg( 'log' );
// Fieldset
$html = Xml::fieldset( $this->msg( 'log' )->text(), $html );
// Form wrapping
$html = Xml::tags( 'form', [ 'action' => $wgScript, 'method' => 'get' ], $html );
$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(),
];
}
/**

View 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;
}

View 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()

View file

@ -2136,6 +2136,9 @@
"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.",
@ -2506,6 +2509,7 @@
"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",

View file

@ -2336,6 +2336,9 @@
"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.",
@ -2706,6 +2709,7 @@
"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}}",