Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
13.46% covered (danger)
13.46%
7 / 52
9.09% covered (danger)
9.09%
1 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
13.46% covered (danger)
13.46%
7 / 52
9.09% covered (danger)
9.09%
1 / 11
252.96
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 onGetPreferences
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 getDiscoveryMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isBetaDiscoveryMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isPublicDiscoveryMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDiscoverBundleData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getArticleSectionTitle
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 onLoginFormValidErrorMessages
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onParserCacheSaveComplete
36.36% covered (danger)
36.36%
4 / 11
0.00% covered (danger)
0.00%
0 / 1
8.12
 onArticlePurge
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 onActionModifyFormFields
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace MediaWiki\Extension\Wikistories;
4
5use MediaWiki\Config\Config;
6use MediaWiki\Deferred\DeferredUpdates;
7use MediaWiki\Extension\Wikistories\Jobs\ArticleChangedJob;
8use MediaWiki\Hook\ActionModifyFormFieldsHook;
9use MediaWiki\Hook\LoginFormValidErrorMessagesHook;
10use MediaWiki\Hook\ParserCacheSaveCompleteHook;
11use MediaWiki\JobQueue\JobQueueGroup;
12use MediaWiki\Page\Article;
13use MediaWiki\Page\Hook\ArticlePurgeHook;
14use MediaWiki\Page\WikiPage;
15use MediaWiki\Parser\ParserCache;
16use MediaWiki\Parser\ParserOptions;
17use MediaWiki\Parser\ParserOutput;
18use MediaWiki\Preferences\Hook\GetPreferencesHook;
19use MediaWiki\SpecialPage\SpecialPage;
20use MediaWiki\Title\Title;
21use MediaWiki\User\User;
22
23class Hooks implements
24    LoginFormValidErrorMessagesHook,
25    GetPreferencesHook,
26    ParserCacheSaveCompleteHook,
27    ArticlePurgeHook,
28    ActionModifyFormFieldsHook
29{
30
31    public const WIKISTORIES_PREF_SHOW_DISCOVERY = 'wikistories-pref-showdiscovery';
32
33    private const WIKISTORIES_MODE_BETA = 'beta';
34
35    private const WIKISTORIES_MODE_PUBLIC = 'public';
36
37    private const WIKISTORIES_PREF_VIEWER_TEXTSIZE = 'wikistories-pref-viewertextsize';
38
39    public function __construct(
40        private readonly Config $mainConfig,
41        private readonly JobQueueGroup $jobQueueGroup,
42        private readonly StoriesCache $storiesCache,
43        private readonly PageLinksSearch $pageLinksSearch,
44    ) {
45    }
46
47    /**
48     * @param User $user
49     * @param array &$preferences
50     */
51    public function onGetPreferences( $user, &$preferences ) {
52        if ( self::isPublicDiscoveryMode( $this->mainConfig ) ) {
53            $preferences[ self::WIKISTORIES_PREF_SHOW_DISCOVERY ] = [
54                'section' => 'rendering/wikistories',
55                'label-message' => 'wikistories-pref-showdiscovery-message',
56                'help-message' => 'wikistories-pref-showdiscovery-help-message',
57                'type' => 'toggle',
58            ];
59        }
60        $preferences[ self::WIKISTORIES_PREF_VIEWER_TEXTSIZE ] = [
61            'type' => 'api',
62        ];
63    }
64
65    /**
66     * @param Config $config
67     * @return mixed
68     */
69    private static function getDiscoveryMode( Config $config ) {
70        return $config->get( 'WikistoriesDiscoveryMode' );
71    }
72
73    public static function isBetaDiscoveryMode( Config $config ): bool {
74        return self::getDiscoveryMode( $config ) === self::WIKISTORIES_MODE_BETA;
75    }
76
77    public static function isPublicDiscoveryMode( Config $config ): bool {
78        return self::getDiscoveryMode( $config ) === self::WIKISTORIES_MODE_PUBLIC;
79    }
80
81    /**
82     * @return array Data used by the 'discover' module
83     */
84    public static function getDiscoverBundleData(): array {
85        return [ 'storyBuilder' => SpecialPage::getTitleValueFor( 'StoryBuilder' )->getText() ];
86    }
87
88    /**
89     * @return array Data used by the 'builder' module to get title translation
90     */
91    public static function getArticleSectionTitle(): array {
92        return [
93            'See_also' => [
94                'en' => 'See_also',
95                'id' => 'Lihat_pula'
96            ]
97        ];
98    }
99
100    /**
101     * Register a message to make sure Special:StoryBuilder can redirect
102     * to the login page when the user is logged out.
103     *
104     * @param string[] &$messages List of messages valid on login screen
105     */
106    public function onLoginFormValidErrorMessages( array &$messages ) {
107        $messages[] = 'wikistories-specialstorybuilder-mustbeloggedin';
108    }
109
110    /**
111     * @param ParserCache $parserCache
112     * @param ParserOutput $parserOutput
113     * @param Title $title
114     * @param ParserOptions $parserOptions
115     * @param int $revId
116     */
117    public function onParserCacheSaveComplete(
118        $parserCache,
119        $parserOutput,
120        $title,
121        $parserOptions,
122        $revId
123    ) {
124        if ( $title->getNamespace() !== NS_MAIN ) {
125            return;
126        }
127
128        if ( $parserOptions->getRenderReason() !== 'edit-page' ) {
129            // Don't want to trigger story outdated verification for any other reason
130            return;
131        }
132
133        DeferredUpdates::addCallableUpdate( function () use ( $title ) {
134            $links = $this->pageLinksSearch->getPageLinks( $title->getDBkey(), 1 );
135            if ( count( $links ) === 0 ) {
136                return;
137            }
138
139            $job = ArticleChangedJob::newSpec( $title->getId() );
140            $this->jobQueueGroup->push( $job );
141        } );
142    }
143
144    /**
145     * @param WikiPage $wikiPage
146     * @return void
147     */
148    public function onArticlePurge( $wikiPage ) {
149        if ( $wikiPage->getNamespace() !== NS_STORY ) {
150            return;
151        }
152
153        $this->storiesCache->invalidateStory( $wikiPage->getId() );
154    }
155
156    /**
157     * @param string $name
158     * @param array &$fields
159     * @param Article $article
160     */
161    public function onActionModifyFormFields(
162        $name,
163        &$fields,
164        $article
165    ) {
166        // skip when not delete action and not an article
167        if ( $name !== 'delete' || $article->getPage()->getNamespace() !== NS_MAIN ) {
168            return;
169        }
170
171        // skip when no stories found in this article
172        $title = $article->getPage()->getTitle()->getDBkey();
173        $links = $this->pageLinksSearch->getPageLinks( $title, 1 );
174        if ( count( $links ) === 0 ) {
175            return;
176        }
177
178        // Add DeleteStory Field before ConfirmB
179        // @todo Add Unit Test to prevent UI break when DeleteAction.php change
180        $confirmBField = $fields[ 'ConfirmB' ];
181        unset( $fields[ 'ConfirmB' ] );
182        $fields[ 'DeleteStory' ] = [
183            'type' => 'check',
184            'id' => 'wpDeleteStory',
185            'default' => true,
186            'tabIndex' => $confirmBField[ 'tabindex' ] + 1,
187            'label-message' => 'deletepage-deletestory'
188        ];
189        $fields[ 'ConfirmB' ] = $confirmBField;
190    }
191}