Merge "Use Sanitizer::stripAllTags( $x ) instead of html_entity_decode( strip_tags...
[lhc/web/wiklou.git] / includes / specials / SpecialRecentchanges.php
index aaa99b6..4f21586 100644 (file)
@@ -31,9 +31,104 @@ use Wikimedia\Rdbms\FakeResultWrapper;
  * @ingroup SpecialPage
  */
 class SpecialRecentChanges extends ChangesListSpecialPage {
+
+       private $watchlistFilterGroupDefinition;
+
        // @codingStandardsIgnoreStart Needed "useless" override to change parameters.
        public function __construct( $name = 'Recentchanges', $restriction = '' ) {
                parent::__construct( $name, $restriction );
+
+               $this->watchlistFilterGroupDefinition = [
+                       'name' => 'watchlist',
+                       'title' => 'rcfilters-filtergroup-watchlist',
+                       'class' => ChangesListStringOptionsFilterGroup::class,
+                       'priority' => -9,
+                       'isFullCoverage' => true,
+                       'filters' => [
+                               [
+                                       'name' => 'watched',
+                                       'label' => 'rcfilters-filter-watchlist-watched-label',
+                                       'description' => 'rcfilters-filter-watchlist-watched-description',
+                                       'cssClassSuffix' => 'watched',
+                                       'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                               return $rc->getAttribute( 'wl_user' );
+                                       }
+                               ],
+                               [
+                                       'name' => 'watchednew',
+                                       'label' => 'rcfilters-filter-watchlist-watchednew-label',
+                                       'description' => 'rcfilters-filter-watchlist-watchednew-description',
+                                       'cssClassSuffix' => 'watchednew',
+                                       'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                               return $rc->getAttribute( 'wl_user' ) &&
+                                                       $rc->getAttribute( 'rc_timestamp' ) &&
+                                                       $rc->getAttribute( 'wl_notificationtimestamp' ) &&
+                                                       $rc->getAttribute( 'rc_timestamp' ) >= $rc->getAttribute( 'wl_notificationtimestamp' );
+                                       },
+                               ],
+                               [
+                                       'name' => 'notwatched',
+                                       'label' => 'rcfilters-filter-watchlist-notwatched-label',
+                                       'description' => 'rcfilters-filter-watchlist-notwatched-description',
+                                       'cssClassSuffix' => 'notwatched',
+                                       'isRowApplicableCallable' => function ( $ctx, $rc ) {
+                                               return $rc->getAttribute( 'wl_user' ) === null;
+                                       },
+                               ]
+                       ],
+                       'default' => ChangesListStringOptionsFilterGroup::NONE,
+                       'queryCallable' => function ( $specialPageClassName, $context, $dbr,
+                               &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedValues ) {
+                               sort( $selectedValues );
+                               $notwatchedCond = 'wl_user IS NULL';
+                               $watchedCond = 'wl_user IS NOT NULL';
+                               $newCond = 'rc_timestamp >= wl_notificationtimestamp';
+
+                               if ( $selectedValues === [ 'notwatched' ] ) {
+                                       $conds[] = $notwatchedCond;
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'watched' ] ) {
+                                       $conds[] = $watchedCond;
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'watchednew' ] ) {
+                                       $conds[] = $dbr->makeList( [
+                                               $watchedCond,
+                                               $newCond
+                                       ], LIST_AND );
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'notwatched', 'watched' ] ) {
+                                       // no filters
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'notwatched', 'watchednew' ] ) {
+                                       $conds[] = $dbr->makeList( [
+                                               $notwatchedCond,
+                                               $dbr->makeList( [
+                                                       $watchedCond,
+                                                       $newCond
+                                               ], LIST_AND )
+                                       ], LIST_OR );
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'watched', 'watchednew' ] ) {
+                                       $conds[] = $watchedCond;
+                                       return;
+                               }
+
+                               if ( $selectedValues === [ 'notwatched', 'watched', 'watchednew' ] ) {
+                                       // no filters
+                                       return;
+                               }
+                       }
+               ];
        }
        // @codingStandardsIgnoreEnd
 
@@ -72,7 +167,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        $jsData = $this->getStructuredFilterJsData();
 
                        $messages = [];
-                       foreach ( $jsData['messageKeys'] as $key ){
+                       foreach ( $jsData['messageKeys'] as $key ) {
                                $messages[$key] = $this->msg( $key )->plain();
                        }
 
@@ -83,9 +178,66 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        );
 
                        $out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] );
+                       $out->addJsConfigVars(
+                               'wgStructuredChangeFiltersEnableSaving',
+                               $this->getConfig()->get( 'StructuredChangeFiltersEnableSaving' )
+                       );
+                       $out->addJsConfigVars(
+                               'wgStructuredChangeFiltersEnableExperimentalViews',
+                               $this->getConfig()->get( 'StructuredChangeFiltersEnableExperimentalViews' )
+                       );
+                       $out->addJsConfigVars(
+                               'wgStructuredChangeFiltersEnableLiveUpdate',
+                               $this->getConfig()->get( 'StructuredChangeFiltersEnableLiveUpdate' )
+                       );
+                       $out->addJsConfigVars(
+                               'wgRCFiltersChangeTags',
+                               $this->buildChangeTagList()
+                       );
                }
        }
 
+       /**
+        * Fetch the change tags list for the front end
+        *
+        * @return Array Tag data
+        */
+       protected function buildChangeTagList() {
+               $explicitlyDefinedTags = array_fill_keys( ChangeTags::listExplicitlyDefinedTags(), 0 );
+               $softwareActivatedTags = array_fill_keys( ChangeTags::listSoftwareActivatedTags(), 0 );
+               $tagStats = ChangeTags::tagUsageStatistics();
+
+               $tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags, $tagStats );
+
+               // Sort by hits
+               arsort( $tagHitCounts );
+
+               // Build the list and data
+               $result = [];
+               foreach ( $tagHitCounts as $tagName => $hits ) {
+                       if (
+                               // Only get active tags
+                               isset( $explicitlyDefinedTags[ $tagName ] ) ||
+                               isset( $softwareActivatedTags[ $tagName ] )
+                       ) {
+                               // Parse description
+                               $desc = ChangeTags::tagLongDescriptionMessage( $tagName, $this->getContext() );
+
+                               $result[] = [
+                                       'name' => $tagName,
+                                       'label' => Sanitizer::stripAllTags(
+                                               ChangeTags::tagDescription( $tagName, $this->getContext() )
+                                       ),
+                                       'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) : '',
+                                       'cssClass' => Sanitizer::escapeClass( 'mw-tag-' . $tagName ),
+                                       'hits' => $hits,
+                               ];
+                       }
+               }
+
+               return $result;
+       }
+
        /**
         * @inheritdoc
         */
@@ -103,6 +255,18 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        protected function registerFilters() {
                parent::registerFilters();
 
+               if (
+                       !$this->including() &&
+                       $this->getUser()->isLoggedIn() &&
+                       $this->getUser()->isAllowed( 'viewmywatchlist' )
+               ) {
+                       $this->registerFiltersFromDefinitions( [ $this->watchlistFilterGroupDefinition ] );
+                       $watchlistGroup = $this->getFilterGroup( 'watchlist' );
+                       $watchlistGroup->getFilter( 'watched' )->setAsSupersetOf(
+                               $watchlistGroup->getFilter( 'watchednew' )
+                       );
+               }
+
                $user = $this->getUser();
 
                $significance = $this->getFilterGroup( 'significance' );
@@ -184,7 +348,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) {
                                $opts['days'] = $m[1];
                        }
-                       if ( preg_match( '/^namespace=(\d+)$/', $bit, $m ) ) {
+                       if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) {
                                $opts['namespace'] = $m[1];
                        }
                        if ( preg_match( '/^tagfilter=(.*)$/', $bit, $m ) ) {
@@ -202,8 +366,8 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         * @inheritdoc
         */
        protected function buildQuery( &$tables, &$fields, &$conds,
-               &$query_options, &$join_conds, FormOptions $opts ) {
-
+               &$query_options, &$join_conds, FormOptions $opts
+       ) {
                $dbr = $this->getDB();
                parent::buildQuery( $tables, $fields, $conds,
                        $query_options, $join_conds, $opts );
@@ -227,8 +391,8 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         * @inheritdoc
         */
        protected function doMainQuery( $tables, $fields, $conds, $query_options,
-               $join_conds, FormOptions $opts ) {
-
+               $join_conds, FormOptions $opts
+       ) {
                $dbr = $this->getDB();
                $user = $this->getUser();
 
@@ -236,7 +400,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $fields = array_merge( RecentChange::selectFields(), $fields );
 
                // JOIN on watchlist for users
-               if ( $user->getId() && $user->isAllowed( 'viewmywatchlist' ) ) {
+               if ( $user->isLoggedIn() && $user->isAllowed( 'viewmywatchlist' ) ) {
                        $tables[] = 'watchlist';
                        $fields[] = 'wl_user';
                        $fields[] = 'wl_notificationtimestamp';
@@ -247,11 +411,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                        ] ];
                }
 
-               if ( $user->isAllowed( 'rollback' ) ) {
-                       $tables[] = 'page';
-                       $fields[] = 'page_latest';
-                       $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
-               }
+               // JOIN on page, used for 'last revision' filter highlight
+               $tables[] = 'page';
+               $fields[] = 'page_latest';
+               $join_conds['page'] = [ 'LEFT JOIN', 'rc_cur_id=page_id' ];
 
                ChangeTags::modifyDisplayQuery(
                        $tables,
@@ -357,6 +520,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
 
                $userShowHiddenCats = $this->getUser()->getBoolOption( 'showhiddencats' );
                $rclistOutput = $list->beginRecentChangesList();
+               if ( $this->isStructuredFilterUiEnabled() ) {
+                       $rclistOutput .= $this->makeLegend();
+               }
+
                foreach ( $rows as $obj ) {
                        if ( $limit == 0 ) {
                                break;
@@ -424,7 +591,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $nondefaults = $opts->getChangedValues();
 
                $panel = [];
-               $panel[] = $this->makeLegend();
+               if ( !$this->isStructuredFilterUiEnabled() ) {
+                       $panel[] = $this->makeLegend();
+               }
                $panel[] = $this->optionsPanel( $defaults, $nondefaults, $numRows );
                $panel[] = '<hr />';
 
@@ -513,7 +682,11 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                if ( !$message->isDisabled() ) {
                        $this->getOutput()->addWikiText(
                                Html::rawElement( 'div',
-                                       [ 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ],
+                                       [
+                                               'class' => 'mw-recentchanges-toplinks',
+                                               'lang' => $wgContLang->getHtmlCode(),
+                                               'dir' => $wgContLang->getDir()
+                                       ],
                                        "\n" . $message->plain() . "\n"
                                ),
                                /* $lineStart */ true,
@@ -708,16 +881,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
         * @return string
         */
        function makeOptionsLink( $title, $override, $options, $active = false ) {
-               $params = $override + $options;
-
-               // T38524: false values have be converted to "0" otherwise
-               // wfArrayToCgi() will omit it them.
-               foreach ( $params as &$value ) {
-                       if ( $value === false ) {
-                               $value = '0';
-                       }
-               }
-               unset( $value );
+               $params = $this->convertParamsForLink( $override + $options );
 
                if ( $active ) {
                        $title = new HtmlArmor( '<strong>' . htmlspecialchars( $title ) . '</strong>' );