Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.45% covered (warning)
70.45%
31 / 44
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageEditingHandler
70.45% covered (warning)
70.45%
31 / 44
33.33% covered (danger)
33.33%
1 / 3
22.60
0.00% covered (danger)
0.00%
0 / 1
 onNamespaceIsMovable
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 onMultiContentSave
78.57% covered (warning)
78.57%
22 / 28
0.00% covered (danger)
0.00%
0 / 1
8.63
 onGetUserPermissionsErrors
41.67% covered (danger)
41.67%
5 / 12
0.00% covered (danger)
0.00%
0 / 1
13.15
1<?php
2
3/**
4 * WikiLambda handler for hooks which alter page editing
5 *
6 * @file
7 * @ingroup Extensions
8 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
9 * @license MIT
10 */
11
12namespace MediaWiki\Extension\WikiLambda\HookHandler;
13
14use ApiMessage;
15use MediaWiki\CommentStore\CommentStoreComment;
16use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
17use MediaWiki\Extension\WikiLambda\ZObjectContent;
18use MediaWiki\Extension\WikiLambda\ZObjectUtils;
19use MediaWiki\Hook\NamespaceIsMovableHook;
20use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsHook;
21use MediaWiki\Revision\RenderedRevision;
22use MediaWiki\Revision\SlotRecord;
23use MediaWiki\Status\Status;
24use MediaWiki\Storage\Hook\MultiContentSaveHook;
25use MediaWiki\Title\Title;
26use MediaWiki\User\User;
27use MediaWiki\User\UserIdentity;
28use MessageSpecifier;
29
30class PageEditingHandler implements NamespaceIsMovableHook, MultiContentSaveHook, GetUserPermissionsErrorsHook {
31
32    /**
33     * @see https://www.mediawiki.org/wiki/Manual:Hooks/NamespaceIsMovable
34     *
35     * @param int $index
36     * @param bool &$result
37     * @return bool|void
38     */
39    public function onNamespaceIsMovable( $index, &$result ) {
40        if ( $index === NS_MAIN ) {
41            $result = false;
42            // Over-ride any other extensions which might have other ideas
43            return false;
44        }
45
46        return null;
47    }
48
49    /**
50     * @see https://www.mediawiki.org/wiki/Manual:Hooks/MultiContentSave
51     *
52     * @param RenderedRevision $renderedRevision
53     * @param UserIdentity $user
54     * @param CommentStoreComment $summary
55     * @param int $flags
56     * @param Status $hookStatus
57     * @return bool|void
58     */
59    public function onMultiContentSave( $renderedRevision, $user, $summary, $flags, $hookStatus ) {
60        $title = $renderedRevision->getRevision()->getPageAsLinkTarget();
61        if ( !$title->inNamespace( NS_MAIN ) ) {
62            return true;
63        }
64
65        $zid = $title->getDBkey();
66        if ( !ZObjectUtils::isValidZObjectReference( $zid ) ) {
67            $hookStatus->fatal( 'wikilambda-invalidzobjecttitle', $zid );
68            return false;
69        }
70
71        $content = $renderedRevision->getRevision()->getSlots()->getContent( SlotRecord::MAIN );
72
73        if ( !( $content instanceof ZObjectContent ) ) {
74            $hookStatus->fatal( 'wikilambda-invalidcontenttype' );
75            return false;
76        }
77
78        if ( !$content->isValid() ) {
79            $hookStatus->fatal( 'wikilambda-invalidzobject' );
80            return false;
81        }
82
83        // (T260751) Ensure uniqueness of type / label / language triples on save.
84        $newLabels = $content->getLabels()->getValueAsList();
85
86        if ( $newLabels === [] ) {
87            // Unlabelled; don't error.
88            return true;
89        }
90
91        $zObjectStore = WikiLambdaServices::getZObjectStore();
92        $clashes = $zObjectStore->findZObjectLabelConflicts(
93            $zid,
94            $content->getZType(),
95            $newLabels
96        );
97
98        if ( $clashes === [] ) {
99            return true;
100        }
101
102        foreach ( $clashes as $language => $clash_zid ) {
103            $hookStatus->fatal( 'wikilambda-labelclash', $clash_zid, $language );
104        }
105
106        return false;
107    }
108
109    /**
110     * @see https://www.mediawiki.org/wiki/Manual:Hooks/getUserPermissionsErrors
111     *
112     * @param Title $title
113     * @param User $user
114     * @param string $action
115     * @param array|string|MessageSpecifier &$result
116     * @return bool|void
117     */
118    public function onGetUserPermissionsErrors( $title, $user, $action, &$result ) {
119        if ( !$title->inNamespace( NS_MAIN ) ) {
120            return;
121        }
122
123        // TODO (T362234): Is there a nicer way of getting 'all change actions'?
124        if ( !( $action == 'create' || $action == 'edit' || $action == 'upload' ) ) {
125            return;
126        }
127
128        $zid = $title->getDBkey();
129        if ( !ZObjectUtils::isValidZObjectReference( $zid ) ) {
130            $result = ApiMessage::create(
131                wfMessage( 'wikilambda-invalidzobjecttitle', $zid ),
132                'wikilambda-invalidzobjecttitle'
133            );
134            return false;
135        }
136
137        // NOTE: We don't do per-user rights checks here; that's left to ZObjectAuthorization
138
139        return true;
140    }
141}