From e1cf035b255a3d057e126e9efdb091673cd674a3 Mon Sep 17 00:00:00 2001 From: Charlot Date: Mon, 25 Sep 2023 11:44:21 +0200 Subject: [PATCH 01/10] rel1_39 start --- README.md | 11 ++--- WSStats.php | 29 ----------- extension.json | 2 +- i18n/en.json | 3 +- src/WSStatsHooks.php | 87 ++++++++++++++++----------------- src/export/WSStatsExport.php | 6 +-- src/specials/SpecialWSStats.php | 10 ++-- 7 files changed, 58 insertions(+), 90 deletions(-) delete mode 100644 WSStats.php diff --git a/README.md b/README.md index bb9d285..31eb0e8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # WSStats -This MediaWiki extension counts pageviews by user +This MediaWiki 1.39.x extension counts pageviews by user * Version 1.0.8 : Removed global references * Version 1.0.7 : Added statistics over time for pages @@ -58,16 +58,11 @@ $wgWSStats['skip_anonymous'] = false; # Skip if user is in following groups $wgWSStats['skip_user_groups'][] = 'sysop'; $wgWSStats['skip_user_groups'][] = 'admin'; -```` -Count all hits: -```` -$wgWSStats = array(); -$wgWSStats['count_all_usergroups'] = true; +# Allow all usergroups, including sysop +$wgWSStats['skip_user_groups'] = []; ```` -***NOTE**: If you have set $wgWSStats['count_all']=true; then $wgWSStats['skip_user_groups'] is ignored.* -'' Skip page with certain text in their referer url. Default action=edit and veaction=edit are ignored. This configuration option is case sensitive: ```` diff --git a/WSStats.php b/WSStats.php deleted file mode 100644 index d714ff7..0000000 --- a/WSStats.php +++ /dev/null @@ -1,29 +0,0 @@ -isAnon(); @@ -45,7 +46,7 @@ public static function isAnon() { * * @return mixed */ - public static function getConfigSetting( string $name ) { + public static function getConfigSetting( string $name ) : mixed { $config = MediaWikiServices::getInstance()->getMainConfig(); if ( $config->has( 'WSStats' ) ) { $WSStatsConfig = $config->get( 'WSStats' ); @@ -61,7 +62,7 @@ public static function getConfigSetting( string $name ) { * * @return mixed */ - public static function getPageTitleFromID( $id ) { + public static function getPageTitleFromID( $id ) : mixed { $title = Title::newFromID( $id ); if ( is_null( $title ) ) { return null; @@ -69,7 +70,13 @@ public static function getPageTitleFromID( $id ) { return $title->getFullText(); } - public static function validateDate( $date, $format = 'Y-m-d H:i:s' ) { + /** + * @param string $date + * @param string $format + * + * @return bool + */ + public static function validateDate( string $date, string $format = 'Y-m-d H:i:s' ) { if ( strpos( $date, ' ' @@ -151,6 +158,8 @@ public static function addToAdminLinks( \ALTree &$adminLinksTree ) { * When running maintenance update with will add the database tables * * @param [type] $updater [description] + * + * @throws \MWException */ public static function addTables( $updater ) { $dbt = $updater->getDB()->getType(); @@ -187,16 +196,11 @@ public static function addTables( $updater ) { */ public static function getViewsPerPage( int $id, $dates = false, $type = false, bool $unique = false ) { global $wgDBprefix; - switch ( $type ) { - case "only anonymous": - $dbType = "user_id = 0 "; - break; - case "only user": - $dbType = "user_id <> 0 "; - break; - default: - $dbType = false; - } + $dbType = match ( $type ) { + "only anonymous" => "user_id = 0 ", + "only user" => "user_id <> 0 ", + default => false, + }; $cnt = '*'; if ( $unique ) { $cnt = 'DISTINCT(user_id)'; @@ -271,7 +275,7 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, $selectOptions ); $dbResult = $res->fetchRow(); - if ( ! isset( $dbResult['count'] ) || empty( $dbResult['count'] ) ) { + if ( !isset( $dbResult['count'] ) || empty( $dbResult['count'] ) ) { return 0; } else { return $dbResult['count']; @@ -361,23 +365,16 @@ public static function getMostViewedPages( $data = ""; if ( $res->numRows() > 0 ) { $renderMethod = new WSStatsExport(); - switch ( $render ) { - case "table": - $data = $renderMethod->renderTable( $res, $pId ); - break; - case "csv": - $data = $renderMethod->renderCSV( $res, $pId ); - break; - case "wsarrays": - $data = $renderMethod->renderWSArrays( - $res, - $variable, - $pId - ); - break; - default: - $data = ""; - } + $data = match ( $render ) { + "table" => $renderMethod->renderTable( $res, + $pId ), + "csv" => $renderMethod->renderCSV( $res, + $pId ), + "wsarrays" => $renderMethod->renderWSArrays( $res, + $variable, + $pId ), + default => "", + }; } return $data; @@ -498,11 +495,7 @@ private static function skipAnonymous(): bool { public static function onBeforePageDisplay( outputPage &$output, Skin &$skin ): bool { $user = RequestContext::getMain()->getUser(); - if ( isset( $_SERVER['HTTP_REFERER'] ) ) { - $ref = $_SERVER['HTTP_REFERER']; - } else { - $ref = false; - } + $ref = $_SERVER['HTTP_REFERER'] ?? false; if ( self::countAllUserGroups() ) { return true; @@ -541,7 +534,7 @@ public static function onBeforePageDisplay( outputPage &$output, Skin &$skin ): * * @return int|mixed|string */ - public static function wsstats( Parser &$parser ) { + public static function wsstats( Parser &$parser ) : mixed { $options = WSStatsHooks::extractOptions( array_slice( func_get_args(), @@ -651,9 +644,15 @@ public static function wsstats( Parser &$parser ) { return "ok, move along. Nothing to see here.."; } - private static function deleteRecord( $table, $pId ): bool { - $dbw = wfGetDB( DB_MASTER ); - $dbw->IngoreErrors = true; + /** + * @param string $table + * @param int|string $pId + * + * @return bool + */ + private static function deleteRecord( string $table, int|string $pId ): bool { + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + $dbw = $lb->getConnectionRef( DB_PRIMARY ); try { $res = $dbw->delete( $table, @@ -680,8 +679,8 @@ private static function deleteRecord( $table, $pId ): bool { * @return bool */ public static function insertRecord( string $table, array $vals ): bool { - $dbw = wfGetDB( DB_MASTER ); - $dbw->IngoreErrors = true; + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + $dbw = $lb->getConnectionRef( DB_PRIMARY ); try { $res = $dbw->insert( $table, @@ -707,7 +706,7 @@ public static function insertRecord( string $table, array $vals ): bool { * associative array in form [name] => value. If no = is provided, * true is assumed like this: [name] => true * - * @param array string $options + * @param array $options * * @return array $results */ diff --git a/src/export/WSStatsExport.php b/src/export/WSStatsExport.php index 3c2b535..8ee2b90 100644 --- a/src/export/WSStatsExport.php +++ b/src/export/WSStatsExport.php @@ -63,7 +63,7 @@ public function renderTable( \Wikimedia\Rdbms\IResultWrapper $q, int $pId ) : st * * @return string */ - public function renderCSV( \Wikimedia\Rdbms\IResultWrapper $q, $pId ) : string { + public function renderCSV( \Wikimedia\Rdbms\IResultWrapper $q, $pId ): string { $data = ''; if ( $pId === 0 ) { while ( $row = $q->fetchRow() ) { @@ -86,7 +86,7 @@ public function renderCSV( \Wikimedia\Rdbms\IResultWrapper $q, $pId ) : string { * * @return mixed */ - private function extensionInstalled( $name ) { + private function extensionInstalled( string $name ) { return extensionRegistry::getInstance()->isLoaded( $name ); } @@ -101,7 +101,7 @@ public function renderWSArrays( \Wikimedia\Rdbms\IResultWrapper $q, string $wsArrayVariableName, int $pId - ) : string { + ): string { global $IP; if ( !$this->extensionInstalled( 'WSArrays' ) ) { return ""; diff --git a/src/specials/SpecialWSStats.php b/src/specials/SpecialWSStats.php index 7a25620..5299ceb 100644 --- a/src/specials/SpecialWSStats.php +++ b/src/specials/SpecialWSStats.php @@ -3,6 +3,7 @@ namespace WSStats\specials; use SpecialPage; +use WSStats\WSStatsHooks; /** * Overview for the WSStats extension @@ -16,16 +17,17 @@ public function __construct() { } /** - * Show the page to the user + * @param null|string $sub * - * @param string $sub The subpage string argument (if any). + * @return string */ public function execute( $sub ) { $out = $this->getOutput(); $out->setPageTitle( "WSStats" ); - $out->addHTML( '

Soon..

' ); + $out->addWikiMsg( 'wsstats-special-list' ); + $out->addWikiTextAsContent( WSStatsHooks::getMostViewedPages() ); - return; + return ''; } } From 4016c5174ccea7452968d1e057df12f0657e03a6 Mon Sep 17 00:00:00 2001 From: Charlot Date: Tue, 26 Sep 2023 11:48:49 +0200 Subject: [PATCH 02/10] rel1_39 start --- sql/WSStats.mysql | 1 + sql/WSStatsAddTitle.mysql | 2 ++ src/WSStatsHooks.php | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 sql/WSStatsAddTitle.mysql diff --git a/sql/WSStats.mysql b/sql/WSStats.mysql index f4c0fc0..faa09a6 100644 --- a/sql/WSStats.mysql +++ b/sql/WSStats.mysql @@ -3,4 +3,5 @@ CREATE TABLE /*_*/WSPS ( `page_id` int(11) NOT NULL default '0', `user_id` int(11) NOT NULL default '0', `added` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP + `title` varchar(250) NOT NULL DEFAULT '' ) /*$wgDBTableOptions*/; diff --git a/sql/WSStatsAddTitle.mysql b/sql/WSStatsAddTitle.mysql new file mode 100644 index 0000000..af3598e --- /dev/null +++ b/sql/WSStatsAddTitle.mysql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/WSPS + ADD COLUMN title VARCHAR(250) NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index 3194ee3..3350534 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -182,6 +182,9 @@ public static function addTables( $updater ) { throw new \MWException( "WSStats does not support $dbt." ); } + $updater->addExtensionField( self::DBTABLE, 'title', __DIR__ . "/../sql/WSStatsAddTitle.$dbt" ); + + return true; } From 7ab24424641af4432b562ad7bc390bf06cca00f6 Mon Sep 17 00:00:00 2001 From: designburo Date: Tue, 26 Sep 2023 22:12:03 +0200 Subject: [PATCH 03/10] Further on wsstats --- extension.json | 4 ++-- sql/WSStats.mysql | 1 + sql/WSStatsAddSpecialBool.mysql | 2 ++ src/WSStatsHooks.php | 7 ++++++- 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 sql/WSStatsAddSpecialBool.mysql diff --git a/extension.json b/extension.json index 66c5926..107e96a 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "WSStats", - "version": "1.1.0", + "version": "2.0.0", "author": [ "Sen-Sai" ], @@ -19,7 +19,7 @@ "skip_user_groups": [ "sysop" ], - "count_all_usergroups": true, + "countSpecialPages" : true, "ignore_in_url": [ "action=edit", "veaction=edit" diff --git a/sql/WSStats.mysql b/sql/WSStats.mysql index faa09a6..ebaf5b5 100644 --- a/sql/WSStats.mysql +++ b/sql/WSStats.mysql @@ -4,4 +4,5 @@ CREATE TABLE /*_*/WSPS ( `user_id` int(11) NOT NULL default '0', `added` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP `title` varchar(250) NOT NULL DEFAULT '' + `isSpecialPage` INT(1) NOT NULL DEFAULT '0' ) /*$wgDBTableOptions*/; diff --git a/sql/WSStatsAddSpecialBool.mysql b/sql/WSStatsAddSpecialBool.mysql new file mode 100644 index 0000000..c3ff906 --- /dev/null +++ b/sql/WSStatsAddSpecialBool.mysql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/WSPS + ADD COLUMN isSpecialPage INT(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index 3350534..a6c619e 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -183,6 +183,7 @@ public static function addTables( $updater ) { } $updater->addExtensionField( self::DBTABLE, 'title', __DIR__ . "/../sql/WSStatsAddTitle.$dbt" ); + $updater->addExtensionField( self::DBTABLE, 'isSpecialPage', __DIR__ . "/../sql/WSStatsAddSpecialBool.$dbt" ); return true; @@ -520,9 +521,13 @@ public static function onBeforePageDisplay( outputPage &$output, Skin &$skin ): if ( $title === null ) { return true; } + if ( $title->isSpecialPage() ) { + $data['isSpecialPage'] = 1; + } + $data['title'] = $title->getFullText(); $data['page_id'] = $title->getArticleID(); - if ( $data['page_id'] != 0 ) { + if ( $data['page_id'] != 0 || $title->isSpecialPage() ) { WSStatsHooks::insertRecord( self::DBTABLE, $data From f495e0b05ce8e56b3ab890cb50ddf0709ce9ead3 Mon Sep 17 00:00:00 2001 From: Charlot Date: Wed, 27 Sep 2023 11:26:41 +0200 Subject: [PATCH 04/10] rel1_39 Added Lusa function --- extension.json | 3 +- src/Helpers/SelectionMaker.php | 130 +++++++++++++++++++++ src/Scribunto/ScribuntoLuaLibrary.php | 160 ++++++++++++++++++++++++++ src/Scribunto/mw.wsstats.lua | 54 +++++++++ src/WSStatsHooks.php | 140 +++++++++++----------- src/export/WSStatsExport.php | 64 +++++++++-- 6 files changed, 466 insertions(+), 85 deletions(-) create mode 100644 src/Helpers/SelectionMaker.php create mode 100644 src/Scribunto/ScribuntoLuaLibrary.php create mode 100644 src/Scribunto/mw.wsstats.lua diff --git a/extension.json b/extension.json index 107e96a..f2212b6 100644 --- a/extension.json +++ b/extension.json @@ -34,7 +34,8 @@ "ParserFirstCallInit": "WSStats\\WSStatsHooks::onParserFirstCallInit", "BeforePageDisplay": "WSStats\\WSStatsHooks::onBeforePageDisplay", "LoadExtensionSchemaUpdates": "WSStats\\WSStatsHooks::addTables", - "AdminLinks": "WSStats\\WSStatsHooks::addToAdminLinks" + "AdminLinks": "WSStats\\WSStatsHooks::addToAdminLinks", + "ScribuntoExternalLibraries": "\\WSStats\\WSStatsHooks::onScribuntoExternalLibraries" }, "MessagesDirs": { "WSStats": [ diff --git a/src/Helpers/SelectionMaker.php b/src/Helpers/SelectionMaker.php new file mode 100644 index 0000000..2567692 --- /dev/null +++ b/src/Helpers/SelectionMaker.php @@ -0,0 +1,130 @@ += \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' ]; + } else { + $selectConditions = [ "page_id = " . $id, + 'added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' ]; + } + } else { + if ( $countSpecialPages && $title !== '' && $id == "0" ) { + $selectConditions = [ "title = " . $title, + $dbType, + 'added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' ]; + } else { + $selectConditions = [ "page_id = " . $id, + $dbType, + 'added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' ]; + } + } + } + + return $selectConditions; + } + + /** + * @param string|bool $startDate + * @param string|bool $endDate + * + * @return array + */ + public function setDatesArray( string|bool $startDate, string|bool $endDate ): array { + $dates = []; + $dates['b'] = $startDate; + $dates['e'] = $endDate; + if ( $dates['b'] !== false && WSStatsHooks::validateDate( $dates['b'] ) === false ) { + $dates['b'] = false; + } + if ( $dates['e'] !== false && WSStatsHooks::validateDate( $dates['e'] ) === false ) { + $dates['e'] = false; + } + return $dates; + } + + /** + * @param array $dates + * + * @return array|false + */ + public function checkDates( array $dates ): bool|array { + if ( $dates['e'] === false && $dates['b'] !== false ) { + $dates['e'] = false; + } + if ( $dates['b'] === false && $dates['e'] !== false ) { + $dates = false; + } + if ( $dates['b'] === false && $dates['e'] === false ) { + $dates = false; + } + return $dates; + } +} \ No newline at end of file diff --git a/src/Scribunto/ScribuntoLuaLibrary.php b/src/Scribunto/ScribuntoLuaLibrary.php new file mode 100644 index 0000000..28578aa --- /dev/null +++ b/src/Scribunto/ScribuntoLuaLibrary.php @@ -0,0 +1,160 @@ + [ $this, 'wsstat' ], + 'wsstats' => [ $this, 'wsstats' ] + ]; + + $this->getEngine()->registerInterface( __DIR__ . '/' . 'mw.wsstats.lua', $interfaceFuncs, [] ); + } + + /** + * This mirrors the functionality of the #wsstats parser function and makes it available + * in Lua. This function will return a table. + * @param string|null $id + * @param string|null $unique + * @param string|null $startDate + * @param string|null $endDate + * @param string|null $limit + * @param string|null $title + * + * @return array + */ + public function wsstats( + ?string $id, + ?string $unique, + ?string $startDate, + ?string $endDate, + ?string $limit, + ?string $title + ): array { + if ( $id === null ) { + $id = 0; + } + + if ( $title === null ) { + $title = ''; + } + if ( $limit === null ) { + $limit = 10; + } + $format = 'lua'; + + if ( $unique === null ) { + $unique = false; + } else { + $unique = true; + } + $selectionMaker = new SelectionMaker(); + if ( $startDate === null ) { + $startDate = false; + } + if ( $endDate === null ) { + $endDate = false; + } + $dates = $selectionMaker->setDatesArray( $startDate, + $endDate ); + $dates = $selectionMaker->checkDates( $dates ); + $data = WSStatsHooks::getMostViewedPages( + $dates, + $format, + $unique, + '', + $limit, + $id, + $title + ); + + return [ $this->convertToLuaTable( $data ) ]; + } + + /** + * Returns the content model of the specified slot. + * + * @param string $slotName + * @param string|null $pageName + * @return array + * @throws MWException + */ + public function slotContentModel( string $slotName, ?string $pageName = null ): array { + $wikiPage = $this->getWikiPage( $pageName ); + + if ( !$wikiPage ) { + return [ null ]; + } + + if ( !$this->userCan( $wikiPage ) ) { + // The user is not allowed to read the page + return [ null ]; + } + + $contentObject = WSSlots::getSlotContent( $wikiPage, $slotName ); + + if ( !$contentObject instanceof TextContent ) { + return [ null ]; + } + + return [ $contentObject->getModel() ]; + } + + /** + * @param WikiPage $wikiPage + * + * @return bool + */ + private function userCan( WikiPage $wikiPage ): bool { + // Only do a check for user rights when not in cli mode + if ( PHP_SAPI === 'cli' ) { + return true; + } + + return MediaWikiServices::getInstance()->getPermissionManager()->userCan( + 'read', + RequestContext::getMain()->getUser(), + $wikiPage->getTitle() + ); + } + + /** + * @param $array + * @return mixed + */ + private function convertToLuaTable( $array ) { + if ( is_array( $array ) ) { + foreach ( $array as $key => $value ) { + $array[$key] = $this->convertToLuaTable( $value ); + } + + array_unshift( $array, '' ); + unset( $array[0] ); + } + + return $array; + } +} diff --git a/src/Scribunto/mw.wsstats.lua b/src/Scribunto/mw.wsstats.lua new file mode 100644 index 0000000..3a80d73 --- /dev/null +++ b/src/Scribunto/mw.wsstats.lua @@ -0,0 +1,54 @@ +-- Variable instantiation +local wsstats = {} +local php + +function wsstats.setupInterface() + -- Interface setup + wsstats.setupInterface = nil + php = mw_interface + mw_interface = nil + + -- Register library within the "mw.slots" namespace + mw = mw or {} + mw.wsstats = wsstats + + package.loaded['mw.wsstats'] = wsstats +end + +-- slotContent +function wsstats.slotContent( slotName, pageName ) + if not type( slotName ) == 'string' or not type( pageName ) == 'string' or not type( pageName ) == 'nil' then + error( 'Invalid parameter type supplied to mw.slots.slotContent()' ) + end + + return php.slotContent( slotName, pageName ) +end + +-- slotTemplates +function wsstats.slotTemplates( slotName, pageName ) + if not type( slotName ) == 'string' or not type( pageName ) == 'string' or not type( pageName ) == 'nil' then + error( 'Invalid parameter type supplied to mw.slots.slotTemplates()' ) + end + + return php.slotTemplates( slotName, pageName ) +end + +-- slotContentModel +function wsstats.slotContentModel( slotName, pageName ) + if not type( slotName ) == 'string' or not type( pageName ) == 'string' or not type( pageName ) == 'nil' then + error( 'Invalid parameter type supplied to mw.slots.slotContentModel()' ) + end + + return php.slotContentModel( slotName, pageName ) +end + +-- slotData +function wsstats.slotData( slotName, pageName ) + if not type( slotName ) == 'string' or not type( pageName ) == 'string' or not type( pageName ) == 'nil' then + error( 'Invalid parameter type supplied to mw.slots.slotContentModel()' ) + end + + return php.slotData( slotName, pageName ) +end + +return slots diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index a6c619e..2c1e44e 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -12,6 +12,8 @@ use Exception; use Parser, Title, ALTree, OutputPage, Skin, WSStats\export\WSStatsExport, MediaWiki\MediaWikiServices; use RequestContext; +use WSSlots\Scribunto\ScribuntoLuaLibrary; +use WSStats\Helpers\SelectionMaker; if ( ! defined( 'MEDIAWIKI' ) ) { die( 'This file is a MediaWiki extension, it is not a valid entry point' ); @@ -195,10 +197,11 @@ public static function addTables( $updater ) { * @param array|false $dates * @param string|false $type * @param bool $unique + * @param string $title * * @return int|mixed */ - public static function getViewsPerPage( int $id, $dates = false, $type = false, bool $unique = false ) { + public static function getViewsPerPage( int $id, $dates = false, $type = false, bool $unique = false, string $title = '' ) { global $wgDBprefix; $dbType = match ( $type ) { "only anonymous" => "user_id = 0 ", @@ -215,6 +218,8 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, $dbResult = array(); $selectWhat = [ 'page_id', + 'title', + 'isSpecialPage', "count" => 'COUNT(' . $cnt . ')' ]; $selectOptions = [ @@ -224,54 +229,17 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, ]; $selectConditions = array(); + $selectionMaker = new SelectionMaker(); + if ( $dates === false ) { // Set Conditions - if ( ! $dbType ) { - $selectConditions = [ - "page_id = " . $id - ]; - } else { - $selectConditions = [ - "page_id = " . $id, - $dbType - ]; - } + $selectConditions = $selectionMaker->createSelectionNoDates( $id, $title, $dbType ); //$sql = 'SELECT page_id, COUNT(' . $cnt . ') AS count FROM ' . $wgDBprefix . 'WSPS WHERE page_id=\'' . $id . '\' ' . $dbType . 'GROUP BY page_id ORDER BY count DESC LIMIT 1'; } else { - if ( $dates['e'] === false ) { - // Set Conditions - if ( ! $dbType ) { - $selectConditions = [ - "page_id = " . $id, - 'added BETWEEN \'' . $dates["b"] . '\' AND NOW()' - ]; - } else { - $selectConditions = [ - "page_id = " . $id, - $dbType, - 'added BETWEEN \'' . $dates["b"] . '\' AND NOW()' - ]; - } - //$sql = 'SELECT page_id, COUNT(' . $cnt . ') AS count FROM ' . $wgDBprefix . 'WSPS WHERE page_id=\'' . $id . '\' ' . $dbType . 'AND added BETWEEN \'' . $dates["b"] . '\' AND NOW()'; - } else { - // Set Conditions - if ( ! $dbType ) { - $selectConditions = [ - "page_id = " . $id, - 'added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' - ]; - } else { - $selectConditions = [ - "page_id = " . $id, - $dbType, - 'added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\'' - ]; - } - //$sql = 'SELECT page_id, COUNT(' . $cnt . ') AS count FROM ' . $wgDBprefix . 'WSPS WHERE page_id=\'' . $id . '\' ' . $dbType . 'AND added >= \'' . $dates["b"] . '\' AND added <= \'' . $dates['e'] . '\' GROUP BY page_id ORDER BY COUNT DESC LIMIT 1'; - } + $selectConditions = $selectionMaker->createSelectionUsingDates( $id, $title, $dbType, $dates ); } - $res = $dbr->select( + $res = $dbr->select( $wgDBprefix . self::DBTABLE, $selectWhat, $selectConditions, @@ -292,7 +260,8 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, * @param bool $unique * @param string $variable * @param int $limit - * @param int $limit + * @param int $pId + * @param string $pTitle * * @return string */ @@ -302,7 +271,8 @@ public static function getMostViewedPages( bool $unique = false, string $variable = "", int $limit = 10, - int $pId = 0 + int $pId = 0, + string $pTitle = '' ): string { global $wgDBprefix; @@ -319,6 +289,8 @@ public static function getMostViewedPages( if ( $pId === 0 ) { $selectWhat = [ 'page_id', + 'title', + 'isSpecialPage', "count" => 'COUNT(' . $cnt . ')' ]; $selectOptions = [ @@ -329,6 +301,8 @@ public static function getMostViewedPages( } else { $selectWhat = [ 'page_id', + 'title', + 'isSpecialPage', 'Date' => 'DATE(added)', "count" => 'COUNT(' . $cnt . ')' ]; @@ -345,6 +319,10 @@ public static function getMostViewedPages( $selectConditions[] = "page_id = '" . $pId . "'"; } + if ( $pTitle !== '' ) { + $selectConditions[] = "title = '" . $pTitle . "'"; + } + if ( $dates === false ) { //$sql = 'SELECT page_id, COUNT(' . $cnt . ') AS count FROM ' . $wgDBprefix . 'WSPS GROUP BY page_id ORDER BY count DESC LIMIT ' . $limit; @@ -368,6 +346,7 @@ public static function getMostViewedPages( $data = ""; if ( $res->numRows() > 0 ) { + $renderMethod = new WSStatsExport(); $data = match ( $render ) { "table" => $renderMethod->renderTable( $res, @@ -377,6 +356,8 @@ public static function getMostViewedPages( "wsarrays" => $renderMethod->renderWSArrays( $res, $variable, $pId ), + "lua" =>$renderMethod->renderLua( $res, + $pId ), default => "", }; } @@ -384,6 +365,26 @@ public static function getMostViewedPages( return $data; } + /** + * Allow extensions to add libraries to Scribunto. + * + * @link https://www.mediawiki.org/wiki/Extension:Scribunto/Hooks/ScribuntoExternalLibraries + * + * @param string $engine + * @param array &$extraLibraries + * @return bool + */ + public static function onScribuntoExternalLibraries( string $engine, array &$extraLibraries ): bool { + if ( $engine !== 'lua' ) { + // Don't mess with other engines + return true; + } + + $extraLibraries['wsstats'] = ScribuntoLuaLibrary::class; + + return true; + } + /** * @param array $options * @param string $k @@ -562,25 +563,27 @@ public static function wsstats( Parser &$parser ) : mixed { if ( $limit === 0 ) { $limit = 10; } - $dates = array(); - $dates['b'] = WSStatsHooks::getOptionSetting( + $selectionMaker = new SelectionMaker(); + $startDate = WSStatsHooks::getOptionSetting( $options, 'start date' ); - $dates['e'] = WSStatsHooks::getOptionSetting( + $endDate = WSStatsHooks::getOptionSetting( $options, 'end date' ); - if ( $dates['b'] !== false && self::validateDate( $dates['b'] ) === false ) { - $dates['b'] = false; - } - if ( $dates['e'] !== false && self::validateDate( $dates['e'] ) === false ) { - $dates['e'] = false; - } + $dates = $selectionMaker->setDatesArray( $startDate, $endDate ); $pid = WSStatsHooks::getOptionSetting( $options, 'id' ); + $pTitle = WSStatsHooks::getOptionSetting( + $options, + 'title' + ); + if ( $pTitle === false ) { + $pTitle = ''; + } $pid = intval( $pid ); if ( isset( $options['stats'] ) ) { $wsArrayName = ""; @@ -601,46 +604,31 @@ public static function wsstats( Parser &$parser ) : mixed { $format = 'table'; } } - if ( $dates['e'] === false && $dates['b'] !== false ) { - $dates['e'] = false; - } - if ( $dates['b'] === false && $dates['e'] !== false ) { - $dates = false; - } - if ( $dates['b'] === false && $dates['e'] === false ) { - $dates = false; - } + $dates = $selectionMaker->checkDates( $dates ); $data = WSStatsHooks::getMostViewedPages( $dates, $format, $unique, $wsArrayName, $limit, - $pid + $pid, + $pTitle ); return $data; } - if ( $pid !== 0 ) { + if ( $pid !== 0 || ( WSStatsHooks::getConfigSetting( 'countSpecialPages' ) !== false && $pTitle !== '' ) ) { $type = WSStatsHooks::getOptionSetting( $options, 'type' ); - - if ( $dates['e'] === false && $dates['b'] !== false ) { - $dates['e'] = false; - } - if ( $dates['b'] === false && $dates['e'] !== false ) { - $dates = false; - } - if ( $dates['b'] === false && $dates['e'] === false ) { - $dates = false; - } + $dates = $selectionMaker->checkDates( $dates ); $data = WSStatsHooks::getViewsPerPage( $pid, $dates, $type, - $unique + $unique, + $pTitle ); if ( $data !== null ) { return $data; diff --git a/src/export/WSStatsExport.php b/src/export/WSStatsExport.php index 8ee2b90..ecec604 100644 --- a/src/export/WSStatsExport.php +++ b/src/export/WSStatsExport.php @@ -14,6 +14,7 @@ namespace WSStats\export; +use Wikimedia\Rdbms\IResultWrapper; use WSStats\WSStatsHooks, extensionRegistry; /** @@ -22,12 +23,21 @@ class WSStatsExport { /** - * @param \Wikimedia\Rdbms\IResultWrapper $q + * @var bool + */ + private bool $specialPages = true; + + public function __construct() { + $this->specialPages = WSStatsHooks::getConfigSetting( 'countSpecialPages' ); + } + + /** + * @param IResultWrapper $q * @param int $pId * * @return string */ - public function renderTable( \Wikimedia\Rdbms\IResultWrapper $q, int $pId ) : string { + public function renderTable( IResultWrapper $q, int $pId ): string { $data = "{| class=\"sortable wikitable smwtable jquery-tablesorter\"\n"; if ( $pId !== 0 ) { $data .= "! " . wfMessage( 'wsstats-page-date' )->text() . "\n"; @@ -38,14 +48,22 @@ public function renderTable( \Wikimedia\Rdbms\IResultWrapper $q, int $pId ) : st $data .= "! " . wfMessage( 'wsstats-page-hits' )->text() . "\n"; } while ( $row = $q->fetchRow() ) { - $pTitle = WSStatsHooks::getPageTitleFromID( $row['page_id'] ); + if ( $row['title'] === '' ) { + $pTitle = WSStatsHooks::getPageTitleFromID( $row['page_id'] ); + } else { + $pTitle = $row['title']; + } if ( !is_null( $pTitle ) ) { $data .= "|-\n"; if ( $pId !== 0 ) { $data .= "| " . $row['Date'] . "\n"; $data .= "| " . $row['count'] . "\n"; } else { - $data .= "| " . $row['page_id'] . "\n"; + if ( $row['isSpecialPage'] != "1" ) { + $data .= "| " . $row['page_id'] . "\n"; + } else { + $data .= "| \n"; + } $data .= "| " . $pTitle . "\n"; $data .= "| " . $row['count'] . "\n"; } @@ -58,16 +76,20 @@ public function renderTable( \Wikimedia\Rdbms\IResultWrapper $q, int $pId ) : st } /** - * @param \Wikimedia\Rdbms\IResultWrapper $q + * @param IResultWrapper $q * @param int $pId * * @return string */ - public function renderCSV( \Wikimedia\Rdbms\IResultWrapper $q, $pId ): string { + public function renderCSV( IResultWrapper $q, $pId ): string { $data = ''; if ( $pId === 0 ) { while ( $row = $q->fetchRow() ) { - $data .= $row['page_id'] . ";" . $row['count'] . ","; + if ( $row['page_id'] == '0' ) { + $data .= ";" . $row['title'] . $row['count'] . ","; + } else { + $data .= $row['page_id'] . ";" . $row['title'] . $row['count'] . ","; + } } } else { while ( $row = $q->fetchRow() ) { @@ -81,6 +103,32 @@ public function renderCSV( \Wikimedia\Rdbms\IResultWrapper $q, $pId ): string { ); } + /** + * @param IResultWrapper $q + * @param int $pId + * + * @return array + */ + public function renderLua( IResultWrapper $q, $pId ): array { + $result = []; + $t = 0; + while ( $row = $q->fetchRow() ) { + $pTitle = WSStatsHooks::getPageTitleFromID( $row['page_id'] ); + if ( $pTitle !== null ) { + if ( $pId === 0 ) { + $result[$t][wfMessage( 'wsstats-page-id' )->text()] = $row['page_id']; + $result[$t][wfMessage( 'wsstats-page-title' )->text()] = $pTitle; + $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; + } else { + $result[$t][wfMessage( 'wsstats-page-date' )->text()] = $row['Date']; + $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; + } + $t++; + } + } + return $result; + } + /** * @param string $name Name of the extension * @@ -98,7 +146,7 @@ private function extensionInstalled( string $name ) { * @return string */ public function renderWSArrays( - \Wikimedia\Rdbms\IResultWrapper $q, + IResultWrapper $q, string $wsArrayVariableName, int $pId ): string { From ff4a769cca6e99e573a1358830f31a4b336a4fb0 Mon Sep 17 00:00:00 2001 From: Charlot Date: Wed, 27 Sep 2023 11:46:12 +0200 Subject: [PATCH 05/10] rel1_39 Added Lua function --- src/Scribunto/ScribuntoLuaLibrary.php | 9 +------ src/Scribunto/mw.wsstats.lua | 37 +++------------------------ src/WSStatsHooks.php | 2 +- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/src/Scribunto/ScribuntoLuaLibrary.php b/src/Scribunto/ScribuntoLuaLibrary.php index 28578aa..1658e98 100644 --- a/src/Scribunto/ScribuntoLuaLibrary.php +++ b/src/Scribunto/ScribuntoLuaLibrary.php @@ -1,18 +1,12 @@ Date: Wed, 27 Sep 2023 15:33:25 +0200 Subject: [PATCH 06/10] Further on wsstats --- src/Scribunto/ScribuntoLuaLibrary.php | 69 ++++++++++++++++----------- src/WSStatsHooks.php | 8 ++-- src/export/WSStatsExport.php | 21 ++++---- src/specials/SpecialWSStats.php | 34 +++++++++++++ 4 files changed, 89 insertions(+), 43 deletions(-) diff --git a/src/Scribunto/ScribuntoLuaLibrary.php b/src/Scribunto/ScribuntoLuaLibrary.php index 1658e98..54c785b 100644 --- a/src/Scribunto/ScribuntoLuaLibrary.php +++ b/src/Scribunto/ScribuntoLuaLibrary.php @@ -30,47 +30,63 @@ public function register(): void { /** * This mirrors the functionality of the #wsstats parser function and makes it available * in Lua. This function will return a table. - * @param string|null $id - * @param string|null $unique - * @param string|null $startDate - * @param string|null $endDate - * @param string|null $limit - * @param string|null $title + * @param array $arguments * * @return array */ public function wsstats( - ?string $id, - ?string $unique, - ?string $startDate, - ?string $endDate, - ?string $limit, - ?string $title + ?array $arguments ): array { - if ( $id === null ) { + + if ( $arguments === null ) { + $arguments = []; + } + $id = WSStatsHooks::getOptionSetting( + $arguments, + 'id' + ); + + $title = WSStatsHooks::getOptionSetting( + $arguments, + 'title' + ); + + if ( $id === false ) { $id = 0; + } else { + $id = intval( $id ); } - if ( $title === null ) { + if ( $title === false ) { $title = ''; } - if ( $limit === null ) { + + $limit = WSStatsHooks::getOptionSetting( + $arguments, + 'limit' + ); + if ( $limit === false ) { $limit = 10; } $format = 'lua'; - if ( $unique === null ) { - $unique = false; - } else { - $unique = true; - } + $unique = WSStatsHooks::getOptionSetting( + $arguments, + 'unique', + false + ); $selectionMaker = new SelectionMaker(); - if ( $startDate === null ) { - $startDate = false; - } - if ( $endDate === null ) { - $endDate = false; - } + + $startDate = WSStatsHooks::getOptionSetting( + $arguments, + 'startDate' + ); + + $endDate = WSStatsHooks::getOptionSetting( + $arguments, + 'endDate' + ); + $dates = $selectionMaker->setDatesArray( $startDate, $endDate ); $dates = $selectionMaker->checkDates( $dates ); @@ -83,7 +99,6 @@ public function wsstats( $id, $title ); - return [ $this->convertToLuaTable( $data ) ]; } diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index 7ecf627..e0a843b 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -223,7 +223,7 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, "count" => 'COUNT(' . $cnt . ')' ]; $selectOptions = [ - 'GROUP BY' => 'page_id', + 'GROUP BY' => 'title', 'ORDER BY' => 'count DESC', 'LIMIT' => 1 ]; @@ -263,7 +263,7 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, * @param int $pId * @param string $pTitle * - * @return string + * @return string|array */ public static function getMostViewedPages( $dates = false, @@ -273,7 +273,7 @@ public static function getMostViewedPages( int $limit = 10, int $pId = 0, string $pTitle = '' - ): string { + ): string|array { global $wgDBprefix; $cnt = '*'; @@ -294,7 +294,7 @@ public static function getMostViewedPages( "count" => 'COUNT(' . $cnt . ')' ]; $selectOptions = [ - 'GROUP BY' => 'page_id', + 'GROUP BY' => 'title', 'ORDER BY' => 'count DESC', 'LIMIT' => $limit ]; diff --git a/src/export/WSStatsExport.php b/src/export/WSStatsExport.php index ecec604..f547985 100644 --- a/src/export/WSStatsExport.php +++ b/src/export/WSStatsExport.php @@ -113,19 +113,16 @@ public function renderLua( IResultWrapper $q, $pId ): array { $result = []; $t = 0; while ( $row = $q->fetchRow() ) { - $pTitle = WSStatsHooks::getPageTitleFromID( $row['page_id'] ); - if ( $pTitle !== null ) { - if ( $pId === 0 ) { - $result[$t][wfMessage( 'wsstats-page-id' )->text()] = $row['page_id']; - $result[$t][wfMessage( 'wsstats-page-title' )->text()] = $pTitle; - $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; - } else { - $result[$t][wfMessage( 'wsstats-page-date' )->text()] = $row['Date']; - $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; - } - $t++; + if ( $pId === 0 ) { + $result[$t][wfMessage( 'wsstats-page-id' )->text()] = $row['page_id']; + $result[$t][wfMessage( 'wsstats-page-title' )->text()] = $row['title']; + $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; + } else { + $result[$t][wfMessage( 'wsstats-page-date' )->text()] = $row['Date']; + $result[$t][wfMessage( 'wsstats-page-hits' )->text()] = $row['count']; + } + $t++; } - } return $result; } diff --git a/src/specials/SpecialWSStats.php b/src/specials/SpecialWSStats.php index 5299ceb..5bec46a 100644 --- a/src/specials/SpecialWSStats.php +++ b/src/specials/SpecialWSStats.php @@ -2,6 +2,7 @@ namespace WSStats\specials; +use MediaWiki\MediaWikiServices; use SpecialPage; use WSStats\WSStatsHooks; @@ -26,8 +27,41 @@ public function execute( $sub ) { $out->setPageTitle( "WSStats" ); $out->addWikiMsg( 'wsstats-special-list' ); $out->addWikiTextAsContent( WSStatsHooks::getMostViewedPages() ); + //$this->databaseMaintenance(); return ''; } + private function databaseMaintenance() { + // TODO: Make this a function to be called from the Special page! + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + $dbr = $lb->getConnectionRef( DB_REPLICA ); + global $wgDBprefix; + $res = $dbr->select( + $wgDBprefix . WSStatsHooks::DBTABLE, + '*', + [], + __METHOD__, + [] + ); + $result = []; + if ( $res->numRows() > 0 ) { + while ( $row = $res->fetchRow() ) { + if ( $row['page_id'] !== 0 && empty( $row['title'] ) ) { + $id = $row['id']; + $result[$id] = WSStatsHooks::getPageTitleFromID( $row['page_id'] ); + } + } + } + + + if ( !empty( $result ) ) { + $dbw = $lb->getConnectionRef( DB_PRIMARY ); + foreach( $result as $id=>$title ) { + $dbw->update( WSStatsHooks::DBTABLE, [ 'title' => $title ], [ 'id' => $id ] ); + } + } + + } + } From 14373de68d9abd974f8c9ab4a590792d1ac69397 Mon Sep 17 00:00:00 2001 From: designburo Date: Wed, 27 Sep 2023 22:19:35 +0200 Subject: [PATCH 07/10] Further on wsstats --- i18n/en.json | 5 ++- src/Helpers/SelectionMaker.php | 9 +++++ src/WSStatsHooks.php | 63 ++++++++++++++++----------------- src/specials/SpecialWSStats.php | 33 ++++++++++++++--- 4 files changed, 73 insertions(+), 37 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index 7780b66..d7434d2 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -11,5 +11,8 @@ "wsstats-page-title": "Page title", "wsstats-page-id": "Page ID", "wsstats-page-hits": "Page hits", - "wsstats-special-list": "Top visited pages of this wiki" + "wsstats-special-list": "Top visited pages of this wiki", + "wwstats-special-db-need-update": "The WSStats Database needs an update to return correct values.", + "wwstats-special-db-need-update-btn": "Click here to update $1 records in the database.", + "wwstats-special-db-need-update-result": "$1 records haven been successfully updated" } diff --git a/src/Helpers/SelectionMaker.php b/src/Helpers/SelectionMaker.php index 2567692..c491661 100644 --- a/src/Helpers/SelectionMaker.php +++ b/src/Helpers/SelectionMaker.php @@ -2,6 +2,8 @@ namespace WSStats\Helpers; +use WSStats\WSStatsHooks; + class SelectionMaker { /** @@ -101,6 +103,13 @@ public function setDatesArray( string|bool $startDate, string|bool $endDate ): a $dates = []; $dates['b'] = $startDate; $dates['e'] = $endDate; + + if ( !strpos( $dates['b'], ' ' ) ) { + $dates['b'] = $dates['b'] . ' 00:00:00'; + } + if ( !strpos( $dates['e'], ' ' ) ) { + $dates['e'] = $dates['e'] . ' 00:00:00'; + } if ( $dates['b'] !== false && WSStatsHooks::validateDate( $dates['b'] ) === false ) { $dates['b'] = false; } diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index e0a843b..4745440 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -79,38 +79,34 @@ public static function getPageTitleFromID( $id ) : mixed { * @return bool */ public static function validateDate( string $date, string $format = 'Y-m-d H:i:s' ) { - if ( strpos( - $date, - ' ' - ) ) { - // we have also a time added - $xploded = explode( - ' ', - $date - ); - $timeExploded = explode( - ":", - $xploded[1] - ); - $timeCount = count( $timeExploded ); - switch ( $timeCount ) { - case 0: - $date = $xploded[0]; - $format = 'Y-m-d'; - break; - case 1: - $format = 'Y-m-d H'; - break; - case 2: - $format = 'Y-m-d H:i'; - break; - case 3: - $format = 'Y-m-d H:i:s'; - break; - default: - $format = 'Y-m-d'; - } + // we have also a time added + $xploded = explode( + ' ', + $date + ); + $timeExploded = explode( + ":", + $xploded[1] + ); + $timeCount = count( $timeExploded ); + switch ( $timeCount ) { + case 0: + $date = $xploded[0]; + $format = 'Y-m-d'; + break; + case 1: + $format = 'Y-m-d H'; + break; + case 2: + $format = 'Y-m-d H:i'; + break; + case 3: + $format = 'Y-m-d H:i:s'; + break; + default: + $format = 'Y-m-d'; } + $d = \DateTime::createFromFormat( $format, $date @@ -239,6 +235,7 @@ public static function getViewsPerPage( int $id, $dates = false, $type = false, $selectConditions = $selectionMaker->createSelectionUsingDates( $id, $title, $dbType, $dates ); } + $res = $dbr->select( $wgDBprefix . self::DBTABLE, $selectWhat, @@ -573,6 +570,8 @@ public static function wsstats( Parser &$parser ) : mixed { 'end date' ); $dates = $selectionMaker->setDatesArray( $startDate, $endDate ); + + $pid = WSStatsHooks::getOptionSetting( $options, 'id' @@ -636,7 +635,7 @@ public static function wsstats( Parser &$parser ) : mixed { return ""; } } - + echo ""; return "ok, move along. Nothing to see here.."; } diff --git a/src/specials/SpecialWSStats.php b/src/specials/SpecialWSStats.php index 5bec46a..919a294 100644 --- a/src/specials/SpecialWSStats.php +++ b/src/specials/SpecialWSStats.php @@ -26,15 +26,32 @@ public function execute( $sub ) { $out = $this->getOutput(); $out->setPageTitle( "WSStats" ); $out->addWikiMsg( 'wsstats-special-list' ); + + if ( isset ( $_POST['doDBUpdate'] ) ) { + $result = $this->doDatabaseMaintenance(); + $out->addHTML( '

' . wfMessage( 'wwstats-special-db-need-update-result', $result ) ); + $out->addHTML( '

' ); + } + $result = $this->getRowsForMaintenance(); + if ( !empty( $result ) ) { + $out->addWikiMsg( 'wwstats-special-db-need-update' ); + $form = '
'; + $form .= '
'; + $out->addHTML( $form ); + } $out->addWikiTextAsContent( WSStatsHooks::getMostViewedPages() ); //$this->databaseMaintenance(); return ''; } - private function databaseMaintenance() { - // TODO: Make this a function to be called from the Special page! - $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); + + /** + * @return array + */ + private function getRowsForMaintenance(): array { + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); $dbr = $lb->getConnectionRef( DB_REPLICA ); global $wgDBprefix; $res = $dbr->select( @@ -53,14 +70,22 @@ private function databaseMaintenance() { } } } + return $result; + } - + /** + * @return int + */ + private function doDatabaseMaintenance(): int { + $result = $this->getRowsForMaintenance(); if ( !empty( $result ) ) { + $lb = MediaWikiServices::getInstance()->getDBLoadBalancer(); $dbw = $lb->getConnectionRef( DB_PRIMARY ); foreach( $result as $id=>$title ) { $dbw->update( WSStatsHooks::DBTABLE, [ 'title' => $title ], [ 'id' => $id ] ); } } + return count( $result ); } From 34d36c5e56e97dc0519465614f453636bf78e518 Mon Sep 17 00:00:00 2001 From: Charlot Date: Thu, 28 Sep 2023 08:55:21 +0200 Subject: [PATCH 08/10] rel1_39 Added 2nd Lua function --- src/Helpers/SelectionMaker.php | 4 +- src/Scribunto/ScribuntoLuaLibrary.php | 137 +++++++++++++++----------- src/Scribunto/mw.wsstats.lua | 5 + src/WSStatsHooks.php | 1 - 4 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/Helpers/SelectionMaker.php b/src/Helpers/SelectionMaker.php index c491661..63019aa 100644 --- a/src/Helpers/SelectionMaker.php +++ b/src/Helpers/SelectionMaker.php @@ -104,10 +104,10 @@ public function setDatesArray( string|bool $startDate, string|bool $endDate ): a $dates['b'] = $startDate; $dates['e'] = $endDate; - if ( !strpos( $dates['b'], ' ' ) ) { + if ( $dates['b'] !== false && !strpos( $dates['b'], ' ' ) ) { $dates['b'] = $dates['b'] . ' 00:00:00'; } - if ( !strpos( $dates['e'], ' ' ) ) { + if ( $dates['e'] !== false && !strpos( $dates['e'], ' ' ) ) { $dates['e'] = $dates['e'] . ' 00:00:00'; } if ( $dates['b'] !== false && WSStatsHooks::validateDate( $dates['b'] ) === false ) { diff --git a/src/Scribunto/ScribuntoLuaLibrary.php b/src/Scribunto/ScribuntoLuaLibrary.php index 54c785b..06ace0f 100644 --- a/src/Scribunto/ScribuntoLuaLibrary.php +++ b/src/Scribunto/ScribuntoLuaLibrary.php @@ -2,11 +2,6 @@ namespace WSStats\Scribunto; -use Error; -use FormatJson; -use JsonContent; -use MediaWiki\MediaWikiServices; -use MWException; use WSStats\Helpers\SelectionMaker; use WSStats\WSStatsHooks; @@ -30,11 +25,11 @@ public function register(): void { /** * This mirrors the functionality of the #wsstats parser function and makes it available * in Lua. This function will return a table. - * @param array $arguments + * @param ?array $arguments * * @return array */ - public function wsstats( + public function wsstat( ?array $arguments ): array { @@ -61,15 +56,13 @@ public function wsstats( $title = ''; } - $limit = WSStatsHooks::getOptionSetting( + $limit = WSStatsHooks::getOptionSetting( $arguments, 'limit' ); if ( $limit === false ) { $limit = 10; } - $format = 'lua'; - $unique = WSStatsHooks::getOptionSetting( $arguments, 'unique', @@ -77,83 +70,113 @@ public function wsstats( ); $selectionMaker = new SelectionMaker(); - $startDate = WSStatsHooks::getOptionSetting( + $startDate = WSStatsHooks::getOptionSetting( $arguments, 'startDate' ); - $endDate = WSStatsHooks::getOptionSetting( + $endDate = WSStatsHooks::getOptionSetting( $arguments, 'endDate' ); - $dates = $selectionMaker->setDatesArray( $startDate, - $endDate ); + $dates = $selectionMaker->setDatesArray( $startDate, $endDate ); $dates = $selectionMaker->checkDates( $dates ); - $data = WSStatsHooks::getMostViewedPages( - $dates, - $format, - $unique, - '', - $limit, - $id, - $title - ); - return [ $this->convertToLuaTable( $data ) ]; + $ret = ''; + if ( $id !== 0 || ( WSStatsHooks::getConfigSetting( 'countSpecialPages' ) !== false && $title !== '' ) ) { + $type = WSStatsHooks::getOptionSetting( $arguments, + 'type' ); + $data = WSStatsHooks::getViewsPerPage( $id, + $dates, + $type, + $unique, + $title ); + if ( $data !== null ) { + $ret = $data; + } + } + return [ $ret ]; } /** - * Returns the content model of the specified slot. + * This mirrors the functionality of the #wsstats parser function and makes it available + * in Lua. This function will return a table. + * @param ?array $arguments * - * @param string $slotName - * @param string|null $pageName * @return array - * @throws MWException */ - public function slotContentModel( string $slotName, ?string $pageName = null ): array { - $wikiPage = $this->getWikiPage( $pageName ); - - if ( !$wikiPage ) { - return [ null ]; + public function wsstats( + ?array $arguments + ): array { + if ( $arguments === null ) { + $arguments = []; } + $id = WSStatsHooks::getOptionSetting( + $arguments, + 'id' + ); - if ( !$this->userCan( $wikiPage ) ) { - // The user is not allowed to read the page - return [ null ]; + $title = WSStatsHooks::getOptionSetting( + $arguments, + 'title' + ); + + if ( $id === false ) { + $id = 0; + } else { + $id = intval( $id ); } - $contentObject = WSSlots::getSlotContent( $wikiPage, $slotName ); + if ( $title === false ) { + $title = ''; + } - if ( !$contentObject instanceof TextContent ) { - return [ null ]; + $limit = WSStatsHooks::getOptionSetting( + $arguments, + 'limit' + ); + if ( $limit === false ) { + $limit = 10; } + $format = 'lua'; - return [ $contentObject->getModel() ]; - } + $unique = WSStatsHooks::getOptionSetting( + $arguments, + 'unique', + false + ); + $selectionMaker = new SelectionMaker(); - /** - * @param WikiPage $wikiPage - * - * @return bool - */ - private function userCan( WikiPage $wikiPage ): bool { - // Only do a check for user rights when not in cli mode - if ( PHP_SAPI === 'cli' ) { - return true; - } + $startDate = WSStatsHooks::getOptionSetting( + $arguments, + 'startDate' + ); - return MediaWikiServices::getInstance()->getPermissionManager()->userCan( - 'read', - RequestContext::getMain()->getUser(), - $wikiPage->getTitle() + $endDate = WSStatsHooks::getOptionSetting( + $arguments, + 'endDate' ); + + $dates = $selectionMaker->setDatesArray( $startDate, + $endDate ); + $dates = $selectionMaker->checkDates( $dates ); + $data = WSStatsHooks::getMostViewedPages( + $dates, + $format, + $unique, + '', + $limit, + $id, + $title + ); + return [ $this->convertToLuaTable( $data ) ]; } /** - * @param $array + * @param mixed $array * @return mixed */ - private function convertToLuaTable( $array ) { + private function convertToLuaTable( mixed $array ) { if ( is_array( $array ) ) { foreach ( $array as $key => $value ) { $array[$key] = $this->convertToLuaTable( $value ); diff --git a/src/Scribunto/mw.wsstats.lua b/src/Scribunto/mw.wsstats.lua index fe4049e..b5d417d 100644 --- a/src/Scribunto/mw.wsstats.lua +++ b/src/Scribunto/mw.wsstats.lua @@ -21,5 +21,10 @@ function wsstats.wsstats( id, unique, startDate, endDate, limit, title ) return php.wsstats( id, unique, startDate, endDate, limit, title ) end +function wsstats.wsstat( id, unique, startDate, endDate, limit, title ) + + return php.wsstat( id, unique, startDate, endDate, limit, title ) +end + return wsstats diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index 4745440..6c527ac 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -635,7 +635,6 @@ public static function wsstats( Parser &$parser ) : mixed { return ""; } } - echo ""; return "ok, move along. Nothing to see here.."; } From 5b96dd57930cbe38f78a5d93705b40ff3859dcde Mon Sep 17 00:00:00 2001 From: Charlot Date: Thu, 28 Sep 2023 10:14:39 +0200 Subject: [PATCH 09/10] rel1_39 readme updated --- README.md | 85 +++++++++++++++++++++++++-- src/Helpers/SelectionMaker.php | 11 ++-- src/Scribunto/ScribuntoLuaLibrary.php | 8 +-- src/Scribunto/mw.wsstats.lua | 8 +-- src/WSStatsHooks.php | 6 +- 5 files changed, 97 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 31eb0e8..2f78840 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # WSStats This MediaWiki 1.39.x extension counts pageviews by user +* Version 2.0.0 : REL 1.39 only. Added statistics for Special Pages. Lua equivalent functions for statistics. Special Page added. * Version 1.0.8 : Removed global references * Version 1.0.7 : Added statistics over time for pages * Version 1.0.6 : Fixed path to sql tables @@ -22,7 +23,7 @@ This MediaWiki 1.39.x extension counts pageviews by user * Version 0.1.2 : Skip usergroup results * Version 0.1.1 : Initial release -##Installation +## Installation Create a folder called WSStats in the MediaWiki extensions folder. Copy the content from bitbucket inside this new folder. @@ -36,7 +37,11 @@ Run the [update script](https://www.mediawiki.org/wiki/Manual:Update.php) which Navigate to Special:Version on your wiki to verify that the extension is successfully installed. -#Configuration +## Upgrading +If you are upgrading from a version before 2.0, then you must visit the Special Page : Special:WSStats. If the database tables need updating, it will show you there and you can update the tables. +You will have to update the tables for statistics to be accurate. This is because we now allow to get statistics based on page titles, while the previous version of WSStats did not store titles. + +# Configuration By default Anonymous users and sysops are skipped from stats recording. To change this add following to LocalSettings.php.. @@ -50,6 +55,12 @@ Allow statistics for anonymous users: $wgWSStats['skip_anonymous'] = false; ```` +By default Special Pages are counted as well. To omit Special pages set the following : +```` +# Special Pages statistics +$wgWSStats['countSpecialPages'] = false; // defaults to true +```` + To skip users in certain groups, just add the groupname to "skip_user_groups" : ```` # Record anonymous users @@ -71,15 +82,27 @@ $wgWSStats['ignore_in_url'][] = 'Template:Test'; $wgWSStats['ignore_in_url'][] = 'action=edit'; ```` -#Using the parser function +# Using the parser function To retrieve statistics you can use the following parser functions : + #### Ask number of hits for page id : 9868 This returns a number ``` {{#wsstats:id=9868}} ``` +You can also ask for statistics based on title, instead of Page Id's. +#### Ask number of hits for page with title : Main Page +This returns a number +``` +{{#wsstats:title=Main Page}} +``` + +About dates. Dates are in a format of YYYY-MM-DD. Internally WSStats works with Date and Time. +This means a date of **2023-10-30** will internally become **2023-10-30 00:00:00**. You can also search by date and time. See the example about this. + + #### Ask number of hits for page id : 714 since start date 2018-02-01 This returns a number ``` @@ -95,12 +118,21 @@ This returns a number |end date=2018-02-08}} ``` +#### You can also get statistics based on date and time Get number of hits for page id : 714 from start date 2023-10-30 14:00:00 and end date 2023-10-30 16:00:00 +This returns a number +``` +{{#wsstats:id=714 +|start date=2023-10-30 14:00:00 +|end date=2023-10-30 16:00:00 +}} +``` + #### Filter results on registered users or anonymous users This returns a number ``` {{#wsstats:id=714 -|start date=2018-02-01 -|end date=2018-02-08 +|start date=2023-10-30 14:00:00 +|end date=2023-10-30 16:00:00 |type=only anonymous}} ``` @@ -179,4 +211,47 @@ This returns a table {{#wsstats:stats |unique |limit=20}} +``` + +# Using the lua functions +New in version 2.0 + +There are two Lua function you can use. + +For the parser function that returns a table ( {{#wsstats:stats.. ) you can use wsstats.stats(). + +For the parser function that returns a number ( {{wsstats:... ) you can use wsstats.stat(); + +All the arguments are the same as for the parser functions, except : + +* start date = startDate +* end date = endDate + +### Example + +If you create a Module called WSStats and you add the following content : +```lua +local p = {} + +function p.stats(frame) + stats = mw.wsstats.stats( frame.args ) + return stats + +end + +function p.stat(frame) + stat = mw.wsstats.stat( frame.args ) + return stat + +end + +return p +``` + +You can then do calls like : +``` +{{#invoke:wsstats|stats}} // returns a Lua table +``` +``` +{{#invoke:wsstats|stat|id=1|startDate=2023-09-25|endDate=2023-09-26}} ``` \ No newline at end of file diff --git a/src/Helpers/SelectionMaker.php b/src/Helpers/SelectionMaker.php index 63019aa..1cd760a 100644 --- a/src/Helpers/SelectionMaker.php +++ b/src/Helpers/SelectionMaker.php @@ -13,19 +13,18 @@ class SelectionMaker { * * @return array */ - public function createSelectionNoDates( int $id, string $title, string|bool $dbType ) : array { + public function createSelectionNoDates( int $id, string $title, string|bool $dbType ): array { // Set Conditions $countSpecialPages = WSStatsHooks::getConfigSetting( 'countSpecialPages' ); if ( !$dbType ) { if ( $countSpecialPages && $title !== '' && $id == "0" ) { - $selectConditions = [ "title = " . $title ]; + $selectConditions = [ "title = '" . $title . "'" ]; } else { $selectConditions = [ "page_id = " . $id ]; } } else { if ( $countSpecialPages && $title !== '' && $id == "0" ) { - $selectConditions = [ "title = " . $title, - $dbType ]; + $selectConditions = [ "title = '" . $title . "'", $dbType ]; } else { $selectConditions = [ "page_id = " . $id, $dbType ]; @@ -49,7 +48,7 @@ public function createSelectionUsingDates( int $id, string $title, string|bool $ // Set Conditions if ( !$dbType ) { if ( $countSpecialPages && $title !== '' && $id == "0" ) { - $selectConditions = [ "title = " . $title, + $selectConditions = [ "title = '" . $title . "'", 'added BETWEEN \'' . $dates["b"] . '\' AND NOW()' ]; } else { $selectConditions = [ "page_id = " . $id, @@ -57,7 +56,7 @@ public function createSelectionUsingDates( int $id, string $title, string|bool $ } } else { if ( $countSpecialPages && $title !== '' && $id == "0" ) { - $selectConditions = [ "title = " . $title, + $selectConditions = [ "title = '" . $title . "'", $dbType, 'added BETWEEN \'' . $dates["b"] . '\' AND NOW()' ]; } else { diff --git a/src/Scribunto/ScribuntoLuaLibrary.php b/src/Scribunto/ScribuntoLuaLibrary.php index 06ace0f..9ea38ad 100644 --- a/src/Scribunto/ScribuntoLuaLibrary.php +++ b/src/Scribunto/ScribuntoLuaLibrary.php @@ -15,8 +15,8 @@ class ScribuntoLuaLibrary extends \Scribunto_LuaLibraryBase { */ public function register(): void { $interfaceFuncs = [ - 'wsstat' => [ $this, 'wsstat' ], - 'wsstats' => [ $this, 'wsstats' ] + 'stat' => [ $this, 'stat' ], + 'stats' => [ $this, 'stats' ] ]; $this->getEngine()->registerInterface( __DIR__ . '/' . 'mw.wsstats.lua', $interfaceFuncs, [] ); @@ -29,7 +29,7 @@ public function register(): void { * * @return array */ - public function wsstat( + public function stat( ?array $arguments ): array { @@ -105,7 +105,7 @@ public function wsstat( * * @return array */ - public function wsstats( + public function stats( ?array $arguments ): array { if ( $arguments === null ) { diff --git a/src/Scribunto/mw.wsstats.lua b/src/Scribunto/mw.wsstats.lua index b5d417d..3d8a5f4 100644 --- a/src/Scribunto/mw.wsstats.lua +++ b/src/Scribunto/mw.wsstats.lua @@ -16,14 +16,14 @@ function wsstats.setupInterface() end -- wsstats stats -function wsstats.wsstats( id, unique, startDate, endDate, limit, title ) +function wsstats.stats( id, unique, startDate, endDate, limit, title ) - return php.wsstats( id, unique, startDate, endDate, limit, title ) + return php.stats( id, unique, startDate, endDate, limit, title ) end -function wsstats.wsstat( id, unique, startDate, endDate, limit, title ) +function wsstats.stat( id, unique, startDate, endDate, limit, title ) - return php.wsstat( id, unique, startDate, endDate, limit, title ) + return php.stat( id, unique, startDate, endDate, limit, title ) end diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index 6c527ac..ef9e720 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -541,12 +541,14 @@ public static function onBeforePageDisplay( outputPage &$output, Skin &$skin ): * @return int|mixed|string */ public static function wsstats( Parser &$parser ) : mixed { + $options = WSStatsHooks::extractOptions( array_slice( func_get_args(), 1 ) ); + $unique = WSStatsHooks::getOptionSetting( $options, 'unique', @@ -714,11 +716,11 @@ public static function extractOptions( array $options ) { ); if ( $pair[0] !== '//' ) { if ( count( $pair ) === 2 ) { - $name = strtolower( trim( $pair[0] ) ); + $name = trim( $pair[0] ); if ( $name == 'template' ) { $value = trim( $pair[1] ); } else { - $value = strtolower( trim( $pair[1] ) ); + $value = trim( $pair[1] ); } $results[ $name ] = $value; From c0d65ddc3532f9cb1fa7536f3ccd80d7a2cc1bc7 Mon Sep 17 00:00:00 2001 From: Charlot Date: Thu, 28 Sep 2023 10:39:32 +0200 Subject: [PATCH 10/10] Pull Request 6 changes --- composer.json | 2 +- i18n/en.json | 6 +++--- src/WSStatsHooks.php | 2 +- src/specials/SpecialWSStats.php | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 745374d..72e09a3 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "keywords": ["mediawiki", "wsstats", "extension", "statistics", "views"], "license": "GPL-2.0-or-later", "require": { - "php": "^7.0", + "php": "^8.0", "ext-zip": "*" }, "require-dev": { diff --git a/i18n/en.json b/i18n/en.json index d7434d2..9d4fa7a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -12,7 +12,7 @@ "wsstats-page-id": "Page ID", "wsstats-page-hits": "Page hits", "wsstats-special-list": "Top visited pages of this wiki", - "wwstats-special-db-need-update": "The WSStats Database needs an update to return correct values.", - "wwstats-special-db-need-update-btn": "Click here to update $1 records in the database.", - "wwstats-special-db-need-update-result": "$1 records haven been successfully updated" + "wsstats-special-db-need-update": "The WSStats Database needs an update to return correct values.", + "wsstats-special-db-need-update-btn": "Click here to update $1 records in the database.", + "wsstats-special-db-need-update-result": "$1 records haven been successfully updated" } diff --git a/src/WSStatsHooks.php b/src/WSStatsHooks.php index ef9e720..40b99ce 100644 --- a/src/WSStatsHooks.php +++ b/src/WSStatsHooks.php @@ -363,7 +363,7 @@ public static function getMostViewedPages( } /** - * Allow extensions to add libraries to Scribunto. + * Add wsstats library to Scribunto. * * @link https://www.mediawiki.org/wiki/Extension:Scribunto/Hooks/ScribuntoExternalLibraries * diff --git a/src/specials/SpecialWSStats.php b/src/specials/SpecialWSStats.php index 919a294..17cb0c3 100644 --- a/src/specials/SpecialWSStats.php +++ b/src/specials/SpecialWSStats.php @@ -29,15 +29,15 @@ public function execute( $sub ) { if ( isset ( $_POST['doDBUpdate'] ) ) { $result = $this->doDatabaseMaintenance(); - $out->addHTML( '

' . wfMessage( 'wwstats-special-db-need-update-result', $result ) ); + $out->addHTML( '

' . wfMessage( 'wsstats-special-db-need-update-result', $result ) ); $out->addHTML( '

' ); } $result = $this->getRowsForMaintenance(); if ( !empty( $result ) ) { - $out->addWikiMsg( 'wwstats-special-db-need-update' ); + $out->addWikiMsg( 'wsstats-special-db-need-update' ); $form = '
'; $form .= '
'; + $form .= 'value="'. wfMessage( 'wsstats-special-db-need-update-btn', count( $result ) ) . '">'; $out->addHTML( $form ); } $out->addWikiTextAsContent( WSStatsHooks::getMostViewedPages() );