jenkins-bot has submitted this change and it was merged.
Change subject: Initial commit
......................................................................
Initial commit
Change-Id: I6e67772c1cc25092099eca1307313bd10bcd101f
---
A GoogleAnalyticsTopPages.php
A i18n/en.json
A i18n/qqq.json
A includes/GoogleAnalyticsTopPages.body.php
A includes/GoogleAnalyticsTopPages.hooks.php
A includes/api/ApiGooglePageStatsUpdate.php
A includes/specials/SpecialGoogleAnalyticsTopPages.php
A includes/sql/page_google_stats.sql
8 files changed, 415 insertions(+), 0 deletions(-)
Approvals:
Florianschmidtwelzow: Looks good to me, approved
jenkins-bot: Verified
diff --git a/GoogleAnalyticsTopPages.php b/GoogleAnalyticsTopPages.php
new file mode 100644
index 0000000..e6f90c0
--- /dev/null
+++ b/GoogleAnalyticsTopPages.php
@@ -0,0 +1,113 @@
+<?php
+ /**
+ GoogleAnalyticsTopPages License
+ Copyright (c) 2014 Florian Schmidt
+
+ Permission is hereby granted, free of charge, to any person obtaining a
copy
+ of this software and associated documentation files (the "Software"),
to deal
+ in the Software without restriction, including without limitation the
rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE
+ SOFTWARE.
+ */
+
+ if ( !defined( 'MEDIAWIKI' ) ) {
+ die( 'This is an extension for Mediawiki and can not run
standalone.' );
+ }
+
+ $wgExtensionCredits['parserhook'][] = array(
+ 'path' => __FILE__,
+ 'name' => 'GoogleAnayltcisTopPages',
+ 'author' => 'Florian Schmidt',
+ 'url' =>
'https://www.mediawiki.org/wiki/Extension:GoogleAnalyticsTopPages',
+ 'descriptionmsg' => 'googleanalyticstoppages-description',
+ 'version' => '0.0.1',
+ 'license-name' => "MIT",
+ );
+
+ $dir = __DIR__;
+
+ // Messages
+ $wgMessagesDirs['MobileFrontend'] = $dir . '/i18n';
+
+ // Autoload Classes
+ $wgAutoloadClasses[ 'GoogleAnalyticsTopPages' ] =
+ $dir . '/includes/GoogleAnalyticsTopPages.body.php';
+ $wgAutoloadClasses[ 'GoogleAnalyticsTopPagesHooks' ] =
+ $dir . '/includes/GoogleAnalyticsTopPages.hooks.php';
+ $wgAutoloadClasses[ 'SpecialGoogleAnalyticsTopPages' ] =
+ $dir . '/includes/specials/SpecialGoogleAnalyticsTopPages.php';
+ $wgAutoloadClasses[ 'ApiGooglePageStatsUpdate' ] =
+ $dir . '/includes/api/ApiGooglePageStatsUpdate.php';
+
+ // Special Page
+ $wgSpecialPageGroups[ 'GoogleAnalyticsTopPages' ] = 'other';
+ $wgSpecialPages[ 'GoogleAnalyticsTopPages' ] =
'SpecialGoogleAnalyticsTopPages';
+
+ // API Modules
+ $wgAPIModules['googlepagestatsupdate'] = 'ApiGooglePageStatsUpdate';
+
+ // Hooks
+ $wgHooks['LoadExtensionSchemaUpdates'][] =
+ 'GoogleAnalyticsTopPagesHooks::onLoadExtensionSchemaUpdates';
+ $wgHooks['ParserFirstCallInit'][] =
'GoogleAnalyticsTopPagesHooks::onParserFirstCallInit';
+
+ // Configuration variables
+
+ /**
+ * Service account name of the service account to use for api requests
(to Google Analytics API).
+ * Can be found in Google Developer console.
+ *
+ * @var string
+ */
+ $wgGATPServiceAccountName = '';
+
+ /**
+ * Absolute path to certificate file to access the service account. Can
be downloaded
+ * from Google Developer console. Be sure to put the certificate
somewhere, where only
+ * you can access it! ({wikiroot}/ isn't a good idea!)
+ *
+ * @var string
+ */
+ $wgGATPKeyFileLocation = '';
+
+ /**
+ * Optional name of this api app.
+ *
+ * @var string
+ */
+ $wgGATPAppName = "googleanalyticstoppages";
+
+ /**
+ * Google Analytics profile id to get the data from. Your service
account need to have at
+ * least read access to access the data. The profile ID isn't the
UA-XXXX string!
+ *
+ * @var string
+ */
+ $wgGATPProfileId = '';
+
+ /**
+ * Interval in days (from today) to get visitor data.
+ *
+ * @var integer
+ */
+ $wgGATPInterval = 30;
+
+ /**
+ * To protect the API against anonymous calls, you can set this to
true. You will need to
+ * transmit $wgSecretKey in your request to authenticate the call als
valid.
+ *
+ * @var boolean
+ */
+ $wgGATPProtectAPI = true;
diff --git a/i18n/en.json b/i18n/en.json
new file mode 100644
index 0000000..3882293
--- /dev/null
+++ b/i18n/en.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Florian Schmidt"
+ ]
+ },
+ "googleanalyticstoppages-description": "Provides a top pages list with
the use of Google Analytics data.",
+ "apihelp-googlepagestatsupdate-param-key": "Value of
[[mw:Manual:$wgSecretKey|$wgSecretKey]]"
+}
diff --git a/i18n/qqq.json b/i18n/qqq.json
new file mode 100644
index 0000000..a7aa6d5
--- /dev/null
+++ b/i18n/qqq.json
@@ -0,0 +1,9 @@
+{
+ "@metadata": {
+ "authors": [
+ "Florian Schmidt"
+ ]
+ },
+ "googleanalyticstoppages-description": "Description of this Extension.",
+ "apihelp-googlepagestatsupdate-param-key": "Help text for API parameter
key."
+}
diff --git a/includes/GoogleAnalyticsTopPages.body.php
b/includes/GoogleAnalyticsTopPages.body.php
new file mode 100644
index 0000000..4f2b50f
--- /dev/null
+++ b/includes/GoogleAnalyticsTopPages.body.php
@@ -0,0 +1,159 @@
+<?php
+ class GoogleAnalyticsTopPages {
+ public static function getData( WebRequest $request ) {
+ global $wgGATPServiceAccountName,
$wgGATPKeyFileLocation, $wgGATPAppName,
+ $wgGATPProfileId, $wgGATPInterval;
+
+ // create a new Google_Client object
+ $client = new Google_Client();
+ // set app name
+ $client->setApplicationName( $wgGATPAppName );
+ // Create the needed Google Analytics service object
+ $service = new Google_Service_Analytics( $client );
+
+ // check, if the client is already authenticated
+ if ( $request->getSessionData( 'service_token' ) !==
null ) {
+ $client->setAccessToken(
$request->getSessionData( 'service_token' ) );
+ }
+
+ // load the certificate key file
+ $key = file_get_contents( $wgGATPKeyFileLocation );
+ // create the service account credentials
+ $cred = new Google_Auth_AssertionCredentials(
+ $wgGATPServiceAccountName,
+ array(
'https://www.googleapis.com/auth/analytics.readonly' ),
+ $key
+ );
+ // set the credentials
+ $client->setAssertionCredentials( $cred );
+ if ( $client->getAuth()->isAccessTokenExpired() ) {
+ // authenticate the service account
+ $client->getAuth()->refreshTokenWithAssertion(
$cred );
+ }
+ // set the service_token to the session for future
requests
+ $request->setSessionData( 'service_token',
$client->getAccessToken() );
+
+ // get the start and end time for the request
+ $startTime = date( 'Y-m-d', time() - $wgGATPInterval *
86400 );
+ $endTime = date( 'Y-m-d', time() );
+
+ // do the request to Google Analytics
+ $response = $service->data_ga->get(
+ 'ga:' . $wgGATPProfileId,
+ $startTime,
+ $endTime,
+ 'ga:pageviews,ga:exits',
+ array(
+ 'dimensions' => 'ga:pagePath',
+ 'max-results' => 10,
+ 'sort' => '-ga:pageviews'
+ )
+ );
+
+ // our response array
+ $titles = array();
+ foreach( $response['rows'] as $row ) {
+ // try to get an actual title object of the
returned pagePath
+ $title = self::makeTitle( $row[0] );
+ if ( $title ) {
+ $titles[] = array(
+ 'page_title' =>
$title->getText(),
+ 'page_visitors' => $row[1]
+ );
+ }
+ }
+
+ return $titles;
+ }
+
+ public static function makeTitle( $text ) {
+ global $wgArticlePath;
+
+ // try to make a Title object without modifications
+ $title = Title::newFromText( $text );
+ if ( $title instanceof Title && $title->exists() ) {
+ return $title;
+ }
+
+ // remove $wgArticle
+ $article = str_replace( '$1', '', $wgArticlePath );
+ $text = str_replace( $article, '', $text );
+ $title = Title::newFromText( $text );
+ if ( $title instanceof Title && $title->exists() ) {
+ return $title;
+ }
+
+ // try parse_url and parse_str (maybe it's an
index.php?title=Page construct)
+ $url = parse_url( $text );
+ if ( isset( $url['query'] ) ) {
+ parse_str( $url['query'], $query );
+ if ( isset( $query['title'] ) ) {
+ $title = Title::newFromText(
$query['title'] );
+ if ( $title instanceof Title &&
$title->exists() ) {
+ return $title;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Render the content which should be added with the
gatp-parser tag
+ *
+ * @param string $input Inout between the tags
+ * @param array $args Tag arguments
+ * @param Parser $parser The parser object which is parsing the
page with the tag
+ * @param PPFrame $frame The parent frame object
+ *
+ * @return string
+ */
+ public static function renderParserTag( $input, array $args,
Parser $parser, PPFrame $frame ) {
+ $dbw = wfGetDB( DB_SLAVE );
+ $result = '';
+
+ // get the actual list of top pages
+ $res = $dbw->select(
+ 'page_google_stats',
+ array(
+ 'page_title',
+ 'page_visitors'
+ ),
+ '',
+ __METHOD__,
+ array(
+ // FIXME: Should be configurable
+ 'LIMIT' => '10',
+ 'ORDER BY' => 'page_visitors DESC'
+ )
+ );
+
+ // if there was an error or no rows, return empty string
+ if ( !$res ) {
+ return '';
+ }
+
+ // build the list of top pages
+ $result .= Html::openElement( 'ol', array( 'class' =>
'special' ) );
+ foreach( $res as $value ) {
+ $title = Title::newFromText( $value->page_title
);
+ if ( $title->exists() ) {
+ $result .= self::makeListItem( $title );
+ }
+ }
+ $result .= Html::closeElement( 'ol' );
+
+ return $result;
+ }
+
+ /**
+ * Creates a list item (<li>) with a link. Label and location
comes from the title object.
+ *
+ * @param Title $title Title object to get information from
+ * @return string Formatted HTML
+ */
+ private static function makeListItem( Title $title ) {
+ return Html::rawElement( 'li', array(),
+ Linker::linkKnown( $title, $title->getText() )
);
+ }
+ }
diff --git a/includes/GoogleAnalyticsTopPages.hooks.php
b/includes/GoogleAnalyticsTopPages.hooks.php
new file mode 100644
index 0000000..98aa44b
--- /dev/null
+++ b/includes/GoogleAnalyticsTopPages.hooks.php
@@ -0,0 +1,46 @@
+<?php
+ class GoogleAnalyticsTopPagesHooks {
+ /**
+ * LoadExtensionSchemaUpdate Hook handler
+ * @see
https://www.mediawiki.org/wiki/Manual:Hooks/LoadExtensionSchemaUpdate
+ */
+ public static function onLoadExtensionSchemaUpdates( $updater =
null ) {
+ global $wgDBname, $wgSharedDB, $wgDBtype;
+
+ // Don't create tables on a shared database
+ if(
+ !empty( $wgSharedDB ) &&
+ $wgSharedDB !== $wgDBname
+ ) {
+ return true;
+ }
+ // Tables to add to the database
+ $tables = array( 'page_google_stats' );
+ // Sql directory inside the extension folder
+ $sql = dirname( __FILE__ ) . '/sql';
+ // Extension of the table schema file (depending on the
database type)
+ switch ( $updater !== null ?
$updater->getDB()->getType() : $wgDBtype ) {
+ default:
+ $ext = 'sql';
+ }
+ // Do the updating
+ foreach ( $tables as $table ) {
+ // Location of the table schema file
+ $schema = "$sql/$table.$ext";
+ $updater->addExtensionUpdate( array(
'addTable', $table, $schema, true ) );
+ }
+ return true;
+ }
+
+ /**
+ * ParserFirstCallInit Hook handler
+ *
+ * @see
https://www.mediawiki.org/wiki/Manual:Hooks/ParserFirstCallInit
+ * @param Parser $parser The parser object this hook is called
from
+ * @return boolean
+ */
+ public static function onParserFirstCallInit( Parser $parser ) {
+ // set our parser tag
+ $parser->setHook( 'gatp',
'GoogleAnalyticsTopPages::renderParserTag' );
+ }
+ }
diff --git a/includes/api/ApiGooglePageStatsUpdate.php
b/includes/api/ApiGooglePageStatsUpdate.php
new file mode 100644
index 0000000..cc52bc9
--- /dev/null
+++ b/includes/api/ApiGooglePageStatsUpdate.php
@@ -0,0 +1,61 @@
+<?php
+ class ApiGooglePageStatsUpdate extends ApiBase {
+ public function execute() {
+ global $wgGATPProtectAPI, $wgSecretKey;
+
+ $apiResult = $this->getResult();
+ $dbw = wfGetDB( DB_MASTER );
+ $params = $this->extractRequestParams();
+ $success = 'false';
+ $tableName = 'page_google_stats';
+
+ // check, if the api is protected and if the key is
correct
+ if (
+ $wgGATPProtectAPI &&
+ $params['key'] !== $wgSecretKey
+ ) {
+ // false key
+ $text = 'Wrong key, try 42.';
+ } else {
+ // delete all rows to insert a complete new set
of data
+ $del = $dbw->delete(
+ $tableName,
+ '*'
+ );
+
+ if ( $del ) {
+ // get the actual data from Google
Analytics
+ $data =
GoogleAnalyticsTopPages::getData( $this->getRequest() );
+
+ // insert the new data
+ $res = $dbw->insert(
+ $tableName,
+ $data
+ );
+ if ( $res ) {
+ $success = 'true';
+ }
+ }
+ $text = $dbw->lastError();
+ }
+ // build result array
+ $r = array(
+ 'success' => $success,
+ 'text' => $text
+ );
+ // add result to API output
+ $apiResult->addValue( null, $this->getModuleName(), $r
);
+ }
+
+ public function mustBePosted() {
+ return true;
+ }
+
+ public function getAllowedParams() {
+ return array(
+ 'key' => array(
+ ApiBase::PARAM_TYPE => 'string',
+ ),
+ );
+ }
+ }
diff --git a/includes/specials/SpecialGoogleAnalyticsTopPages.php
b/includes/specials/SpecialGoogleAnalyticsTopPages.php
new file mode 100644
index 0000000..610048a
--- /dev/null
+++ b/includes/specials/SpecialGoogleAnalyticsTopPages.php
@@ -0,0 +1,10 @@
+<?php
+ class SpecialGoogleAnalyticsTopPages extends SpecialPage {
+ public function __construct() {
+ parent::__construct( 'GoogleAnalyticsTopPages' );
+ }
+
+ public function execute( $par ) {
+
+ }
+ }
diff --git a/includes/sql/page_google_stats.sql
b/includes/sql/page_google_stats.sql
new file mode 100644
index 0000000..601b9ec
--- /dev/null
+++ b/includes/sql/page_google_stats.sql
@@ -0,0 +1,8 @@
+--
+-- extension GoogleAnalyticsTopPages SQL schema
+--
+CREATE TABLE /*$wgDBprefix*/page_google_stats (
+ page_title varbinary(255) NOT NULL PRIMARY KEY,
+ page_visitors bigint(20) unsigned NOT NULL default 0,
+ page_latest int(10) unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
--
To view, visit https://gerrit.wikimedia.org/r/175299
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I6e67772c1cc25092099eca1307313bd10bcd101f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/GoogleAnalyticsTopPages
Gerrit-Branch: master
Gerrit-Owner: Florianschmidtwelzow <[email protected]>
Gerrit-Reviewer: Florianschmidtwelzow <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: Springle <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits