Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 7
240
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
 onListDefinedTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onChangeTagsListActive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addChangeTags
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 onRecentChange_save
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
72
 onAbuseFilter_generateUserVars
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 onAbuseFilterBuilder
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\MobileApp;
4
5use ChangeTags;
6use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook;
7use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
8use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder;
9use MediaWiki\Hook\RecentChange_saveHook;
10use MediaWiki\User\User;
11use RecentChange;
12use Wikimedia\Rdbms\IConnectionProvider;
13
14class Hooks implements
15    ListDefinedTagsHook,
16    ChangeTagsListActiveHook,
17    RecentChange_saveHook
18{
19    private IConnectionProvider $dbProvider;
20
21    public function __construct(
22        IConnectionProvider $dbProvider
23    ) {
24        $this->dbProvider = $dbProvider;
25    }
26
27    /**
28     * ListDefinedTags hook handler
29     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ListDefinedTags
30     *
31     * @param array &$tags
32     */
33    public function onListDefinedTags( &$tags ) {
34        $this->addChangeTags( $tags );
35    }
36
37    /**
38     * ChangeTagsListActive hook handler
39     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ChangeTagsListActive
40     *
41     * @param array &$tags
42     */
43    public function onChangeTagsListActive( &$tags ) {
44        $this->addChangeTags( $tags );
45    }
46
47    /**
48     * @param array &$tags
49     */
50    private function addChangeTags( &$tags ) {
51        $tags[] = 'mobile edit';
52        $tags[] = 'mobile app edit';
53        $tags[] = 'android app edit';
54        $tags[] = 'ios app edit';
55    }
56
57    /**
58     * RecentChange_save hook handler that tags mobile changes
59     * @see https://www.mediawiki.org/wiki/Manual:Hooks/RecentChange_save
60     * @param RecentChange $rc
61     */
62    public function onRecentChange_save( $rc ) {
63        global $wgRequest;
64        $userAgent = $wgRequest->getHeader( "User-agent" );
65        $isWikipediaApp = strpos( $userAgent, "WikipediaApp/" ) === 0;
66        $isCommonsApp = strpos( $userAgent, "Commons/" ) === 0;
67        $logType = $rc->getAttribute( 'rc_log_type' );
68
69        // Apply tag for edits done with the Wikipedia app, and
70        // edits and uploads done with the Commons app
71        if (
72            ( $isWikipediaApp && $logType === null )
73            || ( $isCommonsApp && ( $logType === null || $logType === 'upload' ) )
74        ) {
75            // Although MobileFrontend applies the "mobile edit" tag to any edit
76            // that is made through the mobile domain, the Android app actually
77            // makes its API requests through the desktop domain, meaning that we
78            // must apply the "mobile edit" tag explicitly ourselves, in addition
79            // to the "mobile app edit" tag.
80            $tags = [ 'mobile edit', 'mobile app edit' ];
81
82            $isAndroid = strpos( $userAgent, "Android" ) > 0;
83            $isIOS = strpos( $userAgent, "iOS" ) > 0;
84
85            if ( $isAndroid ) {
86                $tags[] = 'android app edit';
87            } elseif ( $isIOS ) {
88                $tags[] = 'ios app edit';
89            }
90
91            $rc->addTags( $tags );
92        }
93    }
94
95    /**
96     * AbuseFilter-generateUserVars hook handler that adds the user_app variable.
97     *
98     * @see hooks.txt in AbuseFilter extension
99     * @param VariableHolder $vars object to add vars to
100     * @param User $user
101     * @param RecentChange|null $rc If the variables should be generated for an RC entry, this
102     *  is the entry. Null if it's for the current action being filtered.
103     * @return bool
104     */
105    public function onAbuseFilter_generateUserVars( $vars, $user, RecentChange $rc = null ) {
106        global $wgRequest;
107        if ( !$rc ) {
108            $userAgent = $wgRequest->getHeader( "User-agent" );
109            $isWikipediaApp = strpos( $userAgent, "WikipediaApp/" ) === 0;
110            $vars->setVar( 'user_app', $isWikipediaApp );
111        } else {
112            $dbr = $this->dbProvider->getReplicaDatabase();
113            $tags = ChangeTags::getTags( $dbr, $rc->getAttribute( 'rc_id' ) );
114            $vars->setVar( 'user_app', in_array( 'mobile app edit', $tags, true ) );
115        }
116        return true;
117    }
118
119    /**
120     * AbuseFilter-builder hook handler that adds user_app variable to list
121     *  of valid vars
122     *
123     * @param array &$builder Array in AbuseFilter::getBuilderValues to add to.
124     * @return bool
125     */
126    public static function onAbuseFilterBuilder( &$builder ) {
127        $builder['vars']['user_app'] = 'user-app';
128        return true;
129    }
130}