jenkins-bot has submitted this change and it was merged.

Change subject: Category support for topic summary and header
......................................................................


Category support for topic summary and header

Records categories via ParserOutput for topic summaries and
headers. Adds categories to OutputPage when performing 'view'
actions.

Currently has default output on category pages, there is a follow
up patch that requires a core change to enable the prettier
display.

Bug: T87793
Change-Id: I4f1433bd133bf4cdb94fda6bd0d7256f2eb98194
---
M autoload.php
M container.php
M includes/LinksTableUpdater.php
M includes/Model/WikiReference.php
A includes/Parsoid/Extractor/CategoryExtractor.php
M includes/Parsoid/ReferenceExtractor.php
M includes/ReferenceClarifier.php
M includes/View.php
M tests/phpunit/Parsoid/ReferenceExtractorTest.php
9 files changed, 142 insertions(+), 24 deletions(-)

Approvals:
  Matthias Mullie: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/autoload.php b/autoload.php
index 0e12407..72f28b1 100644
--- a/autoload.php
+++ b/autoload.php
@@ -245,6 +245,7 @@
        'Flow\\OccupationController' => __DIR__ . 
'/includes/TalkpageManager.php',
        'Flow\\Parsoid\\ContentFixer' => __DIR__ . 
'/includes/Parsoid/ContentFixer.php',
        'Flow\\Parsoid\\Extractor' => __DIR__ . 
'/includes/Parsoid/Extractor.php',
+       'Flow\\Parsoid\\Extractor\\CategoryExtractor' => __DIR__ . 
'/includes/Parsoid/Extractor/CategoryExtractor.php',
        'Flow\\Parsoid\\Extractor\\ExtLinkExtractor' => __DIR__ . 
'/includes/Parsoid/Extractor/ExtLinkExtractor.php',
        'Flow\\Parsoid\\Extractor\\ImageExtractor' => __DIR__ . 
'/includes/Parsoid/Extractor/ImageExtractor.php',
        'Flow\\Parsoid\\Extractor\\PlaceholderExtractor' => __DIR__ . 
'/includes/Parsoid/Extractor/PlaceholderExtractor.php',
diff --git a/container.php b/container.php
index a3404c8..e4a6dcd 100644
--- a/container.php
+++ b/container.php
@@ -389,6 +389,7 @@
                // belongs to, not just its parent. TopicHistoryIndex is a 
slight tweak to TopKIndex
                // using TreeRepository for extra information and stuffing it 
into topic_root while indexing
                $c['storage.topic_history.indexes.primary'],
+               $c['reference.recorder'],
        );
 };
 $c['storage.post_summary.backend'] = $c->share( function( $c ) {
@@ -964,18 +965,6 @@
        );
 } );
 
-$c['reference.extractor'] = $c->share( function( $c ) {
-       return new Flow\Parsoid\ReferenceExtractor(
-               array(
-                       new Flow\Parsoid\Extractor\ImageExtractor,
-                       new Flow\Parsoid\Extractor\PlaceholderExtractor,
-                       new Flow\Parsoid\Extractor\WikiLinkExtractor,
-                       new Flow\Parsoid\Extractor\ExtLinkExtractor,
-                       new Flow\Parsoid\Extractor\TransclusionExtractor,
-               )
-       );
-} );
-
 $c['storage.wiki_reference.class'] = 'Flow\Model\WikiReference';
 $c['storage.wiki_reference.table'] = 'flow_wiki_ref';
 $c['storage.wiki_reference.primary_key'] = array(
@@ -1117,6 +1106,26 @@
        return new Flow\ReferenceClarifier( $c['storage'], $c['url_generator'] 
);
 } );
 
+$c['reference.extractor'] = $c->share( function( $c ) {
+       $default = array(
+               new Flow\Parsoid\Extractor\ImageExtractor,
+               new Flow\Parsoid\Extractor\PlaceholderExtractor,
+               new Flow\Parsoid\Extractor\WikiLinkExtractor,
+               new Flow\Parsoid\Extractor\ExtLinkExtractor,
+               new Flow\Parsoid\Extractor\TransclusionExtractor,
+       );
+       $extractors = array(
+               'header' => $default,
+               'post-summary' => $default,
+               'post' => $default,
+       );
+       // In addition to the defaults header and summaries collect
+       // the related categories.
+       $extractors['header'][] = $extractors['post-summary'][] = new 
Flow\Parsoid\Extractor\CategoryExtractor;
+
+       return new Flow\Parsoid\ReferenceExtractor( $extractors );
+} );
+
 $c['reference.recorder'] = $c->share( function( $c ) {
        return new Flow\Data\Listener\ReferenceRecorder(
                $c['reference.extractor'],
diff --git a/includes/LinksTableUpdater.php b/includes/LinksTableUpdater.php
index 0886fd7..9346dff 100644
--- a/includes/LinksTableUpdater.php
+++ b/includes/LinksTableUpdater.php
@@ -63,6 +63,15 @@
                                        
$internalLinks[$reference->getTitle()->getPrefixedDBkey()] = 
$reference->getTitle();
                                        $linkBatch->addObj( 
$reference->getTitle() );
                                }
+                       } elseif ( $reference->getType() === 
WikiReference::TYPE_CATEGORY ) {
+                               if ( $reference instanceof WikiReference ) {
+                                       $title = $reference->getTitle();
+                                       $parserOutput->addCategory(
+                                               $title->getDBkey(),
+                                               // parsoid moves the sort key 
into the fragment
+                                               $title->getFragment()
+                                       );
+                               }
                        } elseif ( $reference->getType() === 'file' ) {
                                if ( $reference instanceof WikiReference ) {
                                        // Only local images supported
diff --git a/includes/Model/WikiReference.php b/includes/Model/WikiReference.php
index 8dcf090..43c4f08 100644
--- a/includes/Model/WikiReference.php
+++ b/includes/Model/WikiReference.php
@@ -8,6 +8,7 @@
 class WikiReference extends Reference {
        const TYPE_FILE = 'file';
        const TYPE_TEMPLATE = 'template';
+       const TYPE_CATEGORY = 'category';
 
        protected $target;
 
@@ -26,6 +27,7 @@
                        array(
                                self::TYPE_FILE,
                                self::TYPE_TEMPLATE,
+                               self::TYPE_CATEGORY,
                        )
                );
 
diff --git a/includes/Parsoid/Extractor/CategoryExtractor.php 
b/includes/Parsoid/Extractor/CategoryExtractor.php
new file mode 100644
index 0000000..8b0a0d3
--- /dev/null
+++ b/includes/Parsoid/Extractor/CategoryExtractor.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Flow\Parsoid\Extractor;
+
+use DOMElement;
+use Flow\Model\WikiReference;
+use Flow\Parsoid\ReferenceFactory;
+use Flow\Parsoid\Extractor;
+use Title;
+
+/**
+ * Runs against page content via Flow\Parsoid\ReferenceExtractor
+ * and collects all category references output by parsoid. The DOM
+ * spec states that categories are represented as such:
+ *
+ *   <link rel="mw:PageProp/Category" href="...">
+ *
+ * Flow does not currently handle the other page properties. When it becomes
+ * necessary a more generic page property extractor should be implemented and
+ * this class should be removed.
+ */
+class CategoryExtractor implements Extractor {
+       /**
+        * {@inheritDoc}
+        */
+       public function getXPath() {
+               return '//link[starts-with( @rel, "mw:PageProp/Category" )]';
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function perform( ReferenceFactory $factory, DOMElement $element 
) {
+               // our provided xpath guarantees there is a rel attribute
+               // with our expected format so only perform a very minimal
+               // validation.
+               $rel = $element->getAttribute( 'rel' );
+               if ( $rel !== 'mw:PageProp/Category' ) {
+                       return null;
+               }
+
+               $href = $element->getAttribute( 'href' );
+               if ( $href ) {
+                       return $factory->createWikiReference(
+                               WikiReference::TYPE_CATEGORY,
+                               urldecode( $href )
+                       );
+               } else {
+                       return null;
+               }
+       }
+}
diff --git a/includes/Parsoid/ReferenceExtractor.php 
b/includes/Parsoid/ReferenceExtractor.php
index 7b71f65..dc5af8c 100644
--- a/includes/Parsoid/ReferenceExtractor.php
+++ b/includes/Parsoid/ReferenceExtractor.php
@@ -15,14 +15,16 @@
  */
 class ReferenceExtractor {
        /**
-        * @var Extractor[]
+        * @var Extractor[][] Map from revision type 
(AbstractRevision::getRevisionType())
+        *  to list of Extractor objects to use.
         */
        protected $extractors;
 
        /**
-        * @param Extractor[] $extractors
+        * @param Extractor[][] $extractors Map from revision type 
(AbstractRevision::getRevisionType())
+        *  to a list of extractors to use.
         */
-       public function __construct( $extractors ) {
+       public function __construct( array $extractors ) {
                $this->extractors = $extractors;
        }
 
@@ -34,27 +36,34 @@
         * @return array
         */
        public function getReferences( Workflow $workflow, $objectType, UUID 
$objectId, $text ) {
-               return $this->extractReferences(
-                       new ReferenceFactory( $workflow, $objectType, $objectId 
),
-                       $text
-               );
+               if ( isset( $this->extractors[$objectType] ) ) {
+                       return $this->extractReferences(
+                               new ReferenceFactory( $workflow, $objectType, 
$objectId ),
+                               $this->extractors[$objectType],
+                               $text
+                       );
+               } else {
+                       throw new \Exception( "No extractors available for 
$objectType" );
+                       return array();
+               }
        }
 
        /**
         * @param ReferenceFactory $factory
+        * @param Extractor[] $extractors
         * @param string $text
         * @return Reference[]
         * @throws MWException
         * @throws \Flow\Exception\WikitextException
         */
-       protected function extractReferences( ReferenceFactory $factory, $text 
) {
+       protected function extractReferences( ReferenceFactory $factory, array 
$extractors, $text ) {
                $dom = Utils::createDOM( '<?xml encoding="utf-8" ?>' . $text );
 
                $output = array();
 
                $xpath = new DOMXPath( $dom );
 
-               foreach( $this->extractors as $extractor ) {
+               foreach( $extractors as $extractor ) {
                        $elements = $xpath->query( $extractor->getXPath() );
 
                        if ( !$elements ) {
diff --git a/includes/ReferenceClarifier.php b/includes/ReferenceClarifier.php
index fb199ff..1fe339f 100644
--- a/includes/ReferenceClarifier.php
+++ b/includes/ReferenceClarifier.php
@@ -41,6 +41,11 @@
                // * flow-whatlinkshere-post
                // Topic and Summary are plain text and do not have links.
                foreach( $references as $reference ) {
+                       if ( $reference->getType() === 
WikiReference::TYPE_CATEGORY ) {
+                               // While it might make sense to have backlinks 
from categories to
+                               // a page in what links here, thats not what 
mediawiki currently does.
+                               continue;
+                       }
                        try {
                                $url = $this->getObjectLink( 
$reference->getWorkflowId(), $reference->getObjectType(), 
$reference->getObjectId() );
                                $props[] = wfMessage( 'flow-whatlinkshere-' . 
$reference->getObjectType(), $url )->parse();
diff --git a/includes/View.php b/includes/View.php
index 6e610c0..bcfd9ea 100644
--- a/includes/View.php
+++ b/includes/View.php
@@ -11,6 +11,7 @@
 use Html;
 use IContextSource;
 use Message;
+use Title;
 use WebRequest;
 
 
@@ -156,11 +157,14 @@
                } );
                wfProfileOut( __CLASS__ . '-serialize' );
 
-               // Update newtalk and watchlist notification status on view 
action of any workflow
-               // since the normal page view that resets notification status 
is not accessiable
-               // anymore due to Flow occupation
                if ( $action === 'view' ) {
+                       // Update newtalk and watchlist notification status on 
view action of any workflow
+                       // since the normal page view that resets notification 
status is not accessiable
+                       // anymore due to Flow occupation
                        $user->clearNotification( $title );
+
+                       // attach categories that would be shown on a normal 
page
+                       $out->addCategoryLinks( $this->getCategories( $title ) 
);
                }
 
                /**
@@ -260,4 +264,26 @@
 
                return $result;
        }
+
+       protected function getCategories( Title $title ) {
+               $id = $title->getArticleId();
+               if ( !$id ) {
+                       return array();
+               }
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $res = $dbr->select(
+                       /* from */ 'categorylinks',
+                       /* select */ array( 'cl_to', 'cl_sortkey' ),
+                       /* conditions */ array( 'cl_from' => $id ),
+                       __METHOD__
+               );
+
+               $categories = array();
+               foreach ( $res as $row ) {
+                       $categories[$row->cl_to] = $row->cl_sortkey;
+               }
+
+               return $categories;
+       }
 }
diff --git a/tests/phpunit/Parsoid/ReferenceExtractorTest.php 
b/tests/phpunit/Parsoid/ReferenceExtractorTest.php
index 07d13ee..e47e85c 100644
--- a/tests/phpunit/Parsoid/ReferenceExtractorTest.php
+++ b/tests/phpunit/Parsoid/ReferenceExtractorTest.php
@@ -159,9 +159,14 @@
                $reflMethod = new ReflectionMethod( $referenceExtractor, 
'extractReferences' );
                $reflMethod->setAccessible( true );
 
+               $reflProperty = new \ReflectionProperty( $referenceExtractor, 
'extractors' );
+               $reflProperty->setAccessible( true );
+               $extractors = $reflProperty->getValue( $referenceExtractor );
+
                $result = $reflMethod->invoke(
                        $referenceExtractor,
                        $factory,
+                       $extractors['post'],
                        Utils::convert( 'wt', 'html', $wikitext, 
Title::newFromText( $page ) )
                );
                $this->assertCount( 1, $result );

-- 
To view, visit https://gerrit.wikimedia.org/r/188307
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I4f1433bd133bf4cdb94fda6bd0d7256f2eb98194
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
Gerrit-Reviewer: EBernhardson <[email protected]>
Gerrit-Reviewer: Matthias Mullie <[email protected]>
Gerrit-Reviewer: SG <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to