Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 36 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
0.00% |
0 / 36 |
|
0.00% |
0 / 7 |
306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onListDefinedTags | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onChangeTagsListActive | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
onRecentChange_save | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
110 | |||
onAbuseFilter_generateUserVars | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
onAbuseFilterBuilder | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getMobileAppTagsFromRequest | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\MobileApp; |
4 | |
5 | use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook; |
6 | use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook; |
7 | use MediaWiki\Context\RequestContext; |
8 | use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder; |
9 | use MediaWiki\Hook\RecentChange_saveHook; |
10 | use MediaWiki\MediaWikiServices; |
11 | use MediaWiki\User\User; |
12 | use RecentChange; |
13 | use Wikimedia\Rdbms\IConnectionProvider; |
14 | |
15 | // phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
16 | |
17 | class Hooks implements |
18 | ListDefinedTagsHook, |
19 | ChangeTagsListActiveHook, |
20 | RecentChange_saveHook |
21 | { |
22 | private IConnectionProvider $dbProvider; |
23 | |
24 | private const USER_AGENT_TAGS = [ |
25 | 'mobile edit', |
26 | 'mobile app edit', |
27 | 'android app edit', |
28 | 'ios app edit' |
29 | ]; |
30 | |
31 | private const APP_EDIT_TAGS = [ |
32 | 'app-suggestededit', |
33 | 'app-undo', |
34 | 'app-rollback', |
35 | 'app-description-add', |
36 | 'app-description-change', |
37 | 'app-description-translate', |
38 | 'app-section-source', |
39 | 'app-full-source', |
40 | 'app-select-source', |
41 | 'app-talk-source', |
42 | 'app-talk-reply', |
43 | 'app-talk-topic', |
44 | 'app-image-caption-add', |
45 | 'app-image-caption-translate', |
46 | 'app-image-tag-add', |
47 | 'app-image-add-top', |
48 | 'app-image-add-infobox', |
49 | 'app-ai-assist' |
50 | ]; |
51 | |
52 | public function __construct( |
53 | IConnectionProvider $dbProvider |
54 | ) { |
55 | $this->dbProvider = $dbProvider; |
56 | } |
57 | |
58 | /** |
59 | * ListDefinedTags hook handler |
60 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ListDefinedTags |
61 | * |
62 | * @param array &$tags |
63 | */ |
64 | public function onListDefinedTags( &$tags ) { |
65 | $tags = array_merge( $tags, static::USER_AGENT_TAGS, static::APP_EDIT_TAGS ); |
66 | } |
67 | |
68 | /** |
69 | * ChangeTagsListActive hook handler |
70 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ChangeTagsListActive |
71 | * |
72 | * @param array &$tags |
73 | */ |
74 | public function onChangeTagsListActive( &$tags ) { |
75 | $this->onListDefinedTags( $tags ); |
76 | } |
77 | |
78 | /** |
79 | * RecentChange_save hook handler that tags mobile changes |
80 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/RecentChange_save |
81 | * @param RecentChange $rc |
82 | */ |
83 | public function onRecentChange_save( $rc ) { |
84 | global $wgRequest; |
85 | $userAgent = $wgRequest->getHeader( "User-agent" ); |
86 | $isWikipediaApp = strpos( $userAgent, "WikipediaApp/" ) === 0; |
87 | $isCommonsApp = strpos( $userAgent, "Commons/" ) === 0; |
88 | $logType = $rc->getAttribute( 'rc_log_type' ); |
89 | |
90 | // Apply tag for edits done with the Wikipedia app, and |
91 | // edits and uploads done with the Commons app |
92 | if ( |
93 | ( $isWikipediaApp && $logType === null ) |
94 | || ( $isCommonsApp && ( $logType === null || $logType === 'upload' ) ) |
95 | ) { |
96 | // Although MobileFrontend applies the "mobile edit" tag to any edit |
97 | // that is made through the mobile domain, the Android app actually |
98 | // makes its API requests through the desktop domain, meaning that we |
99 | // must apply the "mobile edit" tag explicitly ourselves, in addition |
100 | // to the "mobile app edit" tag. |
101 | $tags = [ 'mobile edit', 'mobile app edit' ]; |
102 | |
103 | $isAndroid = strpos( $userAgent, "Android" ) > 0; |
104 | $isIOS = strpos( $userAgent, "iOS" ) > 0 || strpos( $userAgent, "iPadOS" ) > 0; |
105 | |
106 | if ( $isAndroid ) { |
107 | $tags[] = 'android app edit'; |
108 | } elseif ( $isIOS ) { |
109 | $tags[] = 'ios app edit'; |
110 | } |
111 | |
112 | $matags = static::getMobileAppTagsFromRequest(); |
113 | if ( $matags ) { |
114 | $tags = array_merge( $tags, $matags ); |
115 | } |
116 | |
117 | $rc->addTags( $tags ); |
118 | } |
119 | } |
120 | |
121 | /** |
122 | * AbuseFilter-generateUserVars hook handler that adds the user_app variable. |
123 | * |
124 | * @see hooks.txt in AbuseFilter extension |
125 | * @param VariableHolder $vars object to add vars to |
126 | * @param User $user |
127 | * @param RecentChange|null $rc If the variables should be generated for an RC entry, this |
128 | * is the entry. Null if it's for the current action being filtered. |
129 | * @return bool |
130 | */ |
131 | public function onAbuseFilter_generateUserVars( $vars, $user, ?RecentChange $rc = null ) { |
132 | global $wgRequest; |
133 | if ( !$rc ) { |
134 | $userAgent = $wgRequest->getHeader( "User-agent" ); |
135 | $isWikipediaApp = strpos( $userAgent, "WikipediaApp/" ) === 0; |
136 | $vars->setVar( 'user_app', $isWikipediaApp ); |
137 | } else { |
138 | $dbr = $this->dbProvider->getReplicaDatabase(); |
139 | $tags = MediaWikiServices::getInstance()->getChangeTagsStore() |
140 | ->getTags( $dbr, $rc->getAttribute( 'rc_id' ) ); |
141 | $vars->setVar( 'user_app', in_array( 'mobile app edit', $tags, true ) ); |
142 | } |
143 | return true; |
144 | } |
145 | |
146 | /** |
147 | * AbuseFilter-builder hook handler that adds user_app variable to list |
148 | * of valid vars |
149 | * |
150 | * @param array &$builder Array in AbuseFilter::getBuilderValues to add to. |
151 | * @return bool |
152 | */ |
153 | public static function onAbuseFilterBuilder( &$builder ) { |
154 | $builder['vars']['user_app'] = 'user-app'; |
155 | return true; |
156 | } |
157 | |
158 | /** |
159 | * Get MobileApp tags from the matags param in the request, and validate against known tags. |
160 | */ |
161 | public static function getMobileAppTagsFromRequest(): array { |
162 | $request = RequestContext::getMain()->getRequest(); |
163 | $tags = explode( ',', $request->getText( 'matags' ) ); |
164 | return array_values( array_intersect( $tags, static::APP_EDIT_TAGS ) ); |
165 | } |
166 | } |