Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
JsonSchemaHooks
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 6
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onApiMain__moduleManager
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 onEditFilterMergedContent
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 onBeforePageDisplay
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 onMovePageIsValidMove
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 onCanonicalNamespaces
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Hooks for managing JSON Schema namespace and content model.
4 *
5 * @file
6 * @ingroup Extensions
7 * @ingroup EventLogging
8 *
9 * @author Ori Livneh <ori@wikimedia.org>
10 */
11
12namespace MediaWiki\Extension\EventLogging;
13
14use MediaWiki\Api\ApiModuleManager;
15use MediaWiki\Api\Hook\ApiMain__moduleManagerHook;
16use MediaWiki\Content\Content;
17use MediaWiki\Context\IContextSource;
18use MediaWiki\EditPage\EditPage;
19use MediaWiki\Extension\EventLogging\Libs\JsonSchemaValidation\JsonSchemaException;
20use MediaWiki\Hook\CanonicalNamespacesHook;
21use MediaWiki\Hook\EditFilterMergedContentHook;
22use MediaWiki\Hook\MovePageIsValidMoveHook;
23use MediaWiki\Output\Hook\BeforePageDisplayHook;
24use MediaWiki\Output\OutputPage;
25use MediaWiki\Skin\Skin;
26use MediaWiki\Status\Status;
27use MediaWiki\Title\Title;
28use MediaWiki\User\User;
29
30class JsonSchemaHooks implements
31    BeforePageDisplayHook,
32    EditFilterMergedContentHook,
33    MovePageIsValidMoveHook,
34    ApiMain__moduleManagerHook,
35    CanonicalNamespacesHook
36{
37    private JsonSchemaHooksHelper $jsonSchemaHooksHelper;
38
39    public function __construct( JsonSchemaHooksHelper $jsonSchemaHooksHelper ) {
40        $this->jsonSchemaHooksHelper = $jsonSchemaHooksHelper;
41    }
42
43    /**
44     * ApiMain::moduleManager hook to register jsonschema
45     * API module only if the Schema namespace is enabled
46     *
47     * @param ApiModuleManager $moduleManager
48     */
49    public function onApiMain__moduleManager( $moduleManager ): void {
50        if ( $this->jsonSchemaHooksHelper->isSchemaNamespaceEnabled() ) {
51            $moduleManager->addModule(
52                'jsonschema',
53                'action',
54                ApiJsonSchema::class
55            );
56        }
57    }
58
59    /**
60     * Validates that the revised contents are valid JSON.
61     * If not valid, rejects edit with error message.
62     *
63     * @param IContextSource $context
64     * @param Content $content
65     * @param Status $status
66     * @param string $summary
67     * @param User $user
68     * @param bool $minoredit
69     * @return bool
70     */
71    public function onEditFilterMergedContent(
72        IContextSource $context,
73        Content $content,
74        Status $status,
75        $summary,
76        User $user,
77        $minoredit
78    ): bool {
79        $title = $context->getTitle();
80
81        if ( $this->jsonSchemaHooksHelper->isSchemaNamespaceEnabled()
82            || !$title->inNamespace( NS_SCHEMA )
83        ) {
84            return true;
85        }
86
87        if ( !preg_match( '/^[a-zA-Z0-9_-]{1,63}$/', $title->getText() ) ) {
88            $status->fatal( 'badtitle' );
89            // @todo Remove this line after this extension do not support mediawiki version 1.36 and before
90            $status->value = EditPage::AS_HOOK_ERROR_EXPECTED;
91            return false;
92        }
93
94        if ( !$content instanceof JsonSchemaContent ) {
95            return true;
96        }
97
98        try {
99            $content->validate();
100            return true;
101        } catch ( JsonSchemaException $e ) {
102            $status->fatal( $context->msg( $e->getCode(), $e->args ) );
103            // @todo Remove this line after this extension do not support mediawiki version 1.36 and before
104            $status->value = EditPage::AS_HOOK_ERROR_EXPECTED;
105            return false;
106        }
107    }
108
109    /**
110     * Add the revision id as the subtitle on NS_SCHEMA pages.
111     *
112     * @param OutputPage $out
113     * @param Skin $skin
114     */
115    public function onBeforePageDisplay( $out, $skin ): void {
116        $title = $out->getTitle();
117        $revId = $out->getRevisionId();
118
119        if ( $this->jsonSchemaHooksHelper->isSchemaNamespaceEnabled()
120            && $title->inNamespace( NS_SCHEMA )
121            && $revId !== null
122        ) {
123            $out->addSubtitle( $out->msg( 'eventlogging-revision-id' )
124                // We use 'rawParams' rather than 'numParams' to make it
125                // easy to copy/paste the value into code.
126                ->rawParams( $revId )
127                ->escaped() );
128        }
129    }
130
131    /**
132     * Prohibit moving (renaming) Schema pages, as doing so violates
133     * immutability guarantees.
134     *
135     * @param Title $currentTitle
136     * @param Title $newTitle
137     * @param Status $status
138     * @return bool
139     */
140    public function onMovePageIsValidMove(
141        $currentTitle, $newTitle, $status
142    ) {
143        if ( !$this->jsonSchemaHooksHelper->isSchemaNamespaceEnabled() ) {
144            // Namespace isn't even enabled
145            return true;
146        } elseif ( $currentTitle->inNamespace( NS_SCHEMA ) ) {
147            $status->fatal( 'eventlogging-error-move-source' );
148            return false;
149        } elseif ( $newTitle->inNamespace( NS_SCHEMA ) ) {
150            $status->fatal( 'eventlogging-error-move-destination' );
151            return false;
152        }
153        return true;
154    }
155
156    /**
157     * @inheritDoc
158     */
159    public function onCanonicalNamespaces( &$namespaces ): void {
160        if ( $this->jsonSchemaHooksHelper->isSchemaNamespaceEnabled() ) {
161            $namespaces[ NS_SCHEMA ] = 'Schema';
162            $namespaces[ NS_SCHEMA_TALK ] = 'Schema_talk';
163        }
164    }
165}