diff --git a/FEATURES/71435 b/FEATURES/71435 new file mode 100644 index 0000000000000000000000000000000000000000..4988b199dbe004ba4f193f946bea0ba0fb7af23a --- /dev/null +++ b/FEATURES/71435 @@ -0,0 +1,10 @@ + '71435' => + ['Label' => $this->_('Piwik est devenu Matomo'), + 'Desc' => '', + 'Image' => '', + 'Video' => '', + 'Category' => '', + 'Right' => function($feature_description, $user) {return true;}, + 'Wiki' => '', + 'Test' => '', + 'Date' => '2018-09-25'], \ No newline at end of file diff --git a/VERSIONS_WIP/71435 b/VERSIONS_WIP/71435 new file mode 100644 index 0000000000000000000000000000000000000000..6ee80d4af8a6106ec498195686414a26cea116af --- /dev/null +++ b/VERSIONS_WIP/71435 @@ -0,0 +1 @@ + - ticket #71435 : Piwik est devenu Matomo \ No newline at end of file diff --git a/application/modules/admin/controllers/StatController.php b/application/modules/admin/controllers/StatController.php index fa7a407d4e6a5b670ad0234a0722c85b94f5a3d0..354192f5bdca0093eebd9c881af2ab5f67926b43 100644 --- a/application/modules/admin/controllers/StatController.php +++ b/application/modules/admin/controllers/StatController.php @@ -52,21 +52,21 @@ class Admin_StatController extends ZendAfi_Controller_Action { } - public function piwikAction() { - $this->view->titre = $this->_('Tableau de bord PIWIK'); - if(!(new Class_AdminVar_Piwik())->isEnabled()) - return $this->view->error = $this->_('Veuillez renseigner la variable PIWIK_AUTH_TOKEN et JS_STAT'); + public function matomoAction() { + $this->view->titre = $this->_('Tableau de bord MATOMO'); + if(!(new Class_AdminVar_Matomo())->isEnabled()) + return $this->view->error = $this->_('Veuillez renseigner la variable MATOMO_AUTH_TOKEN et JS_STAT'); - $this->view->url = $this->_getPiwikDashboardUrl(); + $this->view->url = $this->_getMatomoDashboardUrl(); } - protected function _getPiwikDashboardUrl() { + protected function _getMatomoDashboardUrl() { $js_stat = (new Class_AdminVar_JsStat()); - $base_url = $js_stat->getPiwikUrl() . 'index.php'; - $auth_token = ['token_auth' => (new Class_AdminVar_Piwik())->getAuthToken()]; - $id_site = ['idSite' => $js_stat->getPiwikSiteId()]; + $base_url = $js_stat->getMatomoUrl() . 'index.php'; + $auth_token = ['token_auth' => (new Class_AdminVar_Matomo())->getAuthToken()]; + $id_site = ['idSite' => $js_stat->getMatomoSiteId()]; $dashboard_params = ['module' => 'Widgetize', 'action' => 'iframe', diff --git a/application/modules/admin/views/scripts/stat/matomo.phtml b/application/modules/admin/views/scripts/stat/matomo.phtml new file mode 100644 index 0000000000000000000000000000000000000000..aa926bd56d99a9c88a752ef4a5685ba392706e63 --- /dev/null +++ b/application/modules/admin/views/scripts/stat/matomo.phtml @@ -0,0 +1,19 @@ +<?php +Class_ScriptLoader::getInstance() +->addJQueryReady('var title = $(".modules h1"); + var container_height = Math.round(window.innerHeight - title.position().top - title.height()); + var container = $(".modules"); + container.height(container_height); + $("iframe#matomo").height(container_height - title.height() - Math.round(container.css("padding-bottom").replace("px","")))'); + +if($this->error) + echo $this->tag('p', $this->error, ['class' => 'error']); + +echo $this->tag('iframe', '', ['src' => $this->url, + 'id' => 'matomo', + 'frameborder' => '0', + 'marginheight' => '0', + 'marginwidth' => '0', + 'width' => '100%', + 'scrolling' => 'yes']); +?> diff --git a/application/modules/admin/views/scripts/stat/piwik.phtml b/application/modules/admin/views/scripts/stat/piwik.phtml index 068751548c7465abc25a0f8ebb7e64ee433546a0..aa926bd56d99a9c88a752ef4a5685ba392706e63 100644 --- a/application/modules/admin/views/scripts/stat/piwik.phtml +++ b/application/modules/admin/views/scripts/stat/piwik.phtml @@ -4,13 +4,13 @@ Class_ScriptLoader::getInstance() var container_height = Math.round(window.innerHeight - title.position().top - title.height()); var container = $(".modules"); container.height(container_height); - $("iframe#piwik").height(container_height - title.height() - Math.round(container.css("padding-bottom").replace("px","")))'); + $("iframe#matomo").height(container_height - title.height() - Math.round(container.css("padding-bottom").replace("px","")))'); if($this->error) echo $this->tag('p', $this->error, ['class' => 'error']); echo $this->tag('iframe', '', ['src' => $this->url, - 'id' => 'piwik', + 'id' => 'matomo', 'frameborder' => '0', 'marginheight' => '0', 'marginwidth' => '0', diff --git a/cosmogramme/sql/patch/patch_354.php b/cosmogramme/sql/patch/patch_354.php new file mode 100644 index 0000000000000000000000000000000000000000..ecf2be00d27d426c43a65aba23999cf45dbc87f8 --- /dev/null +++ b/cosmogramme/sql/patch/patch_354.php @@ -0,0 +1,10 @@ +<?php +try { + $adapter = Zend_Db_Table_Abstract::getDefaultAdapter(); + if (!$adapter->query('select valeur from bib_admin_var where clef="MATOMO_AUTH_TOKEN" and valeur<>""')->fetch()) { + $adapter->query('delete from bib_admin_var where CLEF="MATOMO_AUTH_TOKEN"'); + $adapter->query('update bib_admin_var set CLEF="MATOMO_AUTH_TOKEN" where CLEF="PIWIK_AUTH_TOKEN"'); + } + + $adapter->query('update bib_admin_var set VALEUR=replace(VALEUR, "piwik", "matomo") where CLEF="JS_STAT"'); +} catch(Exception $e) {var_dump($e);} diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index c401ec3d770e5428c4f77a8784f83471b034fc94..f5ff50d9ad0262df787040e72392dd5e9e489d27 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -385,7 +385,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader { protected function _getStatVars() { return ['JS_STAT' => Class_AdminVar_Meta::newRawText($this->_('Javascript code for statistics')), - 'PIWIK_AUTH_TOKEN' => Class_AdminVar_Meta::newDefault($this->_('PIWIK authentication token for widgets'))]; + 'MATOMO_AUTH_TOKEN' => Class_AdminVar_Meta::newDefault($this->_('MATOMO authentication token for widgets'))]; } diff --git a/library/Class/AdminVar/JsStat.php b/library/Class/AdminVar/JsStat.php index 111be79302e59164878282e420b2f61ce8472f30..27618ee1533b128710bfc887cb82f4ca19fc850a 100644 --- a/library/Class/AdminVar/JsStat.php +++ b/library/Class/AdminVar/JsStat.php @@ -22,7 +22,7 @@ class Class_AdminVar_JsStat { public function isTrackerSet() { - return $this->isGoogleAnalytics() || $this->isPiwik(); + return $this->isGoogleAnalytics() || $this->isMatomo(); } @@ -31,8 +31,8 @@ class Class_AdminVar_JsStat { } - public function isPiwik() { - return ($this->getPiwikSiteId() != ''); + public function isMatomo() { + return ($this->getMatomoSiteId() != ''); } @@ -40,7 +40,7 @@ class Class_AdminVar_JsStat { return $this->isGoogleAnalytics() ? $this->_getGoogleAnalyticsId() - : $this->getPiwikSiteId(); + : $this->getMatomoSiteId(); } @@ -49,7 +49,7 @@ class Class_AdminVar_JsStat { } - public function getPiwikUrl() { + public function getMatomoUrl() { return $this->_extractFromJsStat("/var u=.*(\/\/[^\"]+)\"/i", 1); } @@ -70,7 +70,7 @@ class Class_AdminVar_JsStat { } - public function getPiwikSiteId() { + public function getMatomoSiteId() { $script = Class_AdminVar::get('JS_STAT'); preg_match_all("/setSiteId\',([^\]]+)\]\)/i", $script, $matches); return isset($matches[1][0]) ? trim($matches[1][0], " \t\n\r\0\x0B'\"") : ''; diff --git a/library/Class/AdminVar/Piwik.php b/library/Class/AdminVar/Matomo.php similarity index 86% rename from library/Class/AdminVar/Piwik.php rename to library/Class/AdminVar/Matomo.php index 1fb11f0ad52075a1fb30e50b4b31dee5e27deb42..4161a03d9f4a6eac7d737024dce4297a03375193 100644 --- a/library/Class/AdminVar/Piwik.php +++ b/library/Class/AdminVar/Matomo.php @@ -20,16 +20,16 @@ */ -class Class_AdminVar_Piwik { +class Class_AdminVar_Matomo { public function isEnabled() { return (('' != $this->getAuthToken()) - && ('' != (new Class_AdminVar_JsStat())->getPiwikUrl())); + && ('' != (new Class_AdminVar_JsStat())->getMatomoUrl())); } public function getAuthToken() { - return Class_AdminVar::get('PIWIK_AUTH_TOKEN'); + return Class_AdminVar::get('MATOMO_AUTH_TOKEN'); } } ?> \ No newline at end of file diff --git a/library/Class/WebService/Analytics/Client.php b/library/Class/WebService/Analytics/Client.php index 705c1c1a882f63aa0d4c2d9044330a79989989df..39fb2edfbb9e277c5308c8efcd675505856c9bde 100644 --- a/library/Class/WebService/Analytics/Client.php +++ b/library/Class/WebService/Analytics/Client.php @@ -28,7 +28,7 @@ class Class_WebService_Analytics_Client { return $js_stat->isGoogleAnalytics() ? new Class_WebService_Analytics_GoogleTracker($js_stat) - : new Class_WebService_Analytics_PiwikTracker($js_stat); + : new Class_WebService_Analytics_MatomoTracker($js_stat); } diff --git a/library/Class/WebService/Analytics/PiwikTracker.php b/library/Class/WebService/Analytics/MatomoTracker.php similarity index 82% rename from library/Class/WebService/Analytics/PiwikTracker.php rename to library/Class/WebService/Analytics/MatomoTracker.php index fa336782250319ffa70f6e6046d324a45f26fc5c..600ad62456ffb59ba09c88c1e68d5cfee748f38a 100644 --- a/library/Class/WebService/Analytics/PiwikTracker.php +++ b/library/Class/WebService/Analytics/MatomoTracker.php @@ -19,14 +19,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Class_WebService_Analytics_PiwikTracker extends Class_WebService_Analytics_NullTracker { +class Class_WebService_Analytics_MatomoTracker extends Class_WebService_Analytics_NullTracker { protected $_tracker; public function __construct($config) { - require_once('piwik-php-tracker/PiwikTracker.php'); + require_once('matomo-php-tracker/PiwikTracker.php'); $this->_config = $config; $this->_tracker = new PiwikTracker($config->getTrackerId(), - $config->getPiwikUrl()); + $config->getMatomoUrl()); } @@ -36,7 +36,7 @@ class Class_WebService_Analytics_PiwikTracker extends Class_WebService_Analytics public function getUrl() { - return $this->_config->getPiwikUrl() . 'piwik.php'; + return $this->_config->getMatomoUrl() . 'matomo.php'; } diff --git a/library/ZendAfi/Acl/AdminControllerGroup.php b/library/ZendAfi/Acl/AdminControllerGroup.php index 5f83a1d122bb71dd933cd9fdd25f24f3a2e6a89f..9693cfd026d3cd79eba28aa1ba6e2cf38f2f6eab 100644 --- a/library/ZendAfi/Acl/AdminControllerGroup.php +++ b/library/ZendAfi/Acl/AdminControllerGroup.php @@ -63,7 +63,7 @@ class ZendAfi_Acl_AdminControllerGroup { 'album/dilicom' => Class_AdminVar::isDilicomPNBEnabled(), 'premier-chapitre' => Class_AdminVar::isPremierChapitreEnabled(), 'i18n' => Class_AdminVar::isTranslationEnabled(), - 'stat/piwik' => (new Class_AdminVar_Piwik())->isEnabled(), + 'stat/matomo' => (new Class_AdminVar_Matomo())->isEnabled(), 'multimedia' => Class_AdminVar::isMultimediaEnabled(), 'search-form' => Class_AdminVar::isCustomSearchFormEnabled(), ]; diff --git a/library/ZendAfi/View/Helper/Admin/ContentNav.php b/library/ZendAfi/View/Helper/Admin/ContentNav.php index d728f63b53aee7e94046b96f5a3e60c86967b457..83b6f08ece978640b4569e9b49b83d8032ecd29b 100644 --- a/library/ZendAfi/View/Helper/Admin/ContentNav.php +++ b/library/ZendAfi/View/Helper/Admin/ContentNav.php @@ -109,7 +109,7 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe public function menuStats() { return $this ->renderBloc($this->_('Statistiques'), - [['piwik_stats', $this->_('Piwik'), '/admin/stat/piwik'], + [['matomo_stats', $this->_('Matomo'), '/admin/stat/matomo'], ['failed_searches', $this->_('Recherches infructueuses'), '/admin/stat/rechercheinfructueuse'] ]); } diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php index cb3334fec4225f17b69c9c5658df26ee9c8c741b..c4a9c02fcc7d0eaa52028620cf8712e4468dff1d 100644 --- a/library/ZendAfi/View/Helper/Admin/HelpLink.php +++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php @@ -104,7 +104,7 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki { 'bib' => ['index' => 'Modifier_une_bibliothèque'], 'index' => ['index' => 'Bokeh', 'update-skin' => 'Mettre_à _jour_la_charte_graphique'], - 'stat' => ['piwik' => 'Piwik'], + 'stat' => ['matomo' => 'Matomo'], 'print' => ['index' => 'Imprimer_un_résultat_de_recherche,_une_notice_ou_des_articles'], 'file-manager' => ['index' => 'Explorateur_de_fichiers'] ]; diff --git a/library/matomo-php-tracker/.gitignore b/library/matomo-php-tracker/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..57f1cb2a7a1855e09f3a4d4bbb900ebfc4b23621 --- /dev/null +++ b/library/matomo-php-tracker/.gitignore @@ -0,0 +1 @@ +/.idea/ \ No newline at end of file diff --git a/library/matomo-php-tracker/LICENSE b/library/matomo-php-tracker/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f7cc808c382e7e791d688f4d2585dc90360e86cc --- /dev/null +++ b/library/matomo-php-tracker/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014, Piwik Open Source Analytics +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/library/matomo-php-tracker/PiwikTracker.php b/library/matomo-php-tracker/PiwikTracker.php new file mode 100644 index 0000000000000000000000000000000000000000..9e40975e3f614567a50ac01b2489a7309e71642c --- /dev/null +++ b/library/matomo-php-tracker/PiwikTracker.php @@ -0,0 +1,2049 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * For more information, see README.md + * + * @license released under BSD License http://www.opensource.org/licenses/bsd-license.php + * @link http://piwik.org/docs/tracking-api/ + * + * @category Piwik + * @package PiwikTracker + */ + +/** + * PiwikTracker implements the Piwik Tracking Web API. + * + * For more information, see README.md + * + * @package PiwikTracker + * @api + */ +class PiwikTracker +{ + /** + * Piwik base URL, for example http://example.org/piwik/ + * Must be set before using the class by calling + * PiwikTracker::$URL = 'http://yourwebsite.org/piwik/'; + * + * @var string + */ + static public $URL = ''; + + /** + * API Version + * + * @ignore + * @var int + */ + const VERSION = 1; + + /** + * @ignore + */ + public $DEBUG_APPEND_URL = ''; + + /** + * Visitor ID length + * + * @ignore + */ + const LENGTH_VISITOR_ID = 16; + + /** + * Charset + * @see setPageCharset + * @ignore + */ + const DEFAULT_CHARSET_PARAMETER_VALUES = 'utf-8'; + + /** + * See piwik.js + */ + const FIRST_PARTY_COOKIES_PREFIX = '_pk_'; + + /** + * Ecommerce item page view tracking stores item's metadata in these Custom Variables slots. + */ + const CVAR_INDEX_ECOMMERCE_ITEM_PRICE = 2; + const CVAR_INDEX_ECOMMERCE_ITEM_SKU = 3; + const CVAR_INDEX_ECOMMERCE_ITEM_NAME = 4; + const CVAR_INDEX_ECOMMERCE_ITEM_CATEGORY = 5; + + /** + * Defines how many categories can be used max when calling addEcommerceItem(). + * @var int + */ + const MAX_NUM_ECOMMERCE_ITEM_CATEGORIES = 5; + + const DEFAULT_COOKIE_PATH = '/'; + + /** + * Builds a PiwikTracker object, used to track visits, pages and Goal conversions + * for a specific website, by using the Piwik Tracking API. + * + * @param int $idSite Id site to be tracked + * @param string $apiUrl "http://example.org/piwik/" or "http://piwik.example.org/" + * If set, will overwrite PiwikTracker::$URL + */ + public function __construct($idSite, $apiUrl = '') + { + $this->ecommerceItems = array(); + $this->attributionInfo = false; + $this->eventCustomVar = false; + $this->forcedDatetime = false; + $this->forcedNewVisit = false; + $this->generationTime = false; + $this->pageCustomVar = false; + $this->customParameters = array(); + $this->customData = false; + $this->hasCookies = false; + $this->token_auth = false; + $this->userAgent = false; + $this->country = false; + $this->region = false; + $this->city = false; + $this->lat = false; + $this->long = false; + $this->width = false; + $this->height = false; + $this->plugins = false; + $this->localHour = false; + $this->localMinute = false; + $this->localSecond = false; + $this->idPageview = false; + + $this->idSite = $idSite; + $this->urlReferrer = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false; + $this->pageCharset = self::DEFAULT_CHARSET_PARAMETER_VALUES; + $this->pageUrl = self::getCurrentUrl(); + $this->ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false; + $this->acceptLanguage = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : false; + $this->userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : false; + if (!empty($apiUrl)) { + self::$URL = $apiUrl; + } + + // Life of the visitor cookie (in sec) + $this->configVisitorCookieTimeout = 33955200; // 13 months (365 + 28 days) + // Life of the session cookie (in sec) + $this->configSessionCookieTimeout = 1800; // 30 minutes + // Life of the session cookie (in sec) + $this->configReferralCookieTimeout = 15768000; // 6 months + + // Visitor Ids in order + $this->userId = false; + $this->forcedVisitorId = false; + $this->cookieVisitorId = false; + $this->randomVisitorId = false; + + $this->setNewVisitorId(); + + $this->configCookiesDisabled = false; + $this->configCookiePath = self::DEFAULT_COOKIE_PATH; + $this->configCookieDomain = ''; + + $this->currentTs = time(); + $this->createTs = $this->currentTs; + $this->visitCount = 0; + $this->currentVisitTs = false; + $this->lastVisitTs = false; + $this->ecommerceLastOrderTimestamp = false; + + // Allow debug while blocking the request + $this->requestTimeout = 600; + $this->doBulkRequests = false; + $this->storedTrackingActions = array(); + + $this->sendImageResponse = true; + + $this->visitorCustomVar = $this->getCustomVariablesFromCookie(); + + $this->outgoingTrackerCookies = array(); + $this->incomingTrackerCookies = array(); + } + + /** + * By default, Piwik expects utf-8 encoded values, for example + * for the page URL parameter values, Page Title, etc. + * It is recommended to only send UTF-8 data to Piwik. + * If required though, you can also specify another charset using this function. + * + * @param string $charset + * @return $this + */ + public function setPageCharset($charset = '') + { + $this->pageCharset = $charset; + return $this; + } + + /** + * Sets the current URL being tracked + * + * @param string $url Raw URL (not URL encoded) + * @return $this + */ + public function setUrl($url) + { + $this->pageUrl = $url; + return $this; + } + + /** + * Sets the URL referrer used to track Referrers details for new visits. + * + * @param string $url Raw URL (not URL encoded) + * @return $this + */ + public function setUrlReferrer($url) + { + $this->urlReferrer = $url; + return $this; + } + + /** + * Sets the time that generating the document on the server side took. + * + * @param int $timeMs Generation time in ms + * @return $this + */ + public function setGenerationTime($timeMs) + { + $this->generationTime = $timeMs; + return $this; + } + + /** + * @deprecated + * @ignore + */ + public function setUrlReferer($url) + { + $this->setUrlReferrer($url); + return $this; + } + + /** + * Sets the attribution information to the visit, so that subsequent Goal conversions are + * properly attributed to the right Referrer URL, timestamp, Campaign Name & Keyword. + * + * This must be a JSON encoded string that would typically be fetched from the JS API: + * piwikTracker.getAttributionInfo() and that you have JSON encoded via JSON2.stringify() + * + * If you call enableCookies() then these referral attribution values will be set + * to the 'ref' first party cookie storing referral information. + * + * @param string $jsonEncoded JSON encoded array containing Attribution info + * @return $this + * @throws Exception + * @see function getAttributionInfo() in https://github.com/piwik/piwik/blob/master/js/piwik.js + */ + public function setAttributionInfo($jsonEncoded) + { + $decoded = json_decode($jsonEncoded, $assoc = true); + if (!is_array($decoded)) { + throw new Exception("setAttributionInfo() is expecting a JSON encoded string, $jsonEncoded given"); + } + $this->attributionInfo = $decoded; + return $this; + } + + /** + * Sets Visit Custom Variable. + * See http://piwik.org/docs/custom-variables/ + * + * @param int $id Custom variable slot ID from 1-5 + * @param string $name Custom variable name + * @param string $value Custom variable value + * @param string $scope Custom variable scope. Possible values: visit, page, event + * @return $this + * @throws Exception + */ + public function setCustomVariable($id, $name, $value, $scope = 'visit') + { + if (!is_int($id)) { + throw new Exception("Parameter id to setCustomVariable should be an integer"); + } + if ($scope == 'page') { + $this->pageCustomVar[$id] = array($name, $value); + } elseif ($scope == 'event') { + $this->eventCustomVar[$id] = array($name, $value); + } elseif ($scope == 'visit') { + $this->visitorCustomVar[$id] = array($name, $value); + } else { + throw new Exception("Invalid 'scope' parameter value"); + } + return $this; + } + + /** + * Returns the currently assigned Custom Variable. + * + * If scope is 'visit', it will attempt to read the value set in the first party cookie created by Piwik Tracker + * ($_COOKIE array). + * + * @param int $id Custom Variable integer index to fetch from cookie. Should be a value from 1 to 5 + * @param string $scope Custom variable scope. Possible values: visit, page, event + * + * @throws Exception + * @return mixed An array with this format: array( 0 => CustomVariableName, 1 => CustomVariableValue ) or false + * @see Piwik.js getCustomVariable() + */ + public function getCustomVariable($id, $scope = 'visit') + { + if ($scope == 'page') { + return isset($this->pageCustomVar[$id]) ? $this->pageCustomVar[$id] : false; + } elseif ($scope == 'event') { + return isset($this->eventCustomVar[$id]) ? $this->eventCustomVar[$id] : false; + } else { + if ($scope != 'visit') { + throw new Exception("Invalid 'scope' parameter value"); + } + } + if (!empty($this->visitorCustomVar[$id])) { + return $this->visitorCustomVar[$id]; + } + $cookieDecoded = $this->getCustomVariablesFromCookie(); + if (!is_int($id)) { + throw new Exception("Parameter to getCustomVariable should be an integer"); + } + if (!is_array($cookieDecoded) + || !isset($cookieDecoded[$id]) + || !is_array($cookieDecoded[$id]) + || count($cookieDecoded[$id]) != 2 + ) { + return false; + } + + return $cookieDecoded[$id]; + } + + /** + * Clears any Custom Variable that may be have been set. + * + * This can be useful when you have enabled bulk requests, + * and you wish to clear Custom Variables of 'visit' scope. + */ + public function clearCustomVariables() + { + $this->visitorCustomVar = array(); + $this->pageCustomVar = array(); + $this->eventCustomVar = array(); + } + + /** + * Sets a custom tracking parameter. This is useful if you need to send any tracking parameters for a 3rd party + * plugin that is not shipped with Piwik itself. Please note that custom parameters are cleared after each + * tracking request. + * + * @param string $trackingApiParameter The name of the tracking API parameter, eg 'dimension1' + * @param string $value Tracking parameter value that shall be sent for this tracking parameter. + * @return $this + * @throws Exception + */ + public function setCustomTrackingParameter($trackingApiParameter, $value) + { + $this->customParameters[$trackingApiParameter] = $value; + return $this; + } + + /** + * Clear / reset all previously set custom tracking parameters. + */ + public function clearCustomTrackingParameters() + { + $this->customParameters = array(); + } + + /** + * Sets the current visitor ID to a random new one. + * @return $this + */ + public function setNewVisitorId() + { + $this->randomVisitorId = substr(md5(uniqid(rand(), true)), 0, self::LENGTH_VISITOR_ID); + $this->userId = false; + $this->forcedVisitorId = false; + $this->cookieVisitorId = false; + return $this; + } + + /** + * Sets the current site ID. + * + * @param int $idSite + * @return $this + */ + public function setIdSite($idSite) + { + $this->idSite = $idSite; + return $this; + } + + /** + * Sets the Browser language. Used to guess visitor countries when GeoIP is not enabled + * + * @param string $acceptLanguage For example "fr-fr" + * @return $this + */ + public function setBrowserLanguage($acceptLanguage) + { + $this->acceptLanguage = $acceptLanguage; + return $this; + } + + /** + * Sets the user agent, used to detect OS and browser. + * If this function is not called, the User Agent will default to the current user agent. + * + * @param string $userAgent + * @return $this + */ + public function setUserAgent($userAgent) + { + $this->userAgent = $userAgent; + return $this; + } + + /** + * Sets the country of the visitor. If not used, Piwik will try to find the country + * using either the visitor's IP address or language. + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth(). + * @param string $country + * @return $this + */ + public function setCountry($country) + { + $this->country = $country; + return $this; + } + + /** + * Sets the region of the visitor. If not used, Piwik may try to find the region + * using the visitor's IP address (if configured to do so). + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth(). + * @param string $region + * @return $this + */ + public function setRegion($region) + { + $this->region = $region; + return $this; + } + + /** + * Sets the city of the visitor. If not used, Piwik may try to find the city + * using the visitor's IP address (if configured to do so). + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth(). + * @param string $city + * @return $this + */ + public function setCity($city) + { + $this->city = $city; + return $this; + } + + /** + * Sets the latitude of the visitor. If not used, Piwik may try to find the visitor's + * latitude using the visitor's IP address (if configured to do so). + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth(). + * @param float $lat + * @return $this + */ + public function setLatitude($lat) + { + $this->lat = $lat; + return $this; + } + + /** + * Sets the longitude of the visitor. If not used, Piwik may try to find the visitor's + * longitude using the visitor's IP address (if configured to do so). + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth(). + * @param float $long + * @return $this + */ + public function setLongitude($long) + { + $this->long = $long; + return $this; + } + + /** + * Enables the bulk request feature. When used, each tracking action is stored until the + * doBulkTrack method is called. This method will send all tracking data at once. + * + */ + public function enableBulkTracking() + { + $this->doBulkRequests = true; + } + + /** + * Enable Cookie Creation - this will cause a first party VisitorId cookie to be set when the VisitorId is set or reset + * + * @param string $domain (optional) Set first-party cookie domain. + * Accepted values: example.com, *.example.com (same as .example.com) or subdomain.example.com + * @param string $path (optional) Set first-party cookie path + */ + public function enableCookies($domain = '', $path = '/') + { + $this->configCookiesDisabled = false; + $this->configCookieDomain = self::domainFixup($domain); + $this->configCookiePath = $path; + } + + /** + * If image response is disabled Piwik will respond with a HTTP 204 header instead of responding with a gif. + */ + public function disableSendImageResponse() + { + $this->sendImageResponse = false; + } + + /** + * Fix-up domain + */ + protected static function domainFixup($domain) + { + if (strlen($domain) > 0) { + $dl = strlen($domain) - 1; + // remove trailing '.' + if ($domain{$dl} === '.') { + $domain = substr($domain, 0, $dl); + } + // remove leading '*' + if (substr($domain, 0, 2) === '*.') { + $domain = substr($domain, 1); + } + } + + return $domain; + } + + /** + * Get cookie name with prefix and domain hash + * @param string $cookieName + * @return string + */ + protected function getCookieName($cookieName) + { + // NOTE: If the cookie name is changed, we must also update the method in piwik.js with the same name. + $hash = substr( + sha1( + ($this->configCookieDomain == '' ? self::getCurrentHost() : $this->configCookieDomain) . $this->configCookiePath + ), + 0, + 4 + ); + + return self::FIRST_PARTY_COOKIES_PREFIX . $cookieName . '.' . $this->idSite . '.' . $hash; + } + + /** + * Tracks a page view + * + * @param string $documentTitle Page title as it will appear in the Actions > Page titles report + * @return mixed Response string or true if using bulk requests. + */ + public function doTrackPageView($documentTitle) + { + $this->generateNewPageviewId(); + + $url = $this->getUrlTrackPageView($documentTitle); + + return $this->sendRequest($url); + } + + private function generateNewPageviewId() + { + $this->idPageview = substr(md5(uniqid(rand(), true)), 0, 6); + } + + /** + * Tracks an event + * + * @param string $category The Event Category (Videos, Music, Games...) + * @param string $action The Event's Action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...) + * @param string|bool $name (optional) The Event's object Name (a particular Movie name, or Song name, or File name...) + * @param float|bool $value (optional) The Event's value + * @return mixed Response string or true if using bulk requests. + */ + public function doTrackEvent($category, $action, $name = false, $value = false) + { + $url = $this->getUrlTrackEvent($category, $action, $name, $value); + + return $this->sendRequest($url); + } + + /** + * Tracks a content impression + * + * @param string $contentName The name of the content. For instance 'Ad Foo Bar' + * @param string $contentPiece The actual content. For instance the path to an image, video, audio, any text + * @param string|bool $contentTarget (optional) The target of the content. For instance the URL of a landing page. + * @return mixed Response string or true if using bulk requests. + */ + public function doTrackContentImpression($contentName, $contentPiece = 'Unknown', $contentTarget = false) + { + $url = $this->getUrlTrackContentImpression($contentName, $contentPiece, $contentTarget); + + return $this->sendRequest($url); + } + + /** + * Tracks a content interaction. Make sure you have tracked a content impression using the same content name and + * content piece, otherwise it will not count. To do so you should call the method doTrackContentImpression(); + * + * @param string $interaction The name of the interaction with the content. For instance a 'click' + * @param string $contentName The name of the content. For instance 'Ad Foo Bar' + * @param string $contentPiece The actual content. For instance the path to an image, video, audio, any text + * @param string|bool $contentTarget (optional) The target the content leading to when an interaction occurs. For instance the URL of a landing page. + * @return mixed Response string or true if using bulk requests. + */ + public function doTrackContentInteraction( + $interaction, + $contentName, + $contentPiece = 'Unknown', + $contentTarget = false + ) + { + $url = $this->getUrlTrackContentInteraction($interaction, $contentName, $contentPiece, $contentTarget); + + return $this->sendRequest($url); + } + + /** + * Tracks an internal Site Search query, and optionally tracks the Search Category, and Search results Count. + * These are used to populate reports in Actions > Site Search. + * + * @param string $keyword Searched query on the site + * @param string $category (optional) Search engine category if applicable + * @param bool|int $countResults (optional) results displayed on the search result page. Used to track "zero result" keywords. + * + * @return mixed Response or true if using bulk requests. + */ + public function doTrackSiteSearch($keyword, $category = '', $countResults = false) + { + $url = $this->getUrlTrackSiteSearch($keyword, $category, $countResults); + + return $this->sendRequest($url); + } + + /** + * Records a Goal conversion + * + * @param int $idGoal Id Goal to record a conversion + * @param float $revenue Revenue for this conversion + * @return mixed Response or true if using bulk request + */ + public function doTrackGoal($idGoal, $revenue = 0.0) + { + $url = $this->getUrlTrackGoal($idGoal, $revenue); + + return $this->sendRequest($url); + } + + /** + * Tracks a download or outlink + * + * @param string $actionUrl URL of the download or outlink + * @param string $actionType Type of the action: 'download' or 'link' + * @return mixed Response or true if using bulk request + */ + public function doTrackAction($actionUrl, $actionType) + { + // Referrer could be udpated to be the current URL temporarily (to mimic JS behavior) + $url = $this->getUrlTrackAction($actionUrl, $actionType); + + return $this->sendRequest($url); + } + + /** + * Adds an item in the Ecommerce order. + * + * This should be called before doTrackEcommerceOrder(), or before doTrackEcommerceCartUpdate(). + * This function can be called for all individual products in the cart (or order). + * SKU parameter is mandatory. Other parameters are optional (set to false if value not known). + * Ecommerce items added via this function are automatically cleared when doTrackEcommerceOrder() or getUrlTrackEcommerceOrder() is called. + * + * @param string $sku (required) SKU, Product identifier + * @param string $name (optional) Product name + * @param string|array $category (optional) Product category, or array of product categories (up to 5 categories can be specified for a given product) + * @param float|int $price (optional) Individual product price (supports integer and decimal prices) + * @param int $quantity (optional) Product quantity. If not specified, will default to 1 in the Reports + * @throws Exception + */ + public function addEcommerceItem($sku, $name = '', $category = '', $price = 0.0, $quantity = 1) + { + if (empty($sku)) { + throw new Exception("You must specify a SKU for the Ecommerce item"); + } + + $price = $this->forceDotAsSeparatorForDecimalPoint($price); + + $this->ecommerceItems[] = array($sku, $name, $category, $price, $quantity); + } + + /** + * Tracks a Cart Update (add item, remove item, update item). + * + * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart, + * including the items that haven't been updated since the last cart update. + * Items which were in the previous cart and are not sent in later Cart updates will be deleted from the cart (in the database). + * + * @param float $grandTotal Cart grandTotal (typically the sum of all items' prices) + * @return mixed Response or true if using bulk request + */ + public function doTrackEcommerceCartUpdate($grandTotal) + { + $url = $this->getUrlTrackEcommerceCartUpdate($grandTotal); + + return $this->sendRequest($url); + } + + /** + * Sends all stored tracking actions at once. Only has an effect if bulk tracking is enabled. + * + * To enable bulk tracking, call enableBulkTracking(). + * + * @throws Exception + * @return string Response + */ + public function doBulkTrack() + { + if (empty($this->storedTrackingActions)) { + throw new Exception( + "Error: you must call the function doTrackPageView or doTrackGoal from this class, + before calling this method doBulkTrack()" + ); + } + + $data = array('requests' => $this->storedTrackingActions); + + // token_auth is not required by default, except if bulk_requests_require_authentication=1 + if (!empty($this->token_auth)) { + $data['token_auth'] = $this->token_auth; + } + + $postData = json_encode($data); + $response = $this->sendRequest($this->getBaseUrl(), 'POST', $postData, $force = true); + + $this->storedTrackingActions = array(); + + return $response; + } + + /** + * Tracks an Ecommerce order. + * + * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order. + * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports. + * Only the parameters $orderId and $grandTotal are required. + * + * @param string|int $orderId (required) Unique Order ID. + * This will be used to count this order only once in the event the order page is reloaded several times. + * orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik. + * @param float $grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.) + * @param float $subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied) + * @param float $tax (optional) Tax amount for this order + * @param float $shipping (optional) Shipping amount for this order + * @param float $discount (optional) Discounted amount in this order + * @return mixed Response or true if using bulk request + */ + public function doTrackEcommerceOrder( + $orderId, + $grandTotal, + $subTotal = 0.0, + $tax = 0.0, + $shipping = 0.0, + $discount = 0.0 + ) + { + $url = $this->getUrlTrackEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount); + + return $this->sendRequest($url); + } + + /** + * Sends a ping request. + * + * Ping requests do not track new actions. If they are sent within the standard visit length (see global.ini.php), + * they will extend the existing visit and the current last action for the visit. If after the standard visit length, + * ping requests will create a new visit using the last action in the last known visit. + * + * @return mixed Response or true if using bulk request + */ + public function doPing() + { + $url = $this->getRequest($this->idSite); + $url .= '&ping=1'; + + return $this->sendRequest($url); + } + + /** + * Sets the current page view as an item (product) page view, or an Ecommerce Category page view. + * + * This must be called before doTrackPageView() on this product/category page. + * It will set 3 custom variables of scope "page" with the SKU, Name and Category for this page view. + * Note: Custom Variables of scope "page" slots 3, 4 and 5 will be used. + * + * On a category page, you may set the parameter $category only and set the other parameters to false. + * + * Tracking Product/Category page views will allow Piwik to report on Product & Categories + * conversion rates (Conversion rate = Ecommerce orders containing this product or category / Visits to the product or category) + * + * @param string $sku Product SKU being viewed + * @param string $name Product Name being viewed + * @param string|array $category Category being viewed. On a Product page, this is the product's category. + * You can also specify an array of up to 5 categories for a given page view. + * @param float $price Specify the price at which the item was displayed + * @return $this + */ + public function setEcommerceView($sku = '', $name = '', $category = '', $price = 0.0) + { + if (!empty($category)) { + if (is_array($category)) { + $category = json_encode($category); + } + } else { + $category = ""; + } + $this->pageCustomVar[self::CVAR_INDEX_ECOMMERCE_ITEM_CATEGORY] = array('_pkc', $category); + + if (!empty($price)) { + $price = (float)$price; + $price = $this->forceDotAsSeparatorForDecimalPoint($price); + $this->pageCustomVar[self::CVAR_INDEX_ECOMMERCE_ITEM_PRICE] = array('_pkp', $price); + } + + // On a category page, do not record "Product name not defined" + if (empty($sku) && empty($name)) { + return $this; + } + if (!empty($sku)) { + $this->pageCustomVar[self::CVAR_INDEX_ECOMMERCE_ITEM_SKU] = array('_pks', $sku); + } + if (empty($name)) { + $name = ""; + } + $this->pageCustomVar[self::CVAR_INDEX_ECOMMERCE_ITEM_NAME] = array('_pkn', $name); + return $this; + } + + /** + * Force the separator for decimal point to be a dot. See https://github.com/piwik/piwik/issues/6435 + * If for instance a German locale is used it would be a comma otherwise. + * + * @param float|string $value + * @return string + */ + private function forceDotAsSeparatorForDecimalPoint($value) + { + if (null === $value || false === $value) { + return $value; + } + + return str_replace(',', '.', $value); + } + + /** + * Returns URL used to track Ecommerce Cart updates + * Calling this function will reinitializes the property ecommerceItems to empty array + * so items will have to be added again via addEcommerceItem() + * @ignore + */ + public function getUrlTrackEcommerceCartUpdate($grandTotal) + { + $url = $this->getUrlTrackEcommerce($grandTotal); + + return $url; + } + + /** + * Returns URL used to track Ecommerce Orders + * Calling this function will reinitializes the property ecommerceItems to empty array + * so items will have to be added again via addEcommerceItem() + * @ignore + */ + public function getUrlTrackEcommerceOrder( + $orderId, + $grandTotal, + $subTotal = 0.0, + $tax = 0.0, + $shipping = 0.0, + $discount = 0.0 + ) + { + if (empty($orderId)) { + throw new Exception("You must specifiy an orderId for the Ecommerce order"); + } + $url = $this->getUrlTrackEcommerce($grandTotal, $subTotal, $tax, $shipping, $discount); + $url .= '&ec_id=' . urlencode($orderId); + $this->ecommerceLastOrderTimestamp = $this->getTimestamp(); + + return $url; + } + + /** + * Returns URL used to track Ecommerce orders + * + * Calling this function will reinitializes the property ecommerceItems to empty array + * so items will have to be added again via addEcommerceItem() + * + * @ignore + */ + protected function getUrlTrackEcommerce($grandTotal, $subTotal = 0.0, $tax = 0.0, $shipping = 0.0, $discount = 0.0) + { + if (!is_numeric($grandTotal)) { + throw new Exception("You must specifiy a grandTotal for the Ecommerce order (or Cart update)"); + } + + $url = $this->getRequest($this->idSite); + $url .= '&idgoal=0'; + if (!empty($grandTotal)) { + $grandTotal = $this->forceDotAsSeparatorForDecimalPoint($grandTotal); + $url .= '&revenue=' . $grandTotal; + } + if (!empty($subTotal)) { + $subTotal = $this->forceDotAsSeparatorForDecimalPoint($subTotal); + $url .= '&ec_st=' . $subTotal; + } + if (!empty($tax)) { + $tax = $this->forceDotAsSeparatorForDecimalPoint($tax); + $url .= '&ec_tx=' . $tax; + } + if (!empty($shipping)) { + $shipping = $this->forceDotAsSeparatorForDecimalPoint($shipping); + $url .= '&ec_sh=' . $shipping; + } + if (!empty($discount)) { + $discount = $this->forceDotAsSeparatorForDecimalPoint($discount); + $url .= '&ec_dt=' . $discount; + } + if (!empty($this->ecommerceItems)) { + $url .= '&ec_items=' . urlencode(json_encode($this->ecommerceItems)); + } + $this->ecommerceItems = array(); + + return $url; + } + + /** + * Builds URL to track a page view. + * + * @see doTrackPageView() + * @param string $documentTitle Page view name as it will appear in Piwik reports + * @return string URL to piwik.php with all parameters set to track the pageview + */ + public function getUrlTrackPageView($documentTitle = '') + { + $url = $this->getRequest($this->idSite); + if (strlen($documentTitle) > 0) { + $url .= '&action_name=' . urlencode($documentTitle); + } + + return $url; + } + + /** + * Builds URL to track a custom event. + * + * @see doTrackEvent() + * @param string $category The Event Category (Videos, Music, Games...) + * @param string $action The Event's Action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...) + * @param string|bool $name (optional) The Event's object Name (a particular Movie name, or Song name, or File name...) + * @param float|bool $value (optional) The Event's value + * @return string URL to piwik.php with all parameters set to track the pageview + * @throws + */ + public function getUrlTrackEvent($category, $action, $name = false, $value = false) + { + $url = $this->getRequest($this->idSite); + if (strlen($category) == 0) { + throw new Exception("You must specify an Event Category name (Music, Videos, Games...)."); + } + if (strlen($action) == 0) { + throw new Exception("You must specify an Event action (click, view, add...)."); + } + + $url .= '&e_c=' . urlencode($category); + $url .= '&e_a=' . urlencode($action); + + if (strlen($name) > 0) { + $url .= '&e_n=' . urlencode($name); + } + if (strlen($value) > 0) { + $value = $this->forceDotAsSeparatorForDecimalPoint($value); + $url .= '&e_v=' . $value; + } + + return $url; + } + + /** + * Builds URL to track a content impression. + * + * @see doTrackContentImpression() + * @param string $contentName The name of the content. For instance 'Ad Foo Bar' + * @param string $contentPiece The actual content. For instance the path to an image, video, audio, any text + * @param string|false $contentTarget (optional) The target of the content. For instance the URL of a landing page. + * @throws Exception In case $contentName is empty + * @return string URL to piwik.php with all parameters set to track the pageview + */ + public function getUrlTrackContentImpression($contentName, $contentPiece, $contentTarget) + { + $url = $this->getRequest($this->idSite); + + if (strlen($contentName) == 0) { + throw new Exception("You must specify a content name"); + } + + $url .= '&c_n=' . urlencode($contentName); + + if (!empty($contentPiece) && strlen($contentPiece) > 0) { + $url .= '&c_p=' . urlencode($contentPiece); + } + if (!empty($contentTarget) && strlen($contentTarget) > 0) { + $url .= '&c_t=' . urlencode($contentTarget); + } + + return $url; + } + + /** + * Builds URL to track a content impression. + * + * @see doTrackContentInteraction() + * @param string $interaction The name of the interaction with the content. For instance a 'click' + * @param string $contentName The name of the content. For instance 'Ad Foo Bar' + * @param string $contentPiece The actual content. For instance the path to an image, video, audio, any text + * @param string|false $contentTarget (optional) The target the content leading to when an interaction occurs. For instance the URL of a landing page. + * @throws Exception In case $interaction or $contentName is empty + * @return string URL to piwik.php with all parameters set to track the pageview + */ + public function getUrlTrackContentInteraction($interaction, $contentName, $contentPiece, $contentTarget) + { + $url = $this->getRequest($this->idSite); + + if (strlen($interaction) == 0) { + throw new Exception("You must specify a name for the interaction"); + } + + if (strlen($contentName) == 0) { + throw new Exception("You must specify a content name"); + } + + $url .= '&c_i=' . urlencode($interaction); + $url .= '&c_n=' . urlencode($contentName); + + if (!empty($contentPiece) && strlen($contentPiece) > 0) { + $url .= '&c_p=' . urlencode($contentPiece); + } + if (!empty($contentTarget) && strlen($contentTarget) > 0) { + $url .= '&c_t=' . urlencode($contentTarget); + } + + return $url; + } + + /** + * Builds URL to track a site search. + * + * @see doTrackSiteSearch() + * @param string $keyword + * @param string $category + * @param int $countResults + * @return string + */ + public function getUrlTrackSiteSearch($keyword, $category, $countResults) + { + $url = $this->getRequest($this->idSite); + $url .= '&search=' . urlencode($keyword); + if (strlen($category) > 0) { + $url .= '&search_cat=' . urlencode($category); + } + if (!empty($countResults) || $countResults === 0) { + $url .= '&search_count=' . (int)$countResults; + } + + return $url; + } + + /** + * Builds URL to track a goal with idGoal and revenue. + * + * @see doTrackGoal() + * @param int $idGoal Id Goal to record a conversion + * @param float $revenue Revenue for this conversion + * @return string URL to piwik.php with all parameters set to track the goal conversion + */ + public function getUrlTrackGoal($idGoal, $revenue = 0.0) + { + $url = $this->getRequest($this->idSite); + $url .= '&idgoal=' . $idGoal; + if (!empty($revenue)) { + $revenue = $this->forceDotAsSeparatorForDecimalPoint($revenue); + $url .= '&revenue=' . $revenue; + } + + return $url; + } + + /** + * Builds URL to track a new action. + * + * @see doTrackAction() + * @param string $actionUrl URL of the download or outlink + * @param string $actionType Type of the action: 'download' or 'link' + * @return string URL to piwik.php with all parameters set to track an action + */ + public function getUrlTrackAction($actionUrl, $actionType) + { + $url = $this->getRequest($this->idSite); + $url .= '&' . $actionType . '=' . urlencode($actionUrl); + + return $url; + } + + /** + * Overrides server date and time for the tracking requests. + * By default Piwik will track requests for the "current datetime" but this function allows you + * to track visits in the past. All times are in UTC. + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth() + * @see setTokenAuth() + * @param string $dateTime Date with the format 'Y-m-d H:i:s', or a UNIX timestamp. + * If the datetime is older than one day (default value for tracking_requests_require_authentication_when_custom_timestamp_newer_than), then you must call setTokenAuth() with a valid Admin/Super user token. + * @return $this + */ + public function setForceVisitDateTime($dateTime) + { + $this->forcedDatetime = $dateTime; + return $this; + } + + /** + * Forces Piwik to create a new visit for the tracking request. + * + * By default, Piwik will create a new visit if the last request by this user was more than 30 minutes ago. + * If you call setForceNewVisit() before calling doTrack*, then a new visit will be created for this request. + * @return $this + */ + public function setForceNewVisit() + { + $this->forcedNewVisit = true; + return $this; + } + + /** + * Overrides IP address + * + * Allowed only for Admin/Super User, must be used along with setTokenAuth() + * @see setTokenAuth() + * @param string $ip IP string, eg. 130.54.2.1 + * @return $this + */ + public function setIp($ip) + { + $this->ip = $ip; + return $this; + } + + /** + * Force the action to be recorded for a specific User. The User ID is a string representing a given user in your system. + * + * A User ID can be a username, UUID or an email address, or any number or string that uniquely identifies a user or client. + * + * @param string $userId Any user ID string (eg. email address, ID, username). Must be non empty. Set to false to de-assign a user id previously set. + * @return $this + * @throws Exception + */ + public function setUserId($userId) + { + if ($userId === false) { + $this->setNewVisitorId(); + return $this; + } + if ($userId === '') { + throw new Exception("User ID cannot be empty."); + } + $this->userId = $userId; + return $this; + } + + /** + * Hash function used internally by Piwik to hash a User ID into the Visitor ID. + * + * Note: matches implementation of Tracker\Request->getUserIdHashed() + * + * @param $id + * @return string + */ + public static function getUserIdHashed($id) + { + return substr(sha1($id), 0, 16); + } + + /** + * Forces the requests to be recorded for the specified Visitor ID. + * Note: it is recommended to use ->setUserId($userId); instead. + * + * Rather than letting Piwik attribute the user with a heuristic based on IP and other user fingeprinting attributes, + * force the action to be recorded for a particular visitor. + * + * If you use both setVisitorId and setUserId, setUserId will take precedence. + * If not set, the visitor ID will be fetched from the 1st party cookie, or will be set to a random UUID. + * + * @deprecated We recommend to use ->setUserId($userId). + * @param string $visitorId 16 hexadecimal characters visitor ID, eg. "33c31e01394bdc63" + * @return $this + * @throws Exception + */ + public function setVisitorId($visitorId) + { + $hexChars = '01234567890abcdefABCDEF'; + if (strlen($visitorId) != self::LENGTH_VISITOR_ID + || strspn($visitorId, $hexChars) !== strlen($visitorId) + ) { + throw new Exception( + "setVisitorId() expects a " + . self::LENGTH_VISITOR_ID + . " characters hexadecimal string (containing only the following: " + . $hexChars + . ")" + ); + } + $this->forcedVisitorId = $visitorId; + return $this; + } + + /** + * If the user initiating the request has the Piwik first party cookie, + * this function will try and return the ID parsed from this first party cookie (found in $_COOKIE). + * + * If you call this function from a server, where the call is triggered by a cron or script + * not initiated by the actual visitor being tracked, then it will return + * the random Visitor ID that was assigned to this visit object. + * + * This can be used if you wish to record more visits, actions or goals for this visitor ID later on. + * + * @return string 16 hex chars visitor ID string + */ + public function getVisitorId() + { + if (!empty($this->userId)) { + return $this->getUserIdHashed($this->userId); + } + if (!empty($this->forcedVisitorId)) { + return $this->forcedVisitorId; + } + if ($this->loadVisitorIdCookie()) { + return $this->cookieVisitorId; + } + + return $this->randomVisitorId; + } + + /** + * Returns the currently set user agent. + * @return string + */ + public function getUserAgent() + { + return $this->userAgent; + } + + /** + * Returns the currently set IP address. + * @return string + */ + public function getIp() + { + return $this->ip; + } + + /** + * Returns the User ID string, which may have been set via: + * $v->setUserId('username@example.org'); + * + * @return bool + */ + public function getUserId() + { + return $this->userId; + } + + /** + * Loads values from the VisitorId Cookie + * + * @return bool True if cookie exists and is valid, False otherwise + */ + protected function loadVisitorIdCookie() + { + $idCookie = $this->getCookieMatchingName('id'); + if ($idCookie === false) { + return false; + } + $parts = explode('.', $idCookie); + if (strlen($parts[0]) != self::LENGTH_VISITOR_ID) { + return false; + } + /* $this->cookieVisitorId provides backward compatibility since getVisitorId() + didn't change any existing VisitorId value */ + $this->cookieVisitorId = $parts[0]; + $this->createTs = $parts[1]; + $this->visitCount = (int)$parts[2]; + $this->currentVisitTs = $parts[3]; + $this->lastVisitTs = $parts[4]; + if (isset($parts[5])) { + $this->ecommerceLastOrderTimestamp = $parts[5]; + } + + return true; + } + + /** + * Deletes all first party cookies from the client + */ + public function deleteCookies() + { + $cookies = array('id', 'ses', 'cvar', 'ref'); + foreach ($cookies as $cookie) { + $this->setCookie($cookie, '', -86400); + } + } + + /** + * Returns the currently assigned Attribution Information stored in a first party cookie. + * + * This function will only work if the user is initiating the current request, and his cookies + * can be read by PHP from the $_COOKIE array. + * + * @return string JSON Encoded string containing the Referrer information for Goal conversion attribution. + * Will return false if the cookie could not be found + * @see Piwik.js getAttributionInfo() + */ + public function getAttributionInfo() + { + if (!empty($this->attributionInfo)) { + return json_encode($this->attributionInfo); + } + + return $this->getCookieMatchingName('ref'); + } + + /** + * Some Tracking API functionality requires express authentication, using either the + * Super User token_auth, or a user with 'admin' access to the website. + * + * The following features require access: + * - force the visitor IP + * - force the date & time of the tracking requests rather than track for the current datetime + * + * @param string $token_auth token_auth 32 chars token_auth string + * @return $this + */ + public function setTokenAuth($token_auth) + { + $this->token_auth = $token_auth; + return $this; + } + + /** + * Sets local visitor time + * + * @param string $time HH:MM:SS format + * @return $this + */ + public function setLocalTime($time) + { + list($hour, $minute, $second) = explode(':', $time); + $this->localHour = (int)$hour; + $this->localMinute = (int)$minute; + $this->localSecond = (int)$second; + return $this; + } + + /** + * Sets user resolution width and height. + * + * @param int $width + * @param int $height + * @return $this + */ + public function setResolution($width, $height) + { + $this->width = $width; + $this->height = $height; + return $this; + } + + /** + * Sets if the browser supports cookies + * This is reported in "List of plugins" report in Piwik. + * + * @param bool $bool + * @return $this + */ + public function setBrowserHasCookies($bool) + { + $this->hasCookies = $bool; + return $this; + } + + /** + * Will append a custom string at the end of the Tracking request. + * @param string $string + * @return $this + */ + public function setDebugStringAppend($string) + { + $this->DEBUG_APPEND_URL = '&' . $string; + return $this; + } + + /** + * Sets visitor browser supported plugins + * + * @param bool $flash + * @param bool $java + * @param bool $director + * @param bool $quickTime + * @param bool $realPlayer + * @param bool $pdf + * @param bool $windowsMedia + * @param bool $gears + * @param bool $silverlight + * @return $this + */ + public function setPlugins( + $flash = false, + $java = false, + $director = false, + $quickTime = false, + $realPlayer = false, + $pdf = false, + $windowsMedia = false, + $gears = false, + $silverlight = false + ) + { + $this->plugins = + '&fla=' . (int)$flash . + '&java=' . (int)$java . + '&dir=' . (int)$director . + '&qt=' . (int)$quickTime . + '&realp=' . (int)$realPlayer . + '&pdf=' . (int)$pdf . + '&wma=' . (int)$windowsMedia . + '&gears=' . (int)$gears . + '&ag=' . (int)$silverlight; + return $this; + } + + /** + * By default, PiwikTracker will read first party cookies + * from the request and write updated cookies in the response (using setrawcookie). + * This can be disabled by calling this function. + */ + public function disableCookieSupport() + { + $this->configCookiesDisabled = true; + } + + /** + * Returns the maximum number of seconds the tracker will spend waiting for a response + * from Piwik. Defaults to 600 seconds. + */ + public function getRequestTimeout() + { + return $this->requestTimeout; + } + + /** + * Sets the maximum number of seconds that the tracker will spend waiting for a response + * from Piwik. + * + * @param int $timeout + * @return $this + * @throws Exception + */ + public function setRequestTimeout($timeout) + { + if (!is_int($timeout) || $timeout < 0) { + throw new Exception("Invalid value supplied for request timeout: $timeout"); + } + + $this->requestTimeout = $timeout; + return $this; + } + + /** + * If a proxy is needed to look up the address of the Piwik site, set it with this + * @param string $proxy IP as string, for example "173.234.92.107" + * @param int $proxyPort + */ + public function setProxy($proxy, $proxyPort = 80) + { + $this->proxy = $proxy; + $this->proxyPort = $proxyPort; + } + + /** + * If the proxy IP and the proxy port have been set, with the setProxy() function + * returns a string, like "173.234.92.107:80" + */ + private function getProxy() + { + if (isset($this->proxy) && isset($this->proxyPort)) { + return $this->proxy.":".$this->proxyPort; + } + return null; + } + + /** + * Used in tests to output useful error messages. + * + * @ignore + */ + static public $DEBUG_LAST_REQUESTED_URL = false; + + /** + * @ignore + */ + protected function sendRequest($url, $method = 'GET', $data = null, $force = false) + { + self::$DEBUG_LAST_REQUESTED_URL = $url; + + // if doing a bulk request, store the url + if ($this->doBulkRequests && !$force) { + $this->storedTrackingActions[] + = $url + . (!empty($this->userAgent) ? ('&ua=' . urlencode($this->userAgent)) : '') + . (!empty($this->acceptLanguage) ? ('&lang=' . urlencode($this->acceptLanguage)) : ''); + + // Clear custom variables so they don't get copied over to other users in the bulk request + $this->clearCustomVariables(); + $this->clearCustomTrackingParameters(); + $this->userAgent = false; + $this->acceptLanguage = false; + + return true; + } + + $proxy = $this->getProxy(); + + if (function_exists('curl_init') && function_exists('curl_exec')) { + $options = array( + CURLOPT_URL => $url, + CURLOPT_USERAGENT => $this->userAgent, + CURLOPT_HEADER => true, + CURLOPT_TIMEOUT => $this->requestTimeout, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array( + 'Accept-Language: ' . $this->acceptLanguage, + ), + ); + + if (defined('PATH_TO_CERTIFICATES_FILE')) { + $options[CURLOPT_CAINFO] = PATH_TO_CERTIFICATES_FILE; + } + + if (isset($proxy)) { + $options[CURLOPT_PROXY] = $proxy; + } + + switch ($method) { + case 'POST': + $options[CURLOPT_POST] = true; + break; + default: + break; + } + + // only supports JSON data + if (!empty($data)) { + $options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json'; + $options[CURLOPT_HTTPHEADER][] = 'Expect:'; + $options[CURLOPT_POSTFIELDS] = $data; + } + + if (!empty($this->outgoingTrackerCookies)) { + $options[CURLOPT_COOKIE] = http_build_query($this->outgoingTrackerCookies); + $this->outgoingTrackerCookies = array(); + } + + $ch = curl_init(); + curl_setopt_array($ch, $options); + ob_start(); + $response = @curl_exec($ch); + ob_end_clean(); + $header = ''; + $content = ''; + if (!empty($response)) { + list($header, $content) = explode("\r\n\r\n", $response, $limitCount = 2); + } + + $this->parseIncomingCookies(explode("\r\n", $header)); + + } elseif (function_exists('stream_context_create')) { + $stream_options = array( + 'http' => array( + 'method' => $method, + 'user_agent' => $this->userAgent, + 'header' => "Accept-Language: " . $this->acceptLanguage . "\r\n", + 'timeout' => $this->requestTimeout, // PHP 5.2.1 + ), + ); + + if (isset($proxy)) { + $stream_options['http']['proxy'] = $proxy; + } + + // only supports JSON data + if (!empty($data)) { + $stream_options['http']['header'] .= "Content-Type: application/json \r\n"; + $stream_options['http']['content'] = $data; + } + + if (!empty($this->outgoingTrackerCookies)) { + $stream_options['http']['header'] .= 'Cookie: ' . http_build_query($this->outgoingTrackerCookies) . "\r\n"; + $this->outgoingTrackerCookies = array(); + } + + $ctx = stream_context_create($stream_options); + $response = file_get_contents($url, 0, $ctx); + $content = $response; + + $this->parseIncomingCookies($http_response_header); + } + + return $content; + } + + /** + * Returns current timestamp, or forced timestamp/datetime if it was set + * @return string|int + */ + protected function getTimestamp() + { + return !empty($this->forcedDatetime) + ? strtotime($this->forcedDatetime) + : time(); + } + + /** + * Returns the base URL for the piwik server. + */ + protected function getBaseUrl() + { + if (empty(self::$URL)) { + throw new Exception( + 'You must first set the Piwik Tracker URL by calling + PiwikTracker::$URL = \'http://your-website.org/piwik/\';' + ); + } + if (strpos(self::$URL, '/piwik.php') === false + && strpos(self::$URL, '/proxy-piwik.php') === false + ) { + self::$URL .= '/piwik.php'; + } + + return self::$URL; + } + + /** + * @ignore + */ + protected function getRequest($idSite) + { + $this->setFirstPartyCookies(); + + $customFields = ''; + if (!empty($this->customParameters)) { + $customFields = '&' . http_build_query($this->customParameters, '', '&'); + } + + $url = $this->getBaseUrl() . + '?idsite=' . $idSite . + '&rec=1' . + '&apiv=' . self::VERSION . + '&r=' . substr(strval(mt_rand()), 2, 6) . + + // XDEBUG_SESSIONS_START and KEY are related to the PHP Debugger, this can be ignored in other languages + (!empty($_GET['XDEBUG_SESSION_START']) ? + '&XDEBUG_SESSION_START=' . @urlencode($_GET['XDEBUG_SESSION_START']) : '') . + (!empty($_GET['KEY']) ? '&KEY=' . @urlencode($_GET['KEY']) : '') . + + // Only allowed for Admin/Super User, token_auth required, + (!empty($this->ip) ? '&cip=' . $this->ip : '') . + (!empty($this->userId) ? '&uid=' . urlencode($this->userId) : '') . + (!empty($this->forcedDatetime) ? '&cdt=' . urlencode($this->forcedDatetime) : '') . + (!empty($this->forcedNewVisit) ? '&new_visit=1' : '') . + ((!empty($this->token_auth) && !$this->doBulkRequests) ? + '&token_auth=' . urlencode($this->token_auth) : '') . + + // Values collected from cookie + '&_idts=' . $this->createTs . + '&_idvc=' . $this->visitCount . + (!empty($this->lastVisitTs) ? '&_viewts=' . $this->lastVisitTs : '') . + (!empty($this->ecommerceLastOrderTimestamp) ? + '&_ects=' . urlencode($this->ecommerceLastOrderTimestamp) : '') . + + // These parameters are set by the JS, but optional when using API + (!empty($this->plugins) ? $this->plugins : '') . + (($this->localHour !== false && $this->localMinute !== false && $this->localSecond !== false) ? + '&h=' . $this->localHour . '&m=' . $this->localMinute . '&s=' . $this->localSecond : '') . + (!empty($this->width) && !empty($this->height) ? '&res=' . $this->width . 'x' . $this->height : '') . + (!empty($this->hasCookies) ? '&cookie=' . $this->hasCookies : '') . + + // Various important attributes + (!empty($this->customData) ? '&data=' . $this->customData : '') . + (!empty($this->visitorCustomVar) ? '&_cvar=' . urlencode(json_encode($this->visitorCustomVar)) : '') . + (!empty($this->pageCustomVar) ? '&cvar=' . urlencode(json_encode($this->pageCustomVar)) : '') . + (!empty($this->eventCustomVar) ? '&e_cvar=' . urlencode(json_encode($this->eventCustomVar)) : '') . + (!empty($this->generationTime) ? '>_ms=' . ((int)$this->generationTime) : '') . + (!empty($this->forcedVisitorId) ? '&cid=' . $this->forcedVisitorId : '&_id=' . $this->getVisitorId()) . + + // URL parameters + '&url=' . urlencode($this->pageUrl) . + '&urlref=' . urlencode($this->urlReferrer) . + ((!empty($this->pageCharset) && $this->pageCharset != self::DEFAULT_CHARSET_PARAMETER_VALUES) ? + '&cs=' . $this->pageCharset : '') . + + // unique pageview id + (!empty($this->idPageview) ? '&pv_id=' . urlencode($this->idPageview) : '') . + + // Attribution information, so that Goal conversions are attributed to the right referrer or campaign + // Campaign name + (!empty($this->attributionInfo[0]) ? '&_rcn=' . urlencode($this->attributionInfo[0]) : '') . + // Campaign keyword + (!empty($this->attributionInfo[1]) ? '&_rck=' . urlencode($this->attributionInfo[1]) : '') . + // Timestamp at which the referrer was set + (!empty($this->attributionInfo[2]) ? '&_refts=' . $this->attributionInfo[2] : '') . + // Referrer URL + (!empty($this->attributionInfo[3]) ? '&_ref=' . urlencode($this->attributionInfo[3]) : '') . + + // custom location info + (!empty($this->country) ? '&country=' . urlencode($this->country) : '') . + (!empty($this->region) ? '®ion=' . urlencode($this->region) : '') . + (!empty($this->city) ? '&city=' . urlencode($this->city) : '') . + (!empty($this->lat) ? '&lat=' . urlencode($this->lat) : '') . + (!empty($this->long) ? '&long=' . urlencode($this->long) : '') . + $customFields . + (!$this->sendImageResponse ? '&send_image=0' : '') . + + // DEBUG + $this->DEBUG_APPEND_URL; + + + // Reset page level custom variables after this page view + $this->pageCustomVar = array(); + $this->eventCustomVar = array(); + $this->clearCustomTrackingParameters(); + + // force new visit only once, user must call again setForceNewVisit() + $this->forcedNewVisit = false; + + return $url; + } + + + /** + * Returns a first party cookie which name contains $name + * + * @param string $name + * @return string String value of cookie, or false if not found + * @ignore + */ + protected function getCookieMatchingName($name) + { + if ($this->configCookiesDisabled) { + return false; + } + if (!is_array($_COOKIE)) { + return false; + } + $name = $this->getCookieName($name); + + // Piwik cookie names use dots separators in piwik.js, + // but PHP Replaces . with _ http://www.php.net/manual/en/language.variables.predefined.php#72571 + $name = str_replace('.', '_', $name); + foreach ($_COOKIE as $cookieName => $cookieValue) { + if (strpos($cookieName, $name) !== false) { + return $cookieValue; + } + } + + return false; + } + + /** + * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1¶m2=value2" + * will return "/dir1/dir2/index.php" + * + * @return string + * @ignore + */ + protected static function getCurrentScriptName() + { + $url = ''; + if (!empty($_SERVER['PATH_INFO'])) { + $url = $_SERVER['PATH_INFO']; + } else { + if (!empty($_SERVER['REQUEST_URI'])) { + if (($pos = strpos($_SERVER['REQUEST_URI'], '?')) !== false) { + $url = substr($_SERVER['REQUEST_URI'], 0, $pos); + } else { + $url = $_SERVER['REQUEST_URI']; + } + } + } + if (empty($url)) { + $url = $_SERVER['SCRIPT_NAME']; + } + + if ($url[0] !== '/') { + $url = '/' . $url; + } + + return $url; + } + + /** + * If the current URL is 'http://example.org/dir1/dir2/index.php?param1=value1¶m2=value2" + * will return 'http' + * + * @return string 'https' or 'http' + * @ignore + */ + protected static function getCurrentScheme() + { + if (isset($_SERVER['HTTPS']) + && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true) + ) { + return 'https'; + } + + return 'http'; + } + + /** + * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1¶m2=value2" + * will return "http://example.org" + * + * @return string + * @ignore + */ + protected static function getCurrentHost() + { + if (isset($_SERVER['HTTP_HOST'])) { + return $_SERVER['HTTP_HOST']; + } + + return 'unknown'; + } + + /** + * If current URL is "http://example.org/dir1/dir2/index.php?param1=value1¶m2=value2" + * will return "?param1=value1¶m2=value2" + * + * @return string + * @ignore + */ + protected static function getCurrentQueryString() + { + $url = ''; + if (isset($_SERVER['QUERY_STRING']) + && !empty($_SERVER['QUERY_STRING']) + ) { + $url .= '?' . $_SERVER['QUERY_STRING']; + } + + return $url; + } + + /** + * Returns the current full URL (scheme, host, path and query string. + * + * @return string + * @ignore + */ + protected static function getCurrentUrl() + { + return self::getCurrentScheme() . '://' + . self::getCurrentHost() + . self::getCurrentScriptName() + . self::getCurrentQueryString(); + } + + /** + * Sets the first party cookies as would the piwik.js + * All cookies are supported: 'id' and 'ses' and 'ref' and 'cvar' cookies. + * @return $this + */ + protected function setFirstPartyCookies() + { + if ($this->configCookiesDisabled) { + return $this; + } + + if (empty($this->cookieVisitorId)) { + $this->loadVisitorIdCookie(); + } + + // Set the 'ref' cookie + $attributionInfo = $this->getAttributionInfo(); + if (!empty($attributionInfo)) { + $this->setCookie('ref', $attributionInfo, $this->configReferralCookieTimeout); + } + + // Set the 'ses' cookie + $this->setCookie('ses', '*', $this->configSessionCookieTimeout); + + // Set the 'id' cookie + $visitCount = $this->visitCount + 1; + $cookieValue = $this->getVisitorId() . '.' . $this->createTs . '.' . $visitCount . '.' . $this->currentTs . + '.' . $this->lastVisitTs . '.' . $this->ecommerceLastOrderTimestamp; + $this->setCookie('id', $cookieValue, $this->configVisitorCookieTimeout); + + // Set the 'cvar' cookie + $this->setCookie('cvar', json_encode($this->visitorCustomVar), $this->configSessionCookieTimeout); + return $this; + } + + /** + * Sets a first party cookie to the client to improve dual JS-PHP tracking. + * + * This replicates the piwik.js tracker algorithms for consistency and better accuracy. + * + * @param $cookieName + * @param $cookieValue + * @param $cookieTTL + * @return $this + */ + protected function setCookie($cookieName, $cookieValue, $cookieTTL) + { + $cookieExpire = $this->currentTs + $cookieTTL; + if (!headers_sent()) { + setcookie( + $this->getCookieName($cookieName), + $cookieValue, + $cookieExpire, + $this->configCookiePath, + $this->configCookieDomain + ); + } + return $this; + } + + /** + * @return bool|mixed + */ + protected function getCustomVariablesFromCookie() + { + $cookie = $this->getCookieMatchingName('cvar'); + if (!$cookie) { + return false; + } + + return json_decode($cookie, $assoc = true); + } + + /** + * Sets a cookie to be sent to the tracking server. + * + * @param $name + * @param $value + */ + public function setOutgoingTrackerCookie($name, $value) + { + if ($value === null) { + unset($this->outgoingTrackerCookies[$name]); + } + else { + $this->outgoingTrackerCookies[$name] = $value; + } + } + + /** + * Gets a cookie which was set by the tracking server. + * + * @param $name + * + * @return bool|string + */ + public function getIncomingTrackerCookie($name) + { + if (isset($this->incomingTrackerCookies[$name])) { + return $this->incomingTrackerCookies[$name]; + } + + return false; + } + + /** + * Reads incoming tracking server cookies. + * + * @param $headers Array with HTTP response headers as values + */ + protected function parseIncomingCookies($headers) + { + $this->incomingTrackerCookies = array(); + + if (!empty($headers)) { + $headerName = 'set-cookie:'; + $headerNameLength = strlen($headerName); + + foreach($headers as $header) { + if (strpos(strtolower($header), $headerName) !== 0) { + continue; + } + $cookies = trim(substr($header, $headerNameLength)); + $posEnd = strpos($cookies, ';'); + if ($posEnd !== false) { + $cookies = substr($cookies, 0, $posEnd); + } + parse_str($cookies, $this->incomingTrackerCookies); + } + } + } +} + +/** + * Helper function to quickly generate the URL to track a page view. + * + * @param $idSite + * @param string $documentTitle + * @return string + */ +function Piwik_getUrlTrackPageView($idSite, $documentTitle = '') +{ + $tracker = new PiwikTracker($idSite); + + return $tracker->getUrlTrackPageView($documentTitle); +} + +/** + * Helper function to quickly generate the URL to track a goal. + * + * @param $idSite + * @param $idGoal + * @param float $revenue + * @return string + */ +function Piwik_getUrlTrackGoal($idSite, $idGoal, $revenue = 0.0) +{ + $tracker = new PiwikTracker($idSite); + + return $tracker->getUrlTrackGoal($idGoal, $revenue); +} diff --git a/library/matomo-php-tracker/README.md b/library/matomo-php-tracker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e8447f0853f9c575ddf950bdf89c32eeaa14e52d --- /dev/null +++ b/library/matomo-php-tracker/README.md @@ -0,0 +1,16 @@ +# PHP Client for Piwik Analytics Tracking API + +The PHP Tracker Client provides all features of the [Piwik Javascript Tracker](http://developer.piwik.org/api-reference/tracking-javascript), +such as Ecommerce Tracking, Custom Variable, Event tracking and more. + +## Documentation and examples +Check out our [Piwik-PHP-Tracker developer documentation](http://developer.piwik.org/api-reference/PHP-Piwik-Tracker) and +[Piwik Tracking API guide](http://piwik.org/docs/tracking-api/). + +## Requirements: +* json extension (json_decode, json_encode) +* CURL or STREAM extensions (to issue the HTTP request to Piwik) + +## License + +Released under the [BSD License](http://www.opensource.org/licenses/bsd-license.php) \ No newline at end of file diff --git a/library/matomo-php-tracker/composer.json b/library/matomo-php-tracker/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..4b7babd14bf607087d911de9deece79543138f21 --- /dev/null +++ b/library/matomo-php-tracker/composer.json @@ -0,0 +1,22 @@ +{ + "name": "piwik/piwik-php-tracker", + "description": "PHP Client for Piwik Analytics Tracking API", + "keywords": ["piwik","tracker","analytics"], + "homepage": "http://piwik.org", + "license": "BSD-2-Clause", + "authors": [ + { + "name": "The Piwik Team", + "email": "hello@piwik.org", + "homepage": "http://piwik.org/the-piwik-team/" + } + ], + "support": { + "forum": "http://forum.piwik.org/", + "issues": "https://github.com/piwik/piwik-php-tracker/issues", + "source": "https://github.com/piwik/piwik-php-tracker" + }, + "autoload": { + "classmap": ["."] + } +} diff --git a/library/piwik-php-tracker b/library/piwik-php-tracker deleted file mode 160000 index f026613f8137c014030c96a22491ede5f5f1676b..0000000000000000000000000000000000000000 --- a/library/piwik-php-tracker +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f026613f8137c014030c96a22491ede5f5f1676b diff --git a/public/admin/skins/bokeh74/config.json b/public/admin/skins/bokeh74/config.json index 4f9b981dfd4b14f5a741b914156bac681516e478..da63d9d5db6b9a0f175e45cf4be6e33c8b395746 100644 --- a/public/admin/skins/bokeh74/config.json +++ b/public/admin/skins/bokeh74/config.json @@ -24,6 +24,7 @@ "translations": "icons/menu/traductions_24.png", "failed_searches": "icons/menu/infructueux_24.png", "piwik_stats": "icons/menu/piwik_24.png", + "matomo_stats": "icons/menu/matomo_24.png", "record_views": "icons/menu/stats_visu_24.png", "top_record_views": "icons/menu/stats_palmares_24.png", "territories": "icons/menu/territoire_24.png", diff --git a/public/admin/skins/bokeh74/icons/menu/matomo_24.png b/public/admin/skins/bokeh74/icons/menu/matomo_24.png new file mode 100644 index 0000000000000000000000000000000000000000..354eae7d2cf769a330ddd0b662026b80d184f674 Binary files /dev/null and b/public/admin/skins/bokeh74/icons/menu/matomo_24.png differ diff --git a/public/admin/skins/bokeh74/icons/menu/matomo_48.png b/public/admin/skins/bokeh74/icons/menu/matomo_48.png new file mode 100644 index 0000000000000000000000000000000000000000..da69bbd295acd59a363029a8a162360947b1b043 Binary files /dev/null and b/public/admin/skins/bokeh74/icons/menu/matomo_48.png differ diff --git a/public/admin/skins/retro/config.json b/public/admin/skins/retro/config.json index 3e4d472326e4e9e09f500a3719f141cfc625b5b4..d521cdc16b76febde15e4b4b1a0a48e3473cd7a9 100644 --- a/public/admin/skins/retro/config.json +++ b/public/admin/skins/retro/config.json @@ -24,6 +24,7 @@ "translations": "icons/menu/traductions_24.png", "failed_searches": "icons/menu/infructueux_24.png", "piwik_stats": "icons/menu/piwik_24.png", + "matomo_stats": "icons/menu/matomo_24.png", "record_views": "icons/menu/stats_visu_24.png", "top_record_views": "icons/menu/stats_palmares_24.png", "territories": "icons/menu/territoire_24.png", diff --git a/tests/application/modules/admin/controllers/StatControllerTest.php b/tests/application/modules/admin/controllers/StatControllerTest.php index a6d94762f7d39d6e0db9d97d32a7bbf734606ad3..acd4c5e171b17ab82404458038fab4419c49c4e4 100644 --- a/tests/application/modules/admin/controllers/StatControllerTest.php +++ b/tests/application/modules/admin/controllers/StatControllerTest.php @@ -68,7 +68,7 @@ class Admin_StatControllerRecherchesInfructueusesTest -class Admin_StatControllerPiwikActionTest +class Admin_StatControllerMatomoActionTest extends Admin_AbstractControllerTestCase { protected $_storm_default_to_volatile = true; @@ -76,8 +76,8 @@ class Admin_StatControllerPiwikActionTest public function setUp() { parent::setUp(); - Class_AdminVar::set('PIWIK_AUTH_TOKEN', 'anierstauie-ua/ive'); - Class_AdminVar::set('JS_STAT', '<!-- Piwik --> + Class_AdminVar::set('MATOMO_AUTH_TOKEN', 'anierstauie-ua/ive'); + Class_AdminVar::set('JS_STAT', '<!-- Matomo --> <script type="text/javascript"> try { var url = window.location.href ; @@ -142,24 +142,24 @@ if ( (url.match ( new RegExp ( "/recherche/" ) )) && ($(\'.resultats_page\').len _paq.push([\'trackPageView\']); _paq.push([\'enableLinkTracking\']); (function() { - var u=(("https:" == document.location.protocol) ? "https" : "http") + "://piwik.server.st/"; - _paq.push([\'setTrackerUrl\', u+\'piwik.php\']); + var u=(("https:" == document.location.protocol) ? "https" : "http") + "://matomo.server.st/"; + _paq.push([\'setTrackerUrl\', u+\'matomo.php\']); _paq.push([\'setSiteId\', 9999]); var d=document, g=d.createElement(\'script\'), s=d.getElementsByTagName(\'script\')[0]; g.type=\'text/javascript\'; - g.defer=true; g.async=true; g.src=u+\'piwik.js\'; s.parentNode.insertBefore(g,s); + g.defer=true; g.async=true; g.src=u+\'matomo.js\'; s.parentNode.insertBefore(g,s); })(); } catch ( err ) {} </script> -<noscript><p><img src="http://piwik.server.st/piwik.php?idsite=9999" style="border:0;" alt="" /></p></noscript> -<!-- End Piwik Code -->'); +<noscript><p><img src="http://matomo.server.st/matomo.php?idsite=9999" style="border:0;" alt="" /></p></noscript> +<!-- End Matomo Code -->'); - $this->dispatch('/admin/stat/piwik', true); + $this->dispatch('/admin/stat/matomo', true); } /** @test */ public function iframeSrcShouldNotContainsProtocol() { - $this->assertXPath('//iframe[starts-with(@src, "//piwik.server.st")]', + $this->assertXPath('//iframe[starts-with(@src, "//matomo.server.st")]', $this->_response->getBody()); } } \ No newline at end of file diff --git a/tests/application/modules/admin/controllers/enssib.fr.html b/tests/application/modules/admin/controllers/enssib.fr.html index 8a7e6f8d60f740db24bfb9da75dd87b4a0350d44..dff394c34f2bc8c111eb0fb994e5935528da8283 100644 --- a/tests/application/modules/admin/controllers/enssib.fr.html +++ b/tests/application/modules/admin/controllers/enssib.fr.html @@ -576,7 +576,7 @@ </script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- -var _paq = _paq || [];(function(){var u=(("https:" == document.location.protocol) ? "" : "http://www.enssib.fr/piwik/");_paq.push(["setSiteId", "1"]);_paq.push(["setTrackerUrl", u+"piwik.php"]);_paq.push(["setDoNotTrack", 1]);_paq.push(["trackPageView"]);_paq.push(["setIgnoreClasses", ["no-tracking","colorbox"]]);_paq.push(["enableLinkTracking"]);var d=document,g=d.createElement("script"),s=d.getElementsByTagName("script")[0];g.type="text/javascript";g.defer=true;g.async=true;g.src=u+"piwik.js";s.parentNode.insertBefore(g,s);})(); +var _paq = _paq || [];(function(){var u=(("https:" == document.location.protocol) ? "" : "http://www.enssib.fr/matomo/");_paq.push(["setSiteId", "1"]);_paq.push(["setTrackerUrl", u+"matomo.php"]);_paq.push(["setDoNotTrack", 1]);_paq.push(["trackPageView"]);_paq.push(["setIgnoreClasses", ["no-tracking","colorbox"]]);_paq.push(["enableLinkTracking"]);var d=document,g=d.createElement("script"),s=d.getElementsByTagName("script")[0];g.type="text/javascript";g.defer=true;g.async=true;g.src=u+"matomo.js";s.parentNode.insertBefore(g,s);})(); //--><!]]> </script> <script type="text/javascript" src="http://www.enssib.fr/sites/www/files/js/js_MpKfe1sTh5JIVGCZ17DsAuT1rqAC38MLLlkjqjQ1X_k.js"></script> @@ -586,7 +586,7 @@ var _paq = _paq || [];(function(){var u=(("https:" == document.location.protocol <script type="text/javascript" src="http://www.enssib.fr/sites/www/files/js/js_ZCgiO0pXCwurcKLOtv0FP6xmULSWKvKgKtn15MtAle4.js"></script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- -jQuery.extend(Drupal.settings, {"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"enssib","theme_token":"XhojEjuKoEDBQMH40fF83wbaojQn4mOO7i5VW5B9V8c","jquery_version":"1.10","js":{"0":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/jquery\/1.10\/jquery.min.js":1,"misc\/jquery.once.js":1,"misc\/drupal.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.core.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.widget.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/external\/jquery.cookie.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/misc\/jquery.form.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.mouse.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.draggable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.droppable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.sortable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.button.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.position.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.resizable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.dialog.min.js":1,"misc\/ajax.js":1,"sites\/all\/modules\/contrib\/jquery_update\/js\/jquery_update.js":1,"sites\/all\/modules\/contrib\/admin_menu\/admin_devel\/admin_devel.js":1,"sites\/all\/modules\/contrib\/button_field\/js\/button_field.ajax.js":1,"public:\/\/languages\/fr_Np55hzn09APffb7Qx6JAhdazIXdVfByTCyRDBe-F_GI.js":1,"sites\/all\/modules\/contrib\/views_slideshow\/js\/views_slideshow.js":1,"sites\/all\/modules\/contrib\/views\/js\/base.js":1,"misc\/progress.js":1,"sites\/all\/modules\/contrib\/homebox\/homebox.js":1,"sites\/all\/modules\/contrib\/homebox\/includes\/tipsy\/jquery.tipsy.js":1,"sites\/all\/libraries\/jquery.cycle\/jquery.cycle.all.min.js":1,"sites\/all\/modules\/contrib\/views_slideshow\/contrib\/views_slideshow_cycle\/js\/views_slideshow_cycle.js":1,"sites\/all\/modules\/contrib\/piwik\/piwik.js":1,"sites\/all\/modules\/contrib\/views\/js\/ajax_view.js":1,"sites\/all\/modules\/features\/enssib_emploi\/js\/emploi.js":1,"sites\/all\/modules\/features\/enssib_suivi_candidatures\/js\/webform_validator.js":1,"sites\/all\/themes\/enssib\/js\/variables.js":1,"sites\/all\/themes\/enssib\/js\/behaviours.js":1,"sites\/all\/themes\/enssib\/js\/enssib.js":1,"sites\/all\/themes\/enssib\/js\/jquery.colorbox-min.js":1,"sites\/all\/themes\/enssib\/js\/jquery.flexslider-min.js":1,"sites\/all\/themes\/enssib\/js\/spamspan.js":1},"css":{"modules\/system\/system.base.css":1,"modules\/system\/system.menus.css":1,"modules\/system\/system.messages.css":1,"modules\/system\/system.theme.css":1,"misc\/ui\/jquery.ui.button.css":1,"misc\/ui\/jquery.ui.resizable.css":1,"misc\/ui\/jquery.ui.dialog.css":1,"sites\/all\/modules\/contrib\/simplenews\/simplenews.css":1,"modules\/aggregator\/aggregator.css":1,"modules\/comment\/comment.css":1,"sites\/all\/modules\/contrib\/date\/date_api\/date.css":1,"modules\/field\/theme\/field.css":1,"modules\/node\/node.css":1,"modules\/search\/search.css":1,"modules\/user\/user.css":1,"sites\/all\/modules\/contrib\/workflow\/workflow_admin_ui\/workflow_admin_ui.css":1,"modules\/forum\/forum.css":1,"sites\/all\/modules\/contrib\/views\/css\/views.css":1,"sites\/all\/modules\/contrib\/ctools\/css\/ctools.css":1,"sites\/all\/modules\/contrib\/tagclouds\/tagclouds.css":1,"sites\/all\/modules\/contrib\/views_slideshow\/views_slideshow.css":1,"sites\/all\/modules\/contrib\/homebox\/includes\/tipsy\/tipsy.css":1,"sites\/all\/modules\/contrib\/commerce\/modules\/cart\/theme\/commerce_cart.theme.css":1,"sites\/all\/modules\/contrib\/views_slideshow\/contrib\/views_slideshow_cycle\/views_slideshow_cycle.css":1,"sites\/all\/themes\/enssib\/css\/styles.css":1,"sites\/all\/themes\/enssib\/css\/enssib.css":1,"sites\/all\/themes\/enssib\/css\/print.css":1}},"urlIsAjaxTrusted":{"\/offres-d-emploi":true,"\/":true},"views":{"ajax_path":"\/views\/ajax","ajaxViews":{"views_dom_id:f92469cd31adf7d758711fa42a3eef13":{"view_name":"offres_d_emploi","view_display_id":"block_2","view_args":"","view_path":"home","view_base_path":"offres-d-emploi","view_dom_id":"f92469cd31adf7d758711fa42a3eef13","pager_element":0},"views_dom_id:a63d63bdbb9ae5fb822a145ec4a1f25b":{"view_name":"actualites_news","view_display_id":"block_caroussel","view_args":"","view_path":"home","view_base_path":"actualites","view_dom_id":"a63d63bdbb9ae5fb822a145ec4a1f25b","pager_element":0}}},"viewsSlideshow":{"actualites_news-block_caroussel":{"methods":{"goToSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"nextSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"pause":["viewsSlideshowControls","viewsSlideshowCycle"],"play":["viewsSlideshowControls","viewsSlideshowCycle"],"previousSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"transitionBegin":["viewsSlideshowPager","viewsSlideshowSlideCounter"],"transitionEnd":[]},"paused":0}},"viewsSlideshowPager":{"actualites_news-block_caroussel":{"bottom":{"type":"viewsSlideshowPagerFields"}}},"viewsSlideshowPagerFields":{"actualites_news-block_caroussel":{"bottom":{"activatePauseOnHover":0}}},"viewsSlideshowCycle":{"#views_slideshow_cycle_main_actualites_news-block_caroussel":{"num_divs":12,"id_prefix":"#views_slideshow_cycle_main_","div_prefix":"#views_slideshow_cycle_div_","vss_id":"actualites_news-block_caroussel","effect":"scrollUp","transition_advanced":1,"timeout":5000,"speed":700,"delay":0,"sync":1,"random":0,"pause":1,"pause_on_click":0,"action_advanced":1,"start_paused":0,"remember_slide":0,"remember_slide_days":1,"pause_when_hidden":0,"pause_when_hidden_type":"full","amount_allowed_visible":"","nowrap":0,"fixed_height":1,"items_per_slide":3,"wait_for_image_load":1,"wait_for_image_load_timeout":3000,"cleartype":0,"cleartypenobg":0,"advanced_options":"{}"}},"piwik":{"trackMailto":1}}); +jQuery.extend(Drupal.settings, {"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"enssib","theme_token":"XhojEjuKoEDBQMH40fF83wbaojQn4mOO7i5VW5B9V8c","jquery_version":"1.10","js":{"0":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/jquery\/1.10\/jquery.min.js":1,"misc\/jquery.once.js":1,"misc\/drupal.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.core.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.widget.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/external\/jquery.cookie.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/misc\/jquery.form.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.mouse.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.draggable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.droppable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.sortable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.button.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.position.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.resizable.min.js":1,"sites\/all\/modules\/contrib\/jquery_update\/replace\/ui\/ui\/minified\/jquery.ui.dialog.min.js":1,"misc\/ajax.js":1,"sites\/all\/modules\/contrib\/jquery_update\/js\/jquery_update.js":1,"sites\/all\/modules\/contrib\/admin_menu\/admin_devel\/admin_devel.js":1,"sites\/all\/modules\/contrib\/button_field\/js\/button_field.ajax.js":1,"public:\/\/languages\/fr_Np55hzn09APffb7Qx6JAhdazIXdVfByTCyRDBe-F_GI.js":1,"sites\/all\/modules\/contrib\/views_slideshow\/js\/views_slideshow.js":1,"sites\/all\/modules\/contrib\/views\/js\/base.js":1,"misc\/progress.js":1,"sites\/all\/modules\/contrib\/homebox\/homebox.js":1,"sites\/all\/modules\/contrib\/homebox\/includes\/tipsy\/jquery.tipsy.js":1,"sites\/all\/libraries\/jquery.cycle\/jquery.cycle.all.min.js":1,"sites\/all\/modules\/contrib\/views_slideshow\/contrib\/views_slideshow_cycle\/js\/views_slideshow_cycle.js":1,"sites\/all\/modules\/contrib\/matomo\/matomo.js":1,"sites\/all\/modules\/contrib\/views\/js\/ajax_view.js":1,"sites\/all\/modules\/features\/enssib_emploi\/js\/emploi.js":1,"sites\/all\/modules\/features\/enssib_suivi_candidatures\/js\/webform_validator.js":1,"sites\/all\/themes\/enssib\/js\/variables.js":1,"sites\/all\/themes\/enssib\/js\/behaviours.js":1,"sites\/all\/themes\/enssib\/js\/enssib.js":1,"sites\/all\/themes\/enssib\/js\/jquery.colorbox-min.js":1,"sites\/all\/themes\/enssib\/js\/jquery.flexslider-min.js":1,"sites\/all\/themes\/enssib\/js\/spamspan.js":1},"css":{"modules\/system\/system.base.css":1,"modules\/system\/system.menus.css":1,"modules\/system\/system.messages.css":1,"modules\/system\/system.theme.css":1,"misc\/ui\/jquery.ui.button.css":1,"misc\/ui\/jquery.ui.resizable.css":1,"misc\/ui\/jquery.ui.dialog.css":1,"sites\/all\/modules\/contrib\/simplenews\/simplenews.css":1,"modules\/aggregator\/aggregator.css":1,"modules\/comment\/comment.css":1,"sites\/all\/modules\/contrib\/date\/date_api\/date.css":1,"modules\/field\/theme\/field.css":1,"modules\/node\/node.css":1,"modules\/search\/search.css":1,"modules\/user\/user.css":1,"sites\/all\/modules\/contrib\/workflow\/workflow_admin_ui\/workflow_admin_ui.css":1,"modules\/forum\/forum.css":1,"sites\/all\/modules\/contrib\/views\/css\/views.css":1,"sites\/all\/modules\/contrib\/ctools\/css\/ctools.css":1,"sites\/all\/modules\/contrib\/tagclouds\/tagclouds.css":1,"sites\/all\/modules\/contrib\/views_slideshow\/views_slideshow.css":1,"sites\/all\/modules\/contrib\/homebox\/includes\/tipsy\/tipsy.css":1,"sites\/all\/modules\/contrib\/commerce\/modules\/cart\/theme\/commerce_cart.theme.css":1,"sites\/all\/modules\/contrib\/views_slideshow\/contrib\/views_slideshow_cycle\/views_slideshow_cycle.css":1,"sites\/all\/themes\/enssib\/css\/styles.css":1,"sites\/all\/themes\/enssib\/css\/enssib.css":1,"sites\/all\/themes\/enssib\/css\/print.css":1}},"urlIsAjaxTrusted":{"\/offres-d-emploi":true,"\/":true},"views":{"ajax_path":"\/views\/ajax","ajaxViews":{"views_dom_id:f92469cd31adf7d758711fa42a3eef13":{"view_name":"offres_d_emploi","view_display_id":"block_2","view_args":"","view_path":"home","view_base_path":"offres-d-emploi","view_dom_id":"f92469cd31adf7d758711fa42a3eef13","pager_element":0},"views_dom_id:a63d63bdbb9ae5fb822a145ec4a1f25b":{"view_name":"actualites_news","view_display_id":"block_caroussel","view_args":"","view_path":"home","view_base_path":"actualites","view_dom_id":"a63d63bdbb9ae5fb822a145ec4a1f25b","pager_element":0}}},"viewsSlideshow":{"actualites_news-block_caroussel":{"methods":{"goToSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"nextSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"pause":["viewsSlideshowControls","viewsSlideshowCycle"],"play":["viewsSlideshowControls","viewsSlideshowCycle"],"previousSlide":["viewsSlideshowPager","viewsSlideshowSlideCounter","viewsSlideshowCycle"],"transitionBegin":["viewsSlideshowPager","viewsSlideshowSlideCounter"],"transitionEnd":[]},"paused":0}},"viewsSlideshowPager":{"actualites_news-block_caroussel":{"bottom":{"type":"viewsSlideshowPagerFields"}}},"viewsSlideshowPagerFields":{"actualites_news-block_caroussel":{"bottom":{"activatePauseOnHover":0}}},"viewsSlideshowCycle":{"#views_slideshow_cycle_main_actualites_news-block_caroussel":{"num_divs":12,"id_prefix":"#views_slideshow_cycle_main_","div_prefix":"#views_slideshow_cycle_div_","vss_id":"actualites_news-block_caroussel","effect":"scrollUp","transition_advanced":1,"timeout":5000,"speed":700,"delay":0,"sync":1,"random":0,"pause":1,"pause_on_click":0,"action_advanced":1,"start_paused":0,"remember_slide":0,"remember_slide_days":1,"pause_when_hidden":0,"pause_when_hidden_type":"full","amount_allowed_visible":"","nowrap":0,"fixed_height":1,"items_per_slide":3,"wait_for_image_load":1,"wait_for_image_load_timeout":3000,"cleartype":0,"cleartypenobg":0,"advanced_options":"{}"}},"matomo":{"trackMailto":1}}); //--><!]]> </script> </body> diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index 61882f565544798dfa32cea51bb4c2d6ac9987f5..d8b624e601f963432407013cac4a7c4c6a6b73fb 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -2216,4 +2216,30 @@ class UpgradeDB_353_Test extends UpgradeDBTestCase { /** @test */ public function placeholderFor1dTouchMigrationPatch() {} +} + + + + +class UpgradeDB_354_Test extends UpgradeDBTestCase { + public function prepare() { + if (!$this->query('select valeur from bib_admin_var where clef="PIWIK_AUTH_TOKEN"')) + $this->query('update bib_admin_var set CLEF="PIWIK_AUTH_TOKEN" where CLEF="MATOMO_AUTH_TOKEN" '); + $this->query('update bib_admin_var set Valeur=replace(VALEUR,"matomo","piwik") where CLEF="JS_STAT"'); + + + } + + /** @test */ + public function piwikBecomeMatomo() { + $this->assertNotNull($this->query('select valeur from bib_admin_var where clef = "MATOMO_AUTH_TOKEN"')->fetch()['valeur']); + + } + + /** @test */ + public function piwikBecomeJsMatomo() { + $data =$this->query('select valeur from bib_admin_var where clef = "JS_STAT"')->fetch(); + $this->assertContains('matomo.biblibre.com',$data['valeur']); + } + } \ No newline at end of file diff --git a/tests/library/Class/WebService/WebAnalyticsClientTest.php b/tests/library/Class/WebService/WebAnalyticsClientTest.php index ccfb737a6cdb312cf56c5d1aeb60322f54f8173f..7a51fa26f286a22ed139553fde38e84cc4a9a1bf 100644 --- a/tests/library/Class/WebService/WebAnalyticsClientTest.php +++ b/tests/library/Class/WebService/WebAnalyticsClientTest.php @@ -144,24 +144,24 @@ class Class_WebService_WebAnalyticsClientInstanciationWithWrongGoogleAnalyticsAn -class Class_WebService_WebAnalyticsClientInstanciationWithVarPiwikAndNoDomainNameTest extends Storm_Test_ModelTestCase { +class Class_WebService_WebAnalyticsClientInstanciationWithVarMatomoAndNoDomainNameTest extends Storm_Test_ModelTestCase { public function setUp() { parent::setUp(); Class_AdminVar::newInstanceWithId('JS_STAT', ['valeur' => "<script type=\"text/javascript\"> var _paq = _paq || []; - _paq.push([\"setDomains\", [\"*.sandbox.pergame.net/piwik/piwik.php\"]]); + _paq.push([\"setDomains\", [\"*.sandbox.pergame.net/matomo/matomo.php\"]]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { - var u=\"//sandbox.pergame.net/piwik/\"; - _paq.push(['setTrackerUrl', u+'piwik.php']); + var u=\"//sandbox.pergame.net/matomo/\"; + _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', 8]); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; - g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script> -<noscript><p><img src=\"//sandbox.pergame.net/piwik/piwik.php?idsite=1\" style=\"border:0;\" alt=\"\" /></p></noscript>"]); +<noscript><p><img src=\"//sandbox.pergame.net/matomo/matomo.php?idsite=1\" style=\"border:0;\" alt=\"\" /></p></noscript>"]); $this->_web_analytics_client = new Class_WebService_Analytics_Client(); } @@ -174,8 +174,8 @@ class Class_WebService_WebAnalyticsClientInstanciationWithVarPiwikAndNoDomainNam /** @test */ - public function trackerUrlShouldBeSandboxPiwik() { - $this->assertEquals('//sandbox.pergame.net/piwik/piwik.php', + public function trackerUrlShouldBeSandboxMatomo() { + $this->assertEquals('//sandbox.pergame.net/matomo/matomo.php', $this->_web_analytics_client->getTrackerUrl()); } } diff --git a/tests/scenarios/piwik/PiwikTest.php b/tests/scenarios/matomo/MatomoTest.php similarity index 76% rename from tests/scenarios/piwik/PiwikTest.php rename to tests/scenarios/matomo/MatomoTest.php index 358bdbff8c8b9e19b0b3c1840e2a338692a5b97f..c0ea0bd8d8e0d630dcf999bbb0be987f94ba34d3 100644 --- a/tests/scenarios/piwik/PiwikTest.php +++ b/tests/scenarios/matomo/MatomoTest.php @@ -18,11 +18,11 @@ * along with BOKEH; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class PiwikFixtures { +class MatomoFixtures { use Storm_Test_THelpers; - public function enablePiwik() { + public function enableMatomo() { $this->authToken(); $this->jsStat(); } @@ -30,7 +30,7 @@ class PiwikFixtures { public function authToken() { $this->fixture('Class_AdminVar', - ['id' => 'PIWIK_AUTH_TOKEN', + ['id' => 'MATOMO_AUTH_TOKEN', 'valeur' => '789456123456789']); } @@ -40,25 +40,25 @@ class PiwikFixtures { ['id' => 'JS_STAT', 'valeur' => "<script type=\"text/javascript\"> var _paq = _paq || []; - _paq.push([\"setDomains\", [\"*.sandbox.pergame.net/piwik/piwik.php\"]]); + _paq.push([\"setDomains\", [\"*.sandbox.pergame.net/matomo/matomo.php\"]]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { - var u=\"//sandbox.pergame.net/piwik/\"; - _paq.push(['setTrackerUrl', u+'piwik.php']); + var u=\"//sandbox.pergame.net/matomo/\"; + _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '8']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; - g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script> -<noscript><p><img src=\"//sandbox.pergame.net/piwik/piwik.php?idsite=1\" style=\"border:0;\" alt=\"\" /></p></noscript>"]); +<noscript><p><img src=\"//sandbox.pergame.net/matomo/matomo.php?idsite=1\" style=\"border:0;\" alt=\"\" /></p></noscript>"]); } public function customJsStat() { $this->fixture('Class_AdminVar', ['id' => 'JS_STAT', - 'valeur' => "<!-- Piwik --> + 'valeur' => "<!-- Matomo --> <script type=\"text/javascript\"> try { var url = window.location.href ; @@ -123,26 +123,26 @@ if ( (url.match ( new RegExp ( \"/recherche/\" ) )) && ($('.resultats_page').len _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { - var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + \"://piwik.mabib.fr/\"; - _paq.push(['setTrackerUrl', u+'piwik.php']); + var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + \"://matomo.mabib.fr/\"; + _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', 13]); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; - g.defer=true; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); + g.defer=true; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); } catch ( err ) {} </script> -<noscript><p><img src=\"http://piwik.mabib.fr/piwik.php?idsite=13\" style=\"border:0;\" alt=\"\" /></p></noscript> -<!-- End Piwik Code -->"]); +<noscript><p><img src=\"http://matomo.mabib.fr/matomo.php?idsite=13\" style=\"border:0;\" alt=\"\" /></p></noscript> +<!-- End Matomo Code -->"]); } } -class PiwikJsStatConfigurationParsingTest extends ModelTestCase { +class MatomoJsStatConfigurationParsingTest extends ModelTestCase { public function setUp() { parent::setUp(); - (new PiwikFixtures())->jsStat(); + (new MatomoFixtures())->jsStat(); $this->_web_analytics_client = new Class_WebService_Analytics_Client(); } @@ -154,8 +154,8 @@ class PiwikJsStatConfigurationParsingTest extends ModelTestCase { /** @test */ - public function trackerUrlShouldBeSandboxPiwik() { - $this->assertEquals('//sandbox.pergame.net/piwik/piwik.php', + public function trackerUrlShouldBeSandboxMatomo() { + $this->assertEquals('//sandbox.pergame.net/matomo/matomo.php', $this->_web_analytics_client->getTrackerUrl()); } } @@ -163,11 +163,11 @@ class PiwikJsStatConfigurationParsingTest extends ModelTestCase { -class PiwikAlternateJsStatConfigurationParsingTest extends ModelTestCase { +class MatomoAlternateJsStatConfigurationParsingTest extends ModelTestCase { public function setUp() { parent::setUp(); - (new PiwikFixtures())->customJsStat(); + (new MatomoFixtures())->customJsStat(); $this->_web_analytics_client = new Class_WebService_Analytics_Client(); } @@ -179,8 +179,8 @@ class PiwikAlternateJsStatConfigurationParsingTest extends ModelTestCase { /** @test */ - public function trackerUrlShouldBeMaBibPiwik() { - $this->assertEquals('//piwik.mabib.fr/piwik.php', + public function trackerUrlShouldBeMaBibMatomo() { + $this->assertEquals('//matomo.mabib.fr/matomo.php', $this->_web_analytics_client->getTrackerUrl()); } } @@ -188,7 +188,7 @@ class PiwikAlternateJsStatConfigurationParsingTest extends ModelTestCase { -abstract class PiwikLeftMenuTestCase extends ViewHelperTestCase { +abstract class MatomoLeftMenuTestCase extends ViewHelperTestCase { protected $_storm_default_to_volatile = true, $html; @@ -215,7 +215,7 @@ abstract class PiwikLeftMenuTestCase extends ViewHelperTestCase { -class PiwikAdminLeftMenuWithOutPiwikTokenTest extends PiwikLeftMenuTestCase { +class MatomoAdminLeftMenuWithOutMatomoTokenTest extends MatomoLeftMenuTestCase { public function setUp() { parent::setUp(); @@ -224,43 +224,43 @@ class PiwikAdminLeftMenuWithOutPiwikTokenTest extends PiwikLeftMenuTestCase { /** @test */ - public function piwikStatsLinkShouldNotBePresent() { - $this->assertNotXPath($this->html, '//a[contains(@href, "admin/stat/piwik")]'); + public function matomoStatsLinkShouldNotBePresent() { + $this->assertNotXPath($this->html, '//a[contains(@href, "admin/stat/matomo")]'); } } -class PiwikAdminLeftMenuWithPiwikTokenTest extends PiwikLeftMenuTestCase { +class MatomoAdminLeftMenuWithMatomoTokenTest extends MatomoLeftMenuTestCase { public function setUp() { parent::setUp(); - (new PiwikFixtures())->enablePiwik(); + (new MatomoFixtures())->enableMatomo(); $this->html = $this->helper->Admin_ContentNav(); } /** @test */ - public function piwikStatsLinkShouldBePresent() { - $this->assertXPathContentContains($this->html, '//a[contains(@href, "admin/stat/piwik")]', 'Piwik'); + public function matomoStatsLinkShouldBePresent() { + $this->assertXPathContentContains($this->html, '//a[contains(@href, "admin/stat/matomo")]', 'Matomo'); } /** @test */ - public function piwikIcoShouldBePresent() { - $this->assertXPath($this->html, '//img[contains(@src, "/icons/menu/piwik_24.png")]'); + public function matomoIcoShouldBePresent() { + $this->assertXPath($this->html, '//img[contains(@src, "/icons/menu/matomo_24.png")]'); } } -class PiwikStatControllerPiwikActionTest extends Admin_AbstractControllerTestCase { +class MatomoStatControllerMatomoActionTest extends Admin_AbstractControllerTestCase { public function setUp() { parent::setUp(); - (new PiwikFixtures())->enablePiwik(); + (new MatomoFixtures())->enableMatomo(); - $this->dispatch('/admin/stat/piwik', true); + $this->dispatch('/admin/stat/matomo', true); } @@ -272,7 +272,7 @@ class PiwikStatControllerPiwikActionTest extends Admin_AbstractControllerTestCas /** @test */ public function pageShouldContainsDashboardFrame() { - $this->assertXPath('//iframe[@src="//sandbox.pergame.net/piwik/index.php?module=Widgetize&action=iframe&moduleToWidgetize=Dashboard&actionToWidgetize=index&period=week&date=today&idSite=8&token_auth=789456123456789"]', $this->_response->getBody()); + $this->assertXPath('//iframe[@src="//sandbox.pergame.net/matomo/index.php?module=Widgetize&action=iframe&moduleToWidgetize=Dashboard&actionToWidgetize=index&period=week&date=today&idSite=8&token_auth=789456123456789"]', $this->_response->getBody()); } } @@ -281,14 +281,14 @@ class PiwikStatControllerPiwikActionTest extends Admin_AbstractControllerTestCas /** * @see http://forge.afi-sa.fr/issues/52810 */ -class PiwikStatControllerPiwikActionWithBothPiwikAndGoogleAnalyticsTest +class MatomoStatControllerMatomoActionWithBothMatomoAndGoogleAnalyticsTest extends Admin_AbstractControllerTestCase { protected $_storm_default_to_volatile = true; public function setUp() { parent::setUp(); - (new PiwikFixtures())->enablePiwik(); + (new MatomoFixtures())->enableMatomo(); $this->fixture('Class_AdminVar', ['id' => 'JS_STAT', 'valeur' => '<script type="text/javascript"> @@ -298,16 +298,16 @@ var _paq = _paq || []; _paq.push([\'trackPageView\']); _paq.push([\'enableLinkTracking\']); (function() { - var u=(("https:" == document.location.protocol) ? "https" : "http") + "://piwik.myserver.com/"; - _paq.push([\'setTrackerUrl\', u+\'piwik.php\']); + var u=(("https:" == document.location.protocol) ? "https" : "http") + "://matomo.myserver.com/"; + _paq.push([\'setTrackerUrl\', u+\'matomo.php\']); _paq.push([\'setSiteId\', 9999]); var d=document, g=d.createElement(\'script\'), s=d.getElementsByTagName(\'script\')[0]; g.type=\'text/javascript\'; - g.defer=true; g.async=true; g.src=u+\'piwik.js\'; s.parentNode.insertBefore(g,s); + g.defer=true; g.async=true; g.src=u+\'matomo.js\'; s.parentNode.insertBefore(g,s); })(); } catch ( err ) {} </script> -<noscript><p><img src="http://piwik.myserver.com/piwik.php?idsite=9999" style="border:0;" alt="" /></p></noscript> -<!-- End Piwik Code --> +<noscript><p><img src="http://matomo.myserver.com/matomo.php?idsite=9999" style="border:0;" alt="" /></p></noscript> +<!-- End Matomo Code --> <script> (function(i,s,o,g,r,a,m){i[\'GoogleAnalyticsObject\']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), @@ -320,7 +320,7 @@ _paq.push([\'enableLinkTracking\']); </script>']); - $this->dispatch('/admin/stat/piwik', true); + $this->dispatch('/admin/stat/matomo', true); }