Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReferenceClarifier
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 5
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getWhatLinksHereProps
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 getWikiReferences
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getObjectLink
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 loadReferencesForPage
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace Flow;
4
5use Flow\Data\ManagerGroup;
6use Flow\Exception\CrossWikiException;
7use Flow\Model\Reference;
8use Flow\Model\UUID;
9use Flow\Model\WikiReference;
10use MediaWiki\Title\Title;
11use MediaWiki\WikiMap\WikiMap;
12
13class ReferenceClarifier {
14    /** @var ManagerGroup */
15    protected $storage;
16    /** @var UrlGenerator */
17    protected $urlGenerator;
18    /** @var WikiReference[][][] */
19    protected $referenceCache;
20
21    public function __construct( ManagerGroup $storage, UrlGenerator $urlGenerator ) {
22        $this->storage = $storage;
23        $this->urlGenerator = $urlGenerator;
24        $this->referenceCache = [];
25    }
26
27    public function getWhatLinksHereProps( $row, Title $from, Title $to ) {
28        $ids = [];
29        $props = [];
30        $references = $this->getWikiReferences( $from, $to );
31
32        // Collect referenced workflow ids and load them so we can generate
33        // links to their pages
34        foreach ( $references as $reference ) {
35            $id = $reference->getWorkflowId();
36            // utilize array key to de-duplicate
37            $ids[$id->getAlphadecimal()] = $id;
38        }
39
40        // Don't need to do anything with them, they are automatically
41        // passed into url generator when loaded.
42        $this->storage->getMulti( 'Workflow', $ids );
43
44        // Messages that can be used here:
45        // * flow-whatlinkshere-header
46        // * flow-whatlinkshere-post
47        // * flow-whatlinkshere-post-summary
48        // Topic is plain text and do not have links.
49        foreach ( $references as $reference ) {
50            if ( $reference->getType() === WikiReference::TYPE_CATEGORY ) {
51                // While it might make sense to have backlinks from categories to
52                // a page in what links here, thats not what mediawiki currently does.
53                continue;
54            }
55            try {
56                $url = $this->getObjectLink( $reference->getWorkflowId(), $reference->getObjectType(), $reference->getObjectId() );
57                $props[] = wfMessage( 'flow-whatlinkshere-' . $reference->getObjectType(), $url )->parse();
58            } catch ( CrossWikiException $e ) {
59                // Ignore expected cross-wiki exception.
60                // Gerrit 136280 would add a wiki field to the query in
61                // loadReferencesForPage(), we can remove catching the exception
62                // in here once it's merged
63            }
64        }
65
66        return $props;
67    }
68
69    /**
70     * @param Title $from
71     * @param Title $to
72     * @return WikiReference[]
73     */
74    public function getWikiReferences( Title $from, Title $to ) {
75        if ( !isset( $this->referenceCache[$from->getPrefixedDBkey()] ) ) {
76            $this->loadReferencesForPage( $from );
77        }
78
79        $fromT = $from->getPrefixedDBkey();
80        $toT = 'title:' . $to->getPrefixedDBkey();
81
82        return $this->referenceCache[$fromT][$toT] ?? [];
83    }
84
85    /**
86     * @param UUID $workflow
87     * @param string $objectType
88     * @param UUID $objectId
89     * @return string Full URL
90     */
91    protected function getObjectLink( UUID $workflow, $objectType, UUID $objectId ) {
92        if ( $objectType === 'post' ) {
93            $anchor = $this->urlGenerator->postLink( null, $workflow, $objectId );
94        } elseif ( $objectType === 'header' || $objectType === 'post-summary' ) {
95            $anchor = $this->urlGenerator->workflowLink( null, $workflow );
96        } else {
97            wfDebugLog( 'Flow', __METHOD__ . ": Unknown \$objectType: $objectType" );
98            $anchor = $this->urlGenerator->workflowLink( null, $workflow );
99        }
100
101        return $anchor->getFullURL();
102    }
103
104    protected function loadReferencesForPage( Title $from ) {
105        /** @var Reference[] $allReferences */
106        $allReferences = [];
107
108        foreach ( [ 'WikiReference', 'URLReference' ] as $refType ) {
109            // find() returns null for error or empty result
110            $res = $this->storage->find(
111                $refType,
112                [
113                    'ref_src_wiki' => WikiMap::getCurrentWikiId(),
114                    'ref_src_namespace' => $from->getNamespace(),
115                    'ref_src_title' => $from->getDBkey(),
116                ]
117            );
118
119            if ( $res ) {
120                /*
121                 * We're "cheating", we have no PK!
122                 * We used to have a unique index (on all columns), but the size
123                 * of the index was too small (urls can be pretty long...)
124                 * We have no data integrity reasons to want to ensure unique
125                 * entries, and the code actually does a good job of only
126                 * inserting uniques. Still, I'll do a sanity check and get rid
127                 * of duplicates, should there be any...
128                 */
129                $res = array_unique( $res );
130                $allReferences = array_merge( $allReferences, $res );
131            }
132        }
133
134        $cache = [];
135        foreach ( $allReferences as $reference ) {
136            if ( !isset( $cache[$reference->getTargetIdentifier()] ) ) {
137                $cache[$reference->getTargetIdentifier()] = [];
138            }
139
140            $cache[$reference->getTargetIdentifier()][] = $reference;
141        }
142
143        $this->referenceCache[$from->getPrefixedDBkey()] = $cache;
144    }
145}