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