Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 109
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 109
0.00% covered (danger)
0.00%
0 / 11
420
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
 onInfoAction
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 onApiQuery__moduleManager
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
20
 onAPIQuerySiteInfoGeneralInfo
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getApiMetricsMap
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getApiScopeMap
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getApiMetricsHelp
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
6
 getApiDaysHelp
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 makeWarningsOnlyStatus
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 toYmd
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 toYmdHis
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\PageViewInfo;
4
5use MediaWiki\Api\ApiBase;
6use MediaWiki\Api\ApiModuleManager;
7use MediaWiki\Api\ApiQuerySiteinfo;
8use MediaWiki\Api\Hook\ApiQuery__moduleManagerHook;
9use MediaWiki\Api\Hook\APIQuerySiteInfoGeneralInfoHook;
10use MediaWiki\Context\IContextSource;
11use MediaWiki\Hook\InfoActionHook;
12use MediaWiki\Html\Html;
13use MediaWiki\MediaWikiServices;
14use StatusValue;
15use Wikimedia\ParamValidator\ParamValidator;
16use Wikimedia\ParamValidator\TypeDef\IntegerDef;
17
18class Hooks implements
19    ApiQuery__moduleManagerHook,
20    APIQuerySiteInfoGeneralInfoHook,
21    InfoActionHook
22{
23
24    public function __construct(
25        private readonly PageViewService $pageViewService,
26    ) {
27    }
28
29    /**
30     * Display total pageviews in the last 30 days.
31     *
32     * @param IContextSource $ctx
33     * @param array &$pageInfo
34     */
35    public function onInfoAction( $ctx, &$pageInfo ) {
36        if ( !$this->pageViewService->supports( PageViewService::METRIC_VIEW,
37            PageViewService::SCOPE_ARTICLE )
38        ) {
39            return;
40        }
41        $title = $ctx->getTitle();
42        $status = $this->pageViewService->getPageData( [ $title ], 30, PageViewService::METRIC_VIEW );
43        $data = $status->getValue();
44        if ( !$status->isOK() ) {
45            return;
46        }
47        $views = $data[$title->getPrefixedDBkey()];
48
49        $total = array_sum( $views );
50        $start = self::toYmdHis( array_key_first( $views ) );
51        $end = self::toYmdHis( array_key_last( $views ) );
52
53        $lang = $ctx->getLanguage();
54        $formatted = $lang->formatNum( $total );
55        $pageInfo['header-basic'][] = [
56            $ctx->msg( 'pvi-month-count' ),
57            Html::rawElement( 'div',
58                [ 'class' => 'mw-pvi-month' ],
59                $ctx->msg( 'pvi-month-count-value', $formatted, $title->getPrefixedDBkey() )->parse()
60            )
61        ];
62    }
63
64    /**
65     * Limit enabled PageViewInfo API modules to those which are supported by the service.
66     * @param ApiModuleManager $moduleManager
67     */
68    public function onApiQuery__moduleManager( $moduleManager ) {
69        $moduleMap = [
70            // Order is: module name, module group, objectFactory spec
71            'pageviews' => [
72                'pageviews',
73                'prop',
74                [
75                    'class' => ApiQueryPageViews::class,
76                    'services' => [
77                        'PageViewService',
78                        'TitleFormatter',
79                    ]
80                ]
81            ],
82            'siteviews' => [
83                'siteviews',
84                'meta',
85                [
86                    'class' => ApiQuerySiteViews::class,
87                    'services' => [
88                        'PageViewService',
89                    ]
90                ]
91            ],
92            'mostviewed' => [
93                'mostviewed',
94                'list',
95                [
96                    'class' => ApiQueryMostViewed::class,
97                    'services' => [
98                        'PageViewService',
99                    ]
100                ]
101            ],
102        ];
103        foreach ( self::getApiScopeMap() as $apiModuleName => $serviceScopeConstant ) {
104            foreach ( self::getApiMetricsMap() as $serviceMetricConstant ) {
105                if ( $this->pageViewService->supports( $serviceMetricConstant, $serviceScopeConstant ) ) {
106                    $moduleManager->addModule( ...$moduleMap[$apiModuleName] );
107                    continue 2;
108                }
109            }
110        }
111    }
112
113    /**
114     * Add information to the siteinfo API output about which metrics are supported.
115     * @param ApiQuerySiteinfo $module
116     * @param array &$result
117     */
118    public function onAPIQuerySiteInfoGeneralInfo( $module, &$result ) {
119        $supportedMetrics = [];
120        foreach ( self::getApiScopeMap() as $apiModuleName => $serviceScopeConstant ) {
121            foreach ( self::getApiMetricsMap() as $apiMetricsName => $serviceMetricConstant ) {
122                $supportedMetrics[$apiModuleName][$apiMetricsName] =
123                    $this->pageViewService->supports( $serviceMetricConstant, $serviceScopeConstant );
124            }
125        }
126        $result['pageviewservice-supported-metrics'] = $supportedMetrics;
127    }
128
129    /**
130     * Maps allowed values of the 'metric' parameter of the pageview-related APIs to service constants.
131     * @return array
132     */
133    public static function getApiMetricsMap() {
134        return [
135            'pageviews' => PageViewService::METRIC_VIEW,
136            'uniques' => PageViewService::METRIC_UNIQUE,
137        ];
138    }
139
140    /**
141     * Maps API module names to service constants.
142     * @return array
143     */
144    public static function getApiScopeMap() {
145        return [
146            'pageviews' => PageViewService::SCOPE_ARTICLE,
147            'siteviews' => PageViewService::SCOPE_SITE,
148            'mostviewed' => PageViewService::SCOPE_TOP,
149        ];
150    }
151
152    /**
153     * Returns an array suitable for merging into getAllowedParams()
154     * @param string $scope One of the PageViewService::SCOPE_* constants
155     * @return array
156     */
157    public static function getApiMetricsHelp( $scope ) {
158        /** @var PageViewService $service */
159        $service = MediaWikiServices::getInstance()->getService( 'PageViewService' );
160        $metrics = array_keys( array_filter( self::getApiMetricsMap(),
161            static function ( $metric ) use ( $scope, $service ) {
162                return $service->supports( $metric, $scope );
163            } ) );
164        $reverseMap = array_flip( self::getApiMetricsMap() );
165        $default = $reverseMap[PageViewService::METRIC_VIEW] ?? reset( $reverseMap );
166
167        return $default ? [
168            'metric' => [
169                ParamValidator::PARAM_TYPE => $metrics,
170                ParamValidator::PARAM_DEFAULT => $default,
171                ApiBase::PARAM_HELP_MSG => 'apihelp-pageviewinfo-param-metric',
172                ApiBase::PARAM_HELP_MSG_PER_VALUE => array_map( static function ( $metric ) {
173                    return 'apihelp-pageviewinfo-paramvalue-metric-' . $metric;
174                }, array_combine( $metrics, $metrics ) ),
175            ],
176        ] : [];
177    }
178
179    /**
180     * Returns an array suitable for merging into getAllowedParams()
181     * @return array
182     */
183    public static function getApiDaysHelp() {
184        $days = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'PageViewInfo' )
185            ->get( 'PageViewApiMaxDays' );
186        return [
187            'days' => [
188                ParamValidator::PARAM_TYPE => 'integer',
189                ParamValidator::PARAM_DEFAULT => $days,
190                IntegerDef::PARAM_MAX => $days,
191                IntegerDef::PARAM_MIN => 1,
192                ApiBase::PARAM_HELP_MSG => 'apihelp-pageviewinfo-param-days',
193            ],
194        ];
195    }
196
197    /**
198     * Transform into a status with errors replaced with warnings
199     * @param StatusValue $status
200     * @return StatusValue
201     */
202    public static function makeWarningsOnlyStatus( StatusValue $status ) {
203        [ $errors, $warnings ] = $status->splitByErrorType();
204        foreach ( $errors->getErrors() as $error ) {
205            $warnings->warning( $error['message'], ...$error['params'] );
206        }
207        return $warnings;
208    }
209
210    /**
211     * Convert YYYY-MM-DD to YYYYMMDD
212     * @param string $date
213     * @return string
214     */
215    protected static function toYmd( $date ) {
216        return substr( $date, 0, 4 ) . substr( $date, 5, 2 ) . substr( $date, 8, 2 );
217    }
218
219    /**
220     * Convert YYYY-MM-DD to TS_MW
221     * @param string $date
222     * @return string
223     */
224    protected static function toYmdHis( $date ) {
225        return substr( $date, 0, 4 ) . substr( $date, 5, 2 ) . substr( $date, 8, 2 ) . '000000';
226    }
227}