jenkins-bot has submitted this change and it was merged. Change subject: UsageTracker: initial commit and reference implementation ......................................................................
UsageTracker: initial commit and reference implementation UsageTracker collects statistics about how often a certain feature (based on tag, page property, database, etc) is used. As collecting this information may be resource intense, all data collection is deferred to jobQueue. There is also a maintenance script to add the respective jobs. Extensions can register via the hook BSUsageTrackerRegisterCollectors. See hooks.txt for an example. Patch Set 2: Implemented CR by RV Patch Set 3: remove vendor files Patch Set 4: Updated .gitignore Patch Set 5: Implemented CR by Raimond Change-Id: I6aea22899ce0fed7d156ab9eb05ea1376d50b380 --- M .gitignore M Blog/Blog.class.php M Blog/i18n/en.json M Blog/i18n/qqq.json M BlueSpiceExtensions.default.php A UsageTracker/UsageTracker.class.php A UsageTracker/UsageTracker.setup.php A UsageTracker/composer.json A UsageTracker/db/mysql/UsageTracker.sql A UsageTracker/doc/hooks.txt A UsageTracker/extension.json A UsageTracker/i18n/en.json A UsageTracker/i18n/qqq.json A UsageTracker/maintenance/usageTrackerUpdate.php A UsageTracker/src/CollectorResult.php A UsageTracker/src/collectors/Base.php A UsageTracker/src/collectors/Database.php A UsageTracker/src/collectors/Property.php A UsageTracker/src/collectors/Tag.php A UsageTracker/src/jobs/UsageTrackerCollectJob.php A UsageTracker/src/specials/SpecialUsageTracker.alias.php A UsageTracker/src/specials/SpecialUsageTracker.php 22 files changed, 710 insertions(+), 2 deletions(-) Approvals: Robert Vogel: Looks good to me, approved Raimond Spekking: Looks good to me, but someone else must approve jenkins-bot: Verified diff --git a/.gitignore b/.gitignore index 3bbc034..916057b 100755 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ html/ latex/ InsertLink/vendor/src/build/ +UsageTracker/vendor/ \ No newline at end of file diff --git a/Blog/Blog.class.php b/Blog/Blog.class.php index 33c2572..a7eb955 100644 --- a/Blog/Blog.class.php +++ b/Blog/Blog.class.php @@ -54,6 +54,7 @@ $this->setHook( 'BSTopMenuBarCustomizerRegisterNavigationSites' ); $this->setHook( 'PageContentSaveComplete' ); $this->setHook( 'ArticleDeleteComplete' ); + $this->setHook( 'BSUsageTrackerRegisterCollectors' ); // Trackback is not fully functional in MW and thus disabled. BsConfig::registerVar( 'MW::Blog::ShowTrackback', false, BsConfig::LEVEL_PRIVATE|BsConfig::TYPE_BOOL ); @@ -426,6 +427,9 @@ if ( $aData !== false ) { return $aData; } + + $parser->getOutput()->setProperty( 'bs-tag-blog', 1 ); + // initialize local variables $oErrorListView = new ViewTagErrorList( $this ); BsExtensionManager::setContext( 'MW::Blog::ShowBlog' ); @@ -873,4 +877,24 @@ return $set; } + public function onBSUsageTrackerRegisterCollectors( &$aCollectorsConfig ) { + $aCollectorsConfig['bs:blog'] = array( + 'class' => 'Property', + 'config' => array( + 'identifier' => 'bs-tag-blog' + ) + ); + $aCollectorsConfig['bs:blog:new'] = array( + 'class' => 'Tag', + 'config' => array( + 'identifier' => 'bs:blog:new' + ) + ); + $aCollectorsConfig['bs:blog:more'] = array( + 'class' => 'Tag', + 'config' => array( + 'identifier' => 'bs:blog:more' + ) + ); + } } diff --git a/Blog/i18n/en.json b/Blog/i18n/en.json index 3a66fa2..bc5435b 100644 --- a/Blog/i18n/en.json +++ b/Blog/i18n/en.json @@ -31,5 +31,6 @@ "bs-blog-tag-blog-desc": "Includes a weblog of article, discussion and file pages. Can be specified by various parameters. These can be combined in any order.", "bs-blog-tag-blogmore-desc": "Defines length of teaser for blog post. Set within the post at the end of the intended teaser. As a result, the link \"{{int:bs-blog-read-more}}\" appears, which links to the whole blog article.", "bs-blog-tag-blogtime-desc": "Inserts a self defined time stamp for display and sorting in blog posts. The format is YYYYMMDDHHmm.", - "bs-blog-tag-blogtime-err": "The format has to be YYYYMMDDHHmm i.e. for 12. Jan. 2013 15:43 the timestamps looks like: 201301121543" + "bs-blog-tag-blogtime-err": "The format has to be YYYYMMDDHHmm i.e. for 12. Jan. 2013 15:43 the timestamps looks like: 201301121543", + "bs-tag-blog": "the blog tag" } diff --git a/Blog/i18n/qqq.json b/Blog/i18n/qqq.json index 67d6ebe..971e5b2 100644 --- a/Blog/i18n/qqq.json +++ b/Blog/i18n/qqq.json @@ -34,5 +34,6 @@ "bs-blog-tag-blog-desc": "Used in InsertMagic extension, tag description for display blog entries on every page.\n{{Related|Bs-tag-desc}}", "bs-blog-tag-blogmore-desc": "Used in InsertMagic extension, tag description for setting teaser boundary on every page.\n{{Related|Bs-tag-desc}}", "bs-blog-tag-blogtime-desc": "Used in InsertMagic extension, tag description for setting blog timestamp on every page.\n{{Related|Bs-tag-desc}}", - "bs-blog-tag-blogtime-err": "Error message on bs:blog:time tag when there is a wrong timestamp format.\nShould be YYYYMMDDHHmm (YmdHi)." + "bs-blog-tag-blogtime-err": "Error message on bs:blog:time tag when there is a wrong timestamp format.\nShould be YYYYMMDDHHmm (YmdHi).", + "bs-tag-blog": "Name of the descriptor used in [[Special:UsageTracker|Special:UsageTracker]] output" } diff --git a/BlueSpiceExtensions.default.php b/BlueSpiceExtensions.default.php index 8ee5145..23769a7 100644 --- a/BlueSpiceExtensions.default.php +++ b/BlueSpiceExtensions.default.php @@ -35,6 +35,7 @@ require_once( __DIR__."/SecureFileStore/SecureFileStore.setup.php" ); require_once( __DIR__."/SmartList/SmartList.setup.php" ); require_once( __DIR__."/TopMenuBarCustomizer/TopMenuBarCustomizer.setup.php" ); +require_once( __DIR__."/UsageTracker/UsageTracker.setup.php" ); require_once( __DIR__."/UserPreferences/UserPreferences.setup.php" ); require_once( __DIR__."/UserSidebar/UserSidebar.setup.php" ); require_once( __DIR__."/WatchList/WatchList.setup.php" ); diff --git a/UsageTracker/UsageTracker.class.php b/UsageTracker/UsageTracker.class.php new file mode 100644 index 0000000..37e42ec --- /dev/null +++ b/UsageTracker/UsageTracker.class.php @@ -0,0 +1,182 @@ +<?php + +/** + * BlueSpice for MediaWiki + * Extension: UsageTracker + * Description: + * Authors: Markus Glaser + * + * Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * For further information visit http://www.bluespice.com + * @author Your Name <gla...@hallowelt.com> + * @version 2.27.0 + * @package BlueSpice_Extensions + * @subpackage Usage Tracker + * @copyright Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved. + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or later + * @filesource + */ + +class UsageTracker extends BsExtensionMW { + + /** + * Contains the configuration for collectors + * @var array Config array + */ + public $aCollectorsConfig = array(); + + /** + * Contains all potential collectors + * @var array Object array of BS\\UsageTracker\\Collectors\\Base + */ + protected $aCollectors = array(); + + /** + * Basic initialisation of extension, e.g. hooks, permissions, etc. + */ + public function initExt() { + $this->mCore->registerPermission( 'usagetracker-update', [ 'sysop' ], [ 'type' => 'global' ] ); + } + + /** + * Collects usage data from one or several collectors. If $aConfig is not set + * it fetches all collectors and adds them to job queue. If $aConfig is set, + * it actually collects from the collectors set in config (typically invoked + * from job queue and only one collector) + * @param array $aConfig + * @return BS\UsageTracker\CollectorResult[] + */ + public function getUsageData( $aConfig = null ) { + $this->initializeCollectors( $aConfig ); + + // If there is no specific collector, register all known collectors and + // add them to job queue for deferred collecting + if ( is_null( $aConfig ) ) { + foreach ( $this->aCollectors as $oCollector ) { + $oCollector->registerJob(); + } + return true; + } + + foreach ( $this->aCollectors as $oCollector ) { + $aData[] = $oCollector->getUsageData( $aConfig ); + } + + // Store collected data in DB for future access + $dbw = wfGetDB( DB_MASTER ); + foreach ( $aData as $oData ) { + // Each usage number is only stored once. So delete any old values first. + $dbw->delete( + 'bs_usagetracker', + ['ut_identifier' => $oData->identifier] + ); + // Update the count + $dbw->insert( + 'bs_usagetracker', + [ + 'ut_identifier' => $oData->identifier, + 'ut_count' => $oData->count, + 'ut_type' => $oData->type, + 'ut_timestamp' => wfTimestampNow() + ], + __METHOD__ + ); + } + + return $aData; + } + + /** + * Load existing data from the database instead of collecting it on the fly, + * as collecting data might be very ressource intense. + * @param array $aConfig + * @return BS\UsageTracker\CollectorResult[] + */ + public function getUsageDataFromDB( $aConfig = null ) { + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + 'bs_usagetracker', + [ + 'ut_identifier', + 'ut_count', + 'ut_type', + 'ut_timestamp' + ], + [], + __METHOD__, + ['ORDER BY' => 'ut_identifier'] + ); + $aData = array(); + while( $oRow = $dbr->fetchObject( $res ) ) { + $aData[] = BS\UsageTracker\CollectorResult::newFromDBRow( $oRow ); + } + return $aData; + } + + /** + * Gets all available collector if $aConfig is null, otherwise uses collectors + * as given in config + * @param array $aConfig + * @return boolean + */ + protected function initializeCollectors( $aConfig = null ) { + + if ( is_null( $aConfig ) ) { + // Get all the collectors definitions + Hooks::run( 'BSUsageTrackerRegisterCollectors', array( &$this->aCollectorsConfig ) ); + } else { + $this->aCollectorsConfig = array(); + $this->aCollectorsConfig[] = $aConfig; + } + + // Instantiate all collectors from definitions + // Check if class exists and inherits from Base as configs may + // contain typos and deprecated declarations. + foreach ( $this->aCollectorsConfig as $aCollectorConfig ) { + if ( strpos( $aCollectorConfig['class'], "\\" ) === false ) { + $classname = "BS\\UsageTracker\\Collectors\\" . $aCollectorConfig['class']; + } + if ( class_exists( $classname ) ) { + $oCollector = new $classname( $aCollectorConfig['config'] ); + if ( $oCollector instanceof BS\UsageTracker\Collectors\Base ) { + $this->aCollectors[] = new $classname( $aCollectorConfig ); + } else { + wfDebugLog( "BSUsageTracker", "Class $classname must be inherited from Base" ); + } + } else { + wfDebugLog( "BSUsageTracker", "Class $classname must does not exist" ); + } + } + + return true; + } + + /** + * Adds the table to the database + * @param DatabaseUpdater $updater + * @return boolean Always true to keep hook running + */ + public static function getSchemaUpdates( $updater ) { + $updater->addExtensionTable( + 'bs_usagetracker', + __DIR__ .'/db/mysql/UsageTracker.sql' + ); + return true; + } +} \ No newline at end of file diff --git a/UsageTracker/UsageTracker.setup.php b/UsageTracker/UsageTracker.setup.php new file mode 100644 index 0000000..783f78d --- /dev/null +++ b/UsageTracker/UsageTracker.setup.php @@ -0,0 +1,3 @@ +<?php + +wfLoadExtension( 'BlueSpiceExtensions/UsageTracker' ); \ No newline at end of file diff --git a/UsageTracker/composer.json b/UsageTracker/composer.json new file mode 100644 index 0000000..65876f4 --- /dev/null +++ b/UsageTracker/composer.json @@ -0,0 +1,8 @@ +{ + "autoload" : { + "files": [ "UsageTracker.class.php" ], + "psr-4": { + "BS\\UsageTracker\\" : "src" + } + } +} \ No newline at end of file diff --git a/UsageTracker/db/mysql/UsageTracker.sql b/UsageTracker/db/mysql/UsageTracker.sql new file mode 100644 index 0000000..fa63989 --- /dev/null +++ b/UsageTracker/db/mysql/UsageTracker.sql @@ -0,0 +1,19 @@ +-- Database definition for UsageTracker +-- +-- Part of BlueSpice for MediaWiki +-- +-- @author Markus Glaser <gla...@hallowelt.com> + +-- @package BlueSpice_Extensions +-- @subpackage UsageTracker +-- @copyright Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved. +-- @license http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or later +-- @filesource + +CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/bs_usagetracker( + `ut_id` int(5) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `ut_identifier` varchar(255) NOT NULL default '', + `ut_count` int(10) unsigned NOT NULL default 0, + `ut_type` varchar(255) NOT NULL default '', + `ut_timestamp` varchar(16) NOT NULL default '' +)/*$wgDBTableOptions*/; \ No newline at end of file diff --git a/UsageTracker/doc/hooks.txt b/UsageTracker/doc/hooks.txt new file mode 100644 index 0000000..df9a14d --- /dev/null +++ b/UsageTracker/doc/hooks.txt @@ -0,0 +1,9 @@ +'BSUsageTrackerRegisterCollectors': Register your extension's usage collector here. +$aCollectorsConfig: description of the collector. Must be an instance of +BS/UsageTracker/Collectors/Base. Example: + $aCollectorsConfig['bs:blog'] = array( + 'class' => 'Property', // Tag, Database + 'config' => array( + 'identifier' => 'tag:bs:blog' + ) + ); \ No newline at end of file diff --git a/UsageTracker/extension.json b/UsageTracker/extension.json new file mode 100644 index 0000000..3f1bd8f --- /dev/null +++ b/UsageTracker/extension.json @@ -0,0 +1,37 @@ +{ + "name": "UsageTracker", + "version": "2.27.0", + "url": "https://help.bluespice.com/index.php/UsageTracker", + "author": "Markus Glaser", + "descriptionmsg": "bs-usagetracker-desc", + "type": "bluespice", + "SpecialPages": { + "UsageTracker": "SpecialUsageTracker" + }, + "bsgExtensions": { + "UsageTracker": { + "className": "UsageTracker", + "extPath": "/BlueSpiceExtensions/UsageTracker" + } + }, + "MessagesDirs": { + "UsageTracker": [ + "i18n" + ] + }, + "ExtensionMessagesFiles": { + "UsageTrackerAlias": "src/specials/SpecialUsageTracker.alias.php" + }, + "AutoloadClasses": { + "UsageTracker": "UsageTracker.class.php", + "SpecialUsageTracker": "src/specials/SpecialUsageTracker.php" + }, + "Hooks": { + "LoadExtensionSchemaUpdates": "UsageTracker::getSchemaUpdates" + }, + "JobClasses": { + "usageTrackerCollectJob": "BS\\UsageTracker\\Jobs\\UsageTrackerCollectJob" + }, + "load_composer_autoloader" : true, + "manifest_version": 1 +} diff --git a/UsageTracker/i18n/en.json b/UsageTracker/i18n/en.json new file mode 100644 index 0000000..3fadc35 --- /dev/null +++ b/UsageTracker/i18n/en.json @@ -0,0 +1,19 @@ +{ + "@metadata": { + "authors": [ + "Markus Glaser <gla...@hallowelt.com>" + ] + }, + "bs-usagetracker-desc": "Holds statistical data about extension use", + "prefs-usagetracker": "Usage Tracker", + "usagetracker": "Usage Tracker", + "bs-usagetracker-startjobs": "Update now", + "bs-usagetracker-create-statistics": "Update usage statistics", + "bs-usagetracker-col-identifier": "Identifier", + "bs-usagetracker-col-desc" : "Description", + "bs-usagetracker-col-count" : "Count", + "bs-usagetracker-col-last-updated" : "Last updated", + "bs-usagetracker-caution" : "Be aware: Updating usage statistics may take some time and resources. Only trigger the update when you really, really need current data. The update is done via deferred jobs, so you might have to reload several times until the data is fully collected.", + "bs-usagetracker-base-collector-desc" : "Number of pages using $1", + "bs-usagetracker-tag-collector-desc" : "Number of pages using tag <$1>" +} diff --git a/UsageTracker/i18n/qqq.json b/UsageTracker/i18n/qqq.json new file mode 100644 index 0000000..e7da5f9 --- /dev/null +++ b/UsageTracker/i18n/qqq.json @@ -0,0 +1,17 @@ +{ + "@metadata": { + "authors": [ + "Markus Glaser <gla...@hallowelt.com>" + ] + }, + "bs-usagetracker-desc": "Used in [{{canonicalurl:Special:Usage_Tracker}} Special:Usage_Tracker], description of usagetracker extension", + "prefs-usagetracker": "Used in [[Special:Usage_Tracker]], headline for UsageTracker section in preferences.", + "usagetracker": "Label for special page in list of special pages", + "bs-usagetracker-startjobs": "Used in [[Special:Usage_Tracker]], label for button to trigger update of statistics", + "bs-usagetracker-create-statistics": "Used in [[Special:Usage_Tracker]], label for fieldset to update usage statistics", + "bs-usagetracker-col-identifier": "Used in [[Special:Usage_Tracker]], label for identifier column", + "bs-usagetracker-col-desc" : "Used in [[Special:Usage_Tracker]], label for description column", + "bs-usagetracker-col-count" : "Used in [[Special:Usage_Tracker]], label for count column", + "bs-usagetracker-col-last-updated" : "Used in [[Special:Usage_Tracker]], label for last updated column", + "bs-usagetracker-caution" : "Used in [[Special:Usage_Tracker]], text to warn users not that the feature is resource intense" +} diff --git a/UsageTracker/maintenance/usageTrackerUpdate.php b/UsageTracker/maintenance/usageTrackerUpdate.php new file mode 100644 index 0000000..1495223 --- /dev/null +++ b/UsageTracker/maintenance/usageTrackerUpdate.php @@ -0,0 +1,36 @@ +<?php + +/** + * Called via commandline + * Can be run without params + * Registers usage statistics collect jobs with the job queue. In order to + * actually get the data, you need to execute maintenance/runJobs.php in + * in addition. Typical commandline: + * ?> php extensions/BlueSpiceExtensions/UsageTracker/maintenance/usagetrackerUpdate.php + * ?> php maintenance/runJobs.php + * runJobs, however, should be run on a cronjob anyways. + */ + +//We are on <mediawiki>/extensions/BlueSpiceExtensions/UsageTracker/maintenance +$IP = realpath( dirname( dirname( dirname( __DIR__ ) ) ) ); + +require_once( $IP.'/BlueSpiceFoundation/maintenance/BSMaintenance.php' ); + +class UsageTrackerUpdate extends BSMaintenance { + + public function execute() { + $aData = BsExtensionManager::getExtension( 'UsageTracker' )->getUsageData(); + } + + public function finalSetup() { + parent::finalSetup(); + $GLOBALS['wgMainCacheType'] = CACHE_NONE; + } +} + +$maintClass = 'UsageTrackerUpdate'; +if (defined('RUN_MAINTENANCE_IF_MAIN')) { + require_once( RUN_MAINTENANCE_IF_MAIN ); +} else { + require_once( DO_MAINTENANCE ); # Make this work on versions before 1.17 +} \ No newline at end of file diff --git a/UsageTracker/src/CollectorResult.php b/UsageTracker/src/CollectorResult.php new file mode 100644 index 0000000..738cdd9 --- /dev/null +++ b/UsageTracker/src/CollectorResult.php @@ -0,0 +1,44 @@ +<?php +namespace BS\UsageTracker; + +class CollectorResult { + public $count = 0; + public $descriptionKey = ''; + public $identifier = ''; + public $type = ''; + public $updateDate = ''; + + public function __construct( $oCollector=null ) { + if ( is_object( $oCollector ) && ( $oCollector instanceof Collectors\Base ) ) { + $this->descriptionKey = $oCollector->getDescriptionKey(); + $this->identifier = $oCollector->getIdentifier(); + $this->updateDate = wfTimestamp(); + $this->type = get_class( $oCollector ); + } + } + + public static function newFromDBRow( $oRow ) { + $oResult = new self(); + $oCollector = new $oRow->ut_type(); + $oResult->descriptionKey = $oCollector->getDescriptionKey(); + $oResult->identifier = $oRow->ut_identifier; + $oResult->type = $oRow->ut_type; + $oResult->updateDate = $oRow->ut_timestamp; + $oResult->count = $oRow->ut_count; + unset( $oCollector ); + return $oResult; + } + + public function getUpdateDate() { + return $this->updateDate; + } + + public function getDescription() { + return wfMessage( + $this->descriptionKey, + wfMessage( $this->identifier )->exists() + ?wfMessage( $this->identifier )->text() + :$this->identifier + )->text(); + } +} \ No newline at end of file diff --git a/UsageTracker/src/collectors/Base.php b/UsageTracker/src/collectors/Base.php new file mode 100644 index 0000000..bd1bc4f --- /dev/null +++ b/UsageTracker/src/collectors/Base.php @@ -0,0 +1,42 @@ +<?php +namespace BS\UsageTracker\Collectors; +use BS\UsageTracker\Jobs\UsageTrackerCollectJob; + +abstract class Base { + protected $identifier = 'bs:'; + protected $descKey = 'bs-usagetracker-base-collector-desc'; + + /** + * Initial configuration. Needed to register as job + * @var type + */ + protected $config = array(); + + public function __construct( $config ) { + if ( isset( $config['config'] ) && is_array( $config['config'] ) ) { + if ( isset( $config['config']['identifier'] ) ) { + $this->identifier = $config['config']['identifier']; + } + }; + $this->config = $config; + } + + public function getDescriptionKey() { + return $this->descKey; + } + + public function getIdentifier() { + return $this->identifier; + } + + abstract public function getUsageData(); + + public function registerJob() { + $oJob = new UsageTrackerCollectJob( + \Title::newFromText( $this->identifier . wfTimestampNow() ), + $this->config + ); + \JobQueueGroup::singleton()->push( $oJob ); + return true; + } +} \ No newline at end of file diff --git a/UsageTracker/src/collectors/Database.php b/UsageTracker/src/collectors/Database.php new file mode 100644 index 0000000..28814ca --- /dev/null +++ b/UsageTracker/src/collectors/Database.php @@ -0,0 +1,37 @@ +<?php +namespace BS\UsageTracker\Collectors; +class Database extends Base { + + protected $table; + protected $uniqueColumns; + + public function __construct( $config = array() ) { + parent::__construct( $config ); + if ( isset( $config['config'] ) && is_array( $config['config'] ) ) { + if ( isset( $config['config']['table'] ) ) { + $this->table = $config['config']['table']; + } + if ( isset( $config['config']['uniqueColumns'] ) ) { + $this->uniqueColumn = + is_array( $config['config']['uniqueColumns'] ) + ? $config['config']['uniqueColumns'] + : [ $config['config']['uniqueColumns'] ]; + } + }; + } + + public function getUsageData() { + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + [ $this->table ], + $this->uniqueColumns, + [], + __METHOD__, + [ "GROUP BY" => $this->uniqueColumns ] + ); + + $oRes = new \BS\UsageTracker\CollectorResult( $this ); + $oRes->count = $res->numRows(); + return $oRes; + } +} \ No newline at end of file diff --git a/UsageTracker/src/collectors/Property.php b/UsageTracker/src/collectors/Property.php new file mode 100644 index 0000000..106a3a3 --- /dev/null +++ b/UsageTracker/src/collectors/Property.php @@ -0,0 +1,22 @@ +<?php +namespace BS\UsageTracker\Collectors; +class Property extends Base { + + public function __construct( $aConfig = array() ) { + parent::__construct( $aConfig ); + } + + public function getUsageData() { + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + [ 'page_props' ], + [ 'pp_propname' ], + [ 'pp_propname' => $this->identifier ], + __METHOD__ + ); + + $oRes = new \BS\UsageTracker\CollectorResult( $this ); + $oRes->count = $res->numRows(); + return $oRes; + } +} \ No newline at end of file diff --git a/UsageTracker/src/collectors/Tag.php b/UsageTracker/src/collectors/Tag.php new file mode 100644 index 0000000..5ce2d29 --- /dev/null +++ b/UsageTracker/src/collectors/Tag.php @@ -0,0 +1,29 @@ +<?php +namespace BS\UsageTracker\Collectors; +class Tag extends Base { + + protected $descKey = 'bs-usagetracker-tag-collector-desc'; + + public function __construct( $aConfig = array() ) { + parent::__construct( $aConfig ); + } + + public function getUsageData() { + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + [ 'page', 'revision', 'text' ], + [ 'old_text' ], + [ 'old_text LIKE "%' . $this->identifier . '%"' ], + __METHOD__, + [], + array( + 'revision' => [ 'JOIN', [ 'page_latest=rev_id' ] ], + 'text' => [ 'JOIN', [ 'rev_text_id=old_id' ] ] + ) + ); + + $oRes = new \BS\UsageTracker\CollectorResult( $this ); + $oRes->count = $res->numRows(); + return $oRes; + } +} \ No newline at end of file diff --git a/UsageTracker/src/jobs/UsageTrackerCollectJob.php b/UsageTracker/src/jobs/UsageTrackerCollectJob.php new file mode 100644 index 0000000..82accbf --- /dev/null +++ b/UsageTracker/src/jobs/UsageTrackerCollectJob.php @@ -0,0 +1,34 @@ +<?php +/** + * This job is created when a usage tracker requests the usage data to be + * collected. This may be very resource intense, so the collection of data + * itself is deferred to a job. + */ +namespace BS\UsageTracker\Jobs; + +class UsageTrackerCollectJob extends \Job { + + /** + * Configuration of the job + * @var array + */ + protected $config = array(); + + /** + * @param Title $title + * @param array $params definition array for specific collector + */ + public function __construct( $title, $params ) { + parent::__construct( 'usageTrackerCollectJob', $title, $params ); + $this->config = $params; + } + + /** + * Run the job of collecting usage data for a given collector + */ + public function run() { + \BsExtensionManager::getExtension( 'UsageTracker' )->getUsageData( $this->config ); + return true; + } + +} diff --git a/UsageTracker/src/specials/SpecialUsageTracker.alias.php b/UsageTracker/src/specials/SpecialUsageTracker.alias.php new file mode 100644 index 0000000..807af2d --- /dev/null +++ b/UsageTracker/src/specials/SpecialUsageTracker.alias.php @@ -0,0 +1,25 @@ +<?php +/** + * Internationalisation file for UsageTracker special page. + * + * Part of BlueSpice for MediaWiki + * + * @author Markus Glaser <gla...@hallowelt.com> + + * @package BlueSpice_Extensions + * @subpackage UsageTracker + * @copyright Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved. + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or later + * @filesource + */ +$specialPageAliases = array(); + +/** English */ +$specialPageAliases['en'] = array( + 'UsageTracker' => array( 'UsageTracker', 'Usage Tracker' ), +); + +/** German (Deutsch) */ +$specialPageAliases['de'] = array( + 'UsageTracker' => array( 'Usage Tracker' ), +); \ No newline at end of file diff --git a/UsageTracker/src/specials/SpecialUsageTracker.php b/UsageTracker/src/specials/SpecialUsageTracker.php new file mode 100644 index 0000000..05efe33 --- /dev/null +++ b/UsageTracker/src/specials/SpecialUsageTracker.php @@ -0,0 +1,117 @@ +<?php + +/** + * Renders the Usage Tracker special page. + * + * Part of BlueSpice for MediaWiki + * + * @author Markus Glaser <gla...@hallowelt.com> + + * @package BlueSpice_Extensions + * @subpackage UsageTracker + * @copyright Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved. + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or later + * @filesource + */ + +class SpecialUsageTracker extends BsSpecialPage { + + /** + * Constructor of SpecialUsageTracker class + */ + public function __construct() { + wfProfileIn( 'BS::'.__METHOD__ ); + parent::__construct( 'UsageTracker' ); + wfProfileOut( 'BS::'.__METHOD__ ); + } + + /** + * Renders special page output. + * @param string $sParameter Not used. + * @return bool Allow other hooked methods to be executed. Always true. + */ + public function execute( $sParameter ) { + parent::execute( $sParameter ); + + $oOut = $this->getOutput(); + $oRequest = $this->getRequest(); + + // Handle update requests (in case the user has the neccesary rights) + if ( $this->getUser()->isAllowed( 'usagetracker-update') ) { + $this->showUpdateForm(); + + if ( $oRequest->wasPosted() ) { + BsExtensionManager::getExtension( 'UsageTracker' )->getUsageData(); + } + } + + // Get stored data from db + $aData = BsExtensionManager::getExtension( 'UsageTracker' )->getUsageDataFromDB(); + + // Show data in table + $sTableHtml = HTML::rawElement( "tr", array(), + HTML::element( "th", array(), wfMessage( 'bs-usagetracker-col-identifier' )->text() ). + HTML::element( "th", array(), wfMessage( 'bs-usagetracker-col-desc' )->text() ). + HTML::element( "th", array(), wfMessage( 'bs-usagetracker-col-last-updated' )->text() ). + HTML::element( "th", array(), wfMessage( 'bs-usagetracker-col-count' )->text() ) + ); + + foreach ( $aData as $oResult ) { + $sTableHtml .= $this->makeRow( $oResult ); + } + + $oOut->addHTML( + HTML::rawElement( "table", array( "class" => "sortable wikitable" ), $sTableHtml ) + ); + + return true; + } + + /** + * Renders a single result table row in HTML + * @param BS\UsageTracker\CollectorResult $oCollectorResult + * @return string HTML for a single table row + */ + protected function makeRow( BS\UsageTracker\CollectorResult $oCollectorResult ) { + $sHtml = HTML::rawElement( "tr", array(), + HTML::element( "td", [ "width" => "10%" ], $oCollectorResult->identifier ). + HTML::element( "td", array(), $oCollectorResult->getDescription() ). + HTML::element( "td", [ "align" => "right", "width" => "10%" ], $oCollectorResult->getUpdateDate() ). + HTML::rawElement( + "td", + [ "align" => "right", "width" => "10%" ], + HTML::element( "strong", array(), $oCollectorResult->count ) + ) + ); + + return $sHtml; + } + + /** + * Output a form to start collect jobs + */ + function showUpdateForm() { + $this->getOutput()->addHTML( + Html::openElement( + 'form', + [ + 'method' => 'post', + 'action' => $this->getContext()->getTitle()->getFullURL(), + 'name' => 'utjobs', + 'id' => 'bs-useagetracker-form1' + ] + ) . + Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) . + Xml::fieldset( $this->msg( 'bs-usagetracker-create-statistics' )->text() ) . + Xml::element( 'div', [], $this->msg( 'bs-usagetracker-caution' )->text() ). + Xml::submitButton( + $this->msg( + 'bs-usagetracker-startjobs' + )->text() + ) . + Html::closeElement( 'fieldset' ) . + Html::closeElement( 'form' ) . "\n" + ); + } + +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/304417 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I6aea22899ce0fed7d156ab9eb05ea1376d50b380 Gerrit-PatchSet: 5 Gerrit-Project: mediawiki/extensions/BlueSpiceExtensions Gerrit-Branch: master Gerrit-Owner: Mglaser <gla...@hallowelt.biz> Gerrit-Reviewer: Dvogel hallowelt <daniel.vo...@hallowelt.com> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Ljonka <l.verhovs...@gmail.com> Gerrit-Reviewer: Mglaser <gla...@hallowelt.biz> Gerrit-Reviewer: Pwirth <wi...@hallowelt.biz> Gerrit-Reviewer: Raimond Spekking <raimond.spekk...@gmail.com> Gerrit-Reviewer: Robert Vogel <vo...@hallowelt.biz> Gerrit-Reviewer: Siebrand <siebr...@kitano.nl> Gerrit-Reviewer: Springle <sprin...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits