diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 30c84dc4b66b973d49f6097ffa0fc8e08edd8d13..a1f08ce04aae0f037cddc1d659bb76e0b06bb786 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -1084,6 +1084,15 @@ treeSearchLimit = 100 ;apiKey = "mykey" ;universal = false +; Uncomment this section and provide your Piwik server address and site id to +; enable Piwik analytics. +[Piwik] +;url = "http://server.address/piwik/" +;site_id = 1 +; Uncomment the following setting to track additional information about searches +; and displayed records with Piwik's custom variables +;custom_variables = true + ; Uncomment portions of this section to activate tabs in the search box for switching ; between search modules. Keys are search backend names, values are labels for use in ; the user interface (subject to translation). diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php index 3a2ef704d700bed7b27210177a6d22636d208182..d3da93b2b7c9d8a90916a5db524d216344ecdb8c 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php @@ -189,6 +189,24 @@ class Factory return new GoogleAnalytics($key, $universal); } + /** + * Construct the Piwik helper. + * + * @param ServiceManager $sm Service manager. + * + * @return Piwik + */ + public static function getPiwik(ServiceManager $sm) + { + $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + $url = isset($config->Piwik->url) ? $config->Piwik->url : false; + $siteId = isset($config->Piwik->site_id) ? $config->Piwik->site_id : 1; + $customVars = isset($config->Piwik->custom_variables) + ? $config->Piwik->custom_variables + : false; + return new Piwik($url, $siteId, $customVars); + } + /** * Construct the GetLastSearchLink helper. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php new file mode 100644 index 0000000000000000000000000000000000000000..1d257c4ef86df73ad579459635f2b19a9befd0e5 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php @@ -0,0 +1,375 @@ +<?php +/** + * Piwik view helper + * + * PHP version 5 + * + * Copyright (C) The National Library of Finland 2014. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package View_Helpers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org Main Site + */ +namespace VuFind\View\Helper\Root; + +/** + * Piwik Web Analytics view helper + * + * @category VuFind2 + * @package View_Helpers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org Main Site + */ +class Piwik extends \Zend\View\Helper\AbstractHelper +{ + /** + * Piwik URL (false if disabled) + * + * @var string|bool + */ + protected $url; + + /** + * Piwik Site ID + * + * @var int + */ + protected $siteId; + + /** + * Whether to track use custom variables to track additional information + * + * @var bool + */ + protected $customVars; + + /** + * Constructor + * + * @param string|bool $url Piwik address (false if disabled) + * @param int $siteId Piwik site ID + * @param bool $customVars Whether to track additional information in + * custom variables + */ + public function __construct($url, $siteId, $customVars) + { + $this->url = $url; + if ($url && substr($url, -1) != '/') { + $this->url .= '/'; + } + $this->siteId = $siteId; + $this->customVars = $customVars; + } + + /** + * Returns Piwik code (if active) or empty string if not. + * + * @return string + */ + public function __invoke() + { + if (!$this->url) { + return ''; + } + + if ($results = $this->getSearchResults()) { + $code = $this->trackSearch($results); + } else if ($recordDriver = $this->getRecordDriver()) { + $code = $this->trackRecordPage($recordDriver); + } else { + $code = $this->trackPageView(); + } + + $inlineScript = $this->getView()->plugin('inlinescript'); + return $inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $code, 'SET'); + } + + /** + * Track a Search + * + * @param VuFind\Search\Base\Results $results Search Results + * + * @return string Tracking Code + */ + protected function trackSearch($results) + { + $customVars = $this->getSearchCustomVars($results); + + $code = $this->getOpeningTrackingCode(); + $code .= $this->getCustomVarsCode($customVars); + $code .= $this->getTrackSearchCode($results); + $code .= $this->getClosingTrackingCode(); + + return $code; + } + + /** + * Track a Record View + * + * @param VuFind\RecordDriver\AbstractBase $recordDriver Record Driver + * + * @return string Tracking Code + */ + protected function trackRecordPage($recordDriver) + { + $customVars = $this->getRecordPageCustomVars($recordDriver); + + $code = $this->getOpeningTrackingCode(); + $code .= $this->getCustomVarsCode($customVars); + $code .= $this->getTrackPageViewCode(); + $code .= $this->getClosingTrackingCode(); + + return $code; + } + + /** + * Track a Generic Page View + * + * @return string Tracking Code + */ + protected function trackPageView() + { + $customVars = $this->getGenericCustomVars(); + + $code = $this->getOpeningTrackingCode(); + $code .= $this->getCustomVarsCode($customVars); + $code .= $this->getTrackPageViewCode(); + $code .= $this->getClosingTrackingCode(); + + return $code; + } + + /** + * Get Search Results if on a Results Page + * + * @return VuFind\Search\Base\Results|null Search results or null if not + * on a search page + */ + protected function getSearchResults() + { + $viewModel = $this->getView()->plugin('view_model'); + $children = $viewModel->getCurrent()->getChildren(); + if (isset($children[0])) { + $template = $children[0]->getTemplate(); + if (!strstr($template, '/home')) { + $results = $children[0]->getVariable('results'); + if (is_a($results, 'VuFind\Search\Base\Results')) { + return $results; + } + } + } + return null; + } + + /** + * Get Record Driver if on a Record Page + * + * @return VuFind\RecordDriver\AbstractBase|null Record driver or null if not + * on a record page + */ + protected function getRecordDriver() + { + $viewModel = $this->getView()->plugin('view_model'); + $children = $viewModel->getCurrent()->getChildren(); + if (isset($children[0])) { + $driver = $children[0]->getVariable('driver'); + if (is_a($driver, 'VuFind\RecordDriver\AbstractBase')) { + return $driver; + } + } + return null; + } + + /** + * Get Custom Variables for Search Results + * + * @param VuFind\Search\Base\Results $results Search results + * + * @return array Associative array of custom variables + */ + protected function getSearchCustomVars($results) + { + if (!$this->customVars) { + return array(); + } + + $facets = array(); + $facetTypes = array(); + $params = $results->getParams(); + foreach ($params->getFilterList() as $filterType => $filters) { + $facetTypes[] = $filterType; + foreach ($filters as $filter) { + $facets[] = $filter['field'] . '|' . $filter['value']; + } + } + $facets = implode("\t", $facets); + $facetTypes = implode("\t", $facetTypes); + + return array( + 'Facets' => $facets, + 'FacetTypes' => $facetTypes, + 'SearchType' => $params->getSearchType(), + 'SearchBackend' => $params->getSearchClassId(), + 'Sort' => $params->getSort(), + 'Page' => $params->getPage(), + 'Limit' => $params->getLimit(), + 'View' => $params->getView() + ); + } + + /** + * Get Custom Variables for a Record Page + * + * @param VuFind\RecordDriver\AbstractBase $recordDriver Record driver + * + * @return array Associative array of custom variables + */ + protected function getRecordPageCustomVars($recordDriver) + { + $id = $recordDriver->getUniqueID(); + $formats = $recordDriver->tryMethod('getFormats'); + if (is_array($formats)) { + $formats = implode(',', $formats); + } + $formats = $formats; + $author = $recordDriver->tryMethod('getPrimaryAuthor'); + if (empty($author)) { + $author = '-'; + } + // Use breadcrumb for title since it's guaranteed to return something + $title = $recordDriver->tryMethod('getBreadcrumb'); + if (empty($title)) { + $title = '-'; + } + $institutions = $recordDriver->tryMethod('getInstitutions'); + if (is_array($institutions)) { + $institutions = implode(',', $institutions); + } + $institutions = $institutions; + + return array( + 'RecordFormat' => $formats, + 'RecordData' => "$id|$author|$title", + 'RecordInstitution' => $institutions + ); + } + + /** + * Get Custom Variables for a Generic Page View + * + * @return array Associative array of custom variables + */ + protected function getGenericCustomVars() + { + return array(); + } + + /** + * Get the Initialization Part of the Tracking Code + * + * @return string JavaScript Code Fragment + */ + protected function getOpeningTrackingCode() + { + return <<<EOT +var _paq = _paq || []; +(function(){ +_paq.push(['setSiteId', {$this->siteId}]); +_paq.push(['setTrackerUrl', '{$this->url}piwik.php']); +_paq.push(['setCustomUrl', location.protocol + '//' + + location.host + location.pathname]); + +EOT; + } + + /** + * Get the Finalization Part of the Tracking Code + * + * @return string JavaScript Code Fragment + */ + protected function getClosingTrackingCode() + { + return <<<EOT +_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='{$this->url}piwik.js'; +s.parentNode.insertBefore(g,s); })(); + +EOT; + } + + /** + * Convert a Custom Variables Array to JavaScript Code + * + * @param array $customVars Custom Variables + * + * @return string JavaScript Code Fragment + */ + protected function getCustomVarsCode($customVars) + { + $escape = $this->getView()->plugin('escapeHtmlAttr'); + $code = ''; + $i = 0; + foreach ($customVars as $key => $value) { + ++$i; + $value = $escape($value); + $code .= <<<EOT +_paq.push(['setCustomVariable', $i, '$key', '$value', 'page']); + +EOT; + } + return $code; + } + + /** + * Get Site Search Tracking Code + * + * @param VuFind\Search\Base\Results $results Search results + * + * @return string JavaScript Code Fragment + */ + protected function getTrackSearchCode($results) + { + $escape = $this->getView()->plugin('escapeHtmlAttr'); + $params = $results->getParams(); + $searchTerms = $escape($params->getDisplayQuery()); + $searchType = $escape($params->getSearchType()); + $resultCount = $results->getResultTotal(); + + // Use trackSiteSearch *instead* of trackPageView in searches + return <<<EOT +_paq.push(['trackSiteSearch', '$searchTerms', '$searchType', $resultCount]); + +EOT; + } + + /** + * Get Page View Tracking Code + * + * @return string JavaScript Code Fragment + */ + protected function getTrackPageViewCode() + { + return <<<EOT +_paq.push(['trackPageView']); + +EOT; + } +} diff --git a/themes/blueprint/templates/layout/layout.phtml b/themes/blueprint/templates/layout/layout.phtml index 98e4bd09e93d561ae833cfb309a4bd1cae196109..cdd5a16b870bd3999b550dbd99ecdd1d0a7f7fd0 100644 --- a/themes/blueprint/templates/layout/layout.phtml +++ b/themes/blueprint/templates/layout/layout.phtml @@ -122,5 +122,6 @@ </div> </div> <?=$this->googleanalytics()?> + <?=$this->piwik()?> </body> </html> \ No newline at end of file diff --git a/themes/bootstrap/templates/layout/layout.phtml b/themes/bootstrap/templates/layout/layout.phtml index 569380385ccd0176c6bb005fece7fe4b66db6dbb..1e98cb170374ec0b45cd95f5487bd7ee63dd038f 100644 --- a/themes/bootstrap/templates/layout/layout.phtml +++ b/themes/bootstrap/templates/layout/layout.phtml @@ -54,7 +54,7 @@ } $this->headScript()->appendScript($this->jsTranslations()->getScript()); } - + // Session keep-alive if ($this->KeepAlive()) { $this->headScript()->appendScript('var keepAliveInterval = ' @@ -87,7 +87,7 @@ <?=$this->layout()->searchbox?> <? endif; ?> </div> - + <? if((!isset($this->layout()->showBreadcrumbs) || $this->layout()->showBreadcrumbs == true) && !empty($this->layout()->breadcrumbs) && $this->layout()->breadcrumbs !== false @@ -129,5 +129,6 @@ <div class="modal-body"><?=$this->transEsc('Loading') ?>...</div> </div> <?=$this->googleanalytics()?> + <?=$this->piwik()?> </body> </html> diff --git a/themes/bootstrap3/templates/layout/layout.phtml b/themes/bootstrap3/templates/layout/layout.phtml index d8e7e5adcebd63ed128825fca5f5ef35814d7e30..944e3203fc7b3185c1e3400c4edc1b9b7fb22b11 100644 --- a/themes/bootstrap3/templates/layout/layout.phtml +++ b/themes/bootstrap3/templates/layout/layout.phtml @@ -136,5 +136,6 @@ </div> </div> <?=$this->googleanalytics()?> + <?=$this->piwik()?> </body> </html> diff --git a/themes/jquerymobile/templates/layout/layout.phtml b/themes/jquerymobile/templates/layout/layout.phtml index e07261cda88e1d3d78a53231d63ca20b7489de1d..26eb02d6d6f2c38ece9dd799caac871decdadcc4 100644 --- a/themes/jquerymobile/templates/layout/layout.phtml +++ b/themes/jquerymobile/templates/layout/layout.phtml @@ -49,5 +49,6 @@ </div> </div> <?=$this->googleanalytics()?> + <?=$this->piwik()?> </body> </html> diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index eecfd12bdd7f2f69f36368a693286cbb7b2dd993..695695a8fd7dedd6185df30a76fc7874b0df7b88 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -22,6 +22,7 @@ return array( 'keepalive' => 'VuFind\View\Helper\Root\Factory::getKeepAlive', 'proxyurl' => 'VuFind\View\Helper\Root\Factory::getProxyUrl', 'openurl' => 'VuFind\View\Helper\Root\Factory::getOpenUrl', + 'piwik' => 'VuFind\View\Helper\Root\Factory::getPiwik', 'recaptcha' => 'VuFind\View\Helper\Root\Factory::getRecaptcha', 'record' => 'VuFind\View\Helper\Root\Factory::getRecord', 'recordlink' => 'VuFind\View\Helper\Root\Factory::getRecordLink',