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

Reply via email to