Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
Session
0.00% covered (danger)
0.00%
0 / 97
0.00% covered (danger)
0.00%
0 / 13
1640
0.00% covered (danger)
0.00%
0 / 1
 hasSession
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 startSession
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 touchSession
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 clearCollection
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 enable
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 disable
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isEnabled
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 hasItems
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 countArticles
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 findArticle
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
132
 purge
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
90
 getCollection
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 setCollection
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Collection;
4
5use MediaWiki\MediaWikiServices;
6use MediaWiki\Session\SessionManager;
7use MediaWiki\Title\Title;
8
9/**
10 * Collection Extension for MediaWiki
11 *
12 * Copyright (C) PediaPress GmbH
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 * http://www.gnu.org/copyleft/gpl.html
28 */
29
30class Session {
31    /**
32     * @return bool
33     */
34    public static function hasSession() {
35        $session = SessionManager::getGlobalSession();
36        return isset( $session['wsCollection'] );
37    }
38
39    public static function startSession() {
40        $session = SessionManager::getGlobalSession();
41        $session->persist();
42
43        self::clearCollection();
44    }
45
46    public static function touchSession() {
47        $session = SessionManager::getGlobalSession();
48        $collection = $session['wsCollection'];
49        $collection['timestamp'] = wfTimestampNow();
50        $session['wsCollection'] = $collection;
51    }
52
53    public static function clearCollection() {
54        $session = SessionManager::getGlobalSession();
55        $session['wsCollection'] = [
56            'enabled' => true,
57            'title' => '',
58            'subtitle' => '',
59            'settings' => [],
60            'items' => [],
61        ];
62        Suggest::clear();
63        self::touchSession();
64    }
65
66    public static function enable() {
67        $session = SessionManager::getGlobalSession();
68        $session->persist();
69
70        $session['wsCollection']['enabled'] = true;
71        self::touchSession();
72    }
73
74    public static function disable() {
75        $session = SessionManager::getGlobalSession();
76
77        if ( !isset( $session['wsCollection'] ) ) {
78            return;
79        }
80        self::clearCollection();
81        $session['wsCollection']['enabled'] = false;
82        self::touchSession();
83    }
84
85    /**
86     * @return bool
87     */
88    public static function isEnabled() {
89        $session = SessionManager::getGlobalSession();
90
91        return isset( $session['wsCollection'] ) &&
92            isset( $session['wsCollection']['enabled'] ) &&
93            $session['wsCollection']['enabled'];
94    }
95
96    /**
97     * @return bool
98     */
99    public static function hasItems() {
100        $session = SessionManager::getGlobalSession();
101
102        return isset( $session['wsCollection'] ) &&
103            isset( $session['wsCollection']['items'] );
104    }
105
106    /**
107     * @return int
108     */
109    public static function countArticles() {
110        if ( !self::hasItems() ) {
111            return 0;
112        }
113        $session = SessionManager::getGlobalSession();
114        $count = 0;
115        foreach ( $session['wsCollection']['items'] as $item ) {
116            if ( $item && $item['type'] == 'article' ) {
117                $count++;
118            }
119        }
120        return $count;
121    }
122
123    /**
124     * @param string $title
125     * @param int $oldid
126     * @return int
127     */
128    public static function findArticle( $title, $oldid = 0 ) {
129        if ( !self::hasItems() ) {
130            return -1;
131        }
132
133        // FIXME: Some places use DB keys, other use prefixedtext, and this can lead to mismatches.
134        // This class should just take Title (or a narrower interface) and be responsible for the stringification!
135        $titleObj = Title::newFromText( $title );
136        $titleStr = $titleObj ? $titleObj->getPrefixedDBkey() : $title;
137        $session = SessionManager::getGlobalSession();
138
139        foreach ( $session['wsCollection']['items'] as $index => $item ) {
140            if ( !$item || $item['type'] !== 'article' ) {
141                continue;
142            }
143            $curTitleObj = Title::newFromText( $item['title'] );
144            $curTitleStr = $curTitleObj ? $curTitleObj->getPrefixedDBkey() : $item['title'];
145            if ( $curTitleStr === $titleStr ) {
146                if ( $oldid ) {
147                    if ( $item['revision'] == strval( $oldid ) ) {
148                        return $index;
149                    }
150                } else {
151                    if ( $item['revision'] == $item['latest'] ) {
152                        return $index;
153                    }
154                }
155            }
156        }
157        return -1;
158    }
159
160    /**
161     * @return bool
162     */
163    public static function purge() {
164        $session = SessionManager::getGlobalSession();
165
166        if ( !isset( $session['wsCollection'] ) ) {
167            return false;
168        }
169
170        $coll = $session['wsCollection'];
171        $coll['items'] ??= [];
172        // Remove broken entries caused by old, bogus code paths
173        $coll['items'] = array_filter( $coll['items'] );
174
175        // Checking if articles exist is expensive, use a single batch to speed it up
176        $services = MediaWikiServices::getInstance();
177        $linkBatch = $services->getLinkBatchFactory()->newLinkBatch();
178        $linkCache = $services->getLinkCache();
179        foreach ( $coll['items'] as $item ) {
180            if ( $item['type'] === 'article' ) {
181                $title = Title::newFromText( $item['title'] );
182                if ( $title ) {
183                    $linkBatch->addObj( $title );
184                }
185            }
186        }
187        $linkBatch->execute();
188
189        $newitems = [];
190        foreach ( $coll['items'] as $item ) {
191            if ( $item['type'] === 'article' ) {
192                $title = Title::newFromText( $item['title'] );
193                // Remove articles that don't exist any more
194                if ( !$title || $linkCache->isBadLink( $title->getPrefixedDBkey() ) ) {
195                    continue;
196                }
197            }
198            $newitems[] = $item;
199        }
200        $coll['items'] = $newitems;
201        $session['wsCollection'] = $coll;
202        return true;
203    }
204
205    /**
206     * @return array
207     */
208    public static function getCollection() {
209        $session = SessionManager::getGlobalSession();
210        $collection = self::purge() ? $session['wsCollection'] : [];
211        return array_merge( [
212            /* Make sure required properties are present.  */
213            'title' => '',
214            'subtitle' => '',
215            'settings' => [],
216            'items' => [],
217        ], $collection );
218    }
219
220    /**
221     * @param array $collection
222     */
223    public static function setCollection( array $collection ) {
224        $session = SessionManager::getGlobalSession();
225        $session['wsCollection'] = $collection;
226        self::touchSession();
227    }
228}