Browse Source

Intial import of the PiwikBundle.

This bundle can be used to easily add the piwik javascript add the end of each page. Some method in the contoller or in the twig template can be called to add some additional functionality as :
- adding ecommerce product or category view
- adding ecommerce basket
- adding ecommerce goal
- adding search tracking
- adding heartbeat functionality.
tags/0.1.0
Ulrich Van Den Hekke 7 years ago
commit
4cf0036890

+ 4
- 0
.hgignore View File

@@ -0,0 +1,4 @@
syntax: glob
.settings
.project
.buildpath

+ 101
- 0
Controller/DefaultController.php View File

@@ -0,0 +1,101 @@
<?php
/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\Controller;
use Symfony\Component\HttpFoundation\Response;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

class DefaultController extends Controller {
/**
* @Route("/tracker.{_format}", requirements={"_format" = "js"})
*/
public function trackerAction() {
$request = $this->getRequest();
$piwikService = $this->container->get('shadoware_piwik.service');
$piwik_url = "http://".$piwikService->getPiwikURL() ."/";
// 1) PIWIK.JS PROXY: No _GET parameter, we serve the JS file
if ($request->query->count() == 0) {
$expires = new \DateTime();
$expires->modify('+86400 seconds');
// Re-download the piwik.js once a day maximum
$lastModified = new \DateTime();
$lastModified->modify('-86400 seconds');
// set HTTP response headers
$response = new Response();
$response->setExpires($expires);
$response->setLastModified(new \DateTime());
$response->setMaxAge(86400);
$response->setSharedMaxAge(86400);
$response->setPublic();
$response->setVary('Accept-Encoding');
// Returns 304 if not modified since
$lastModifiedSince = $request->headers->get('If-Modified-Since');
if (false !== ($semicolon = strpos($lastModifiedSince, ';'))) {
$lastModifiedSince = strtotime(substr($lastModifiedSince, 0, $semicolon));
}
else {
$lastModifiedSince = @strtotime($lastModifiedSince);
}

if ($lastModifiedSince && $lastModifiedSince < $lastModified->getTimestamp()) {
$response->setNotModified();
return $response;
}

$response->headers->set('Content-Type', 'application/javascript; charset=UTF-8');
if($piwikJs = file_get_contents($piwik_url.'piwik.js')) {
$response->setContent($piwikJs);
return $response;
}
throw new \Exception("Can't get the content of the 'piwik.js' file.");
}
}
/**
* @Route("/track")
*/
public function trackAction() {
$request = $this->getRequest();
$piwikService = $this->container->get('shadoware_piwik.service');
$piwik_url = "http://".$piwikService->getPiwikURL() ."/";
$token_auth = $piwikService->getTokenId();
// Maximum time, in seconds, to wait for the Piwik server to return the 1*1 GIF
$timeout = 5;

// 2) PIWIK.PHP PROXY: GET parameters found, this is a tracking request, we redirect it to Piwik
$url = $piwik_url."piwik.php?cip=".@$request->server->get('REMOTE_ADDR')."&token_auth=".$token_auth.'&';
foreach($request->query->all() as $key => $value) {
$url .= $key .'='.urlencode($value).'&';
}
$stream_options = array('http' => array(
'user_agent' => @$request->server->get('HTTP_USER_AGENT'),
'header' => "Accept-Language: " . @$request->server->get('HTTP_ACCEPT_LANGUAGE') . "\r\n" ,
'timeout' => $timeout
));
$ctx = stream_context_create($stream_options);

$response = new Response();
$response->headers->set('Content-Type', 'image/gif');
$response->setContent(file_get_contents($url, 0, $ctx));
return $response;
}
}

+ 46
- 0
DependencyInjection/Configuration.php View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Shadoware\PiwikBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface {
/**
* {@inheritDoc}
*/
public function getConfigTreeBuilder() {
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('shadoware_piwik');

$rootNode->children()
->scalarNode('base_url')->isRequired()->cannotBeEmpty()->end()
->scalarNode('id_site')->isRequired()->cannotBeEmpty()->end()
->scalarNode('hidePiwik')->defaultFalse()->end()
->scalarNode('tokenId')->defaultNull()->end()
->booleanNode('async')->defaultFalse()->end()
->arrayNode('heartbeat')
->children()
->scalarNode('enabled')->defaultFalse()->end()
->scalarNode('minimumVisitLength')->defaultValue(15)->end()
->scalarNode('delay')->defaultValue(30)->end()
->end()
->end()
->end();
return $treeBuilder;
}
}

+ 43
- 0
DependencyInjection/ShadowarePiwikExtension.php View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Shadoware\PiwikBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class ShadowarePiwikExtension extends Extension {
/**
* {@inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container) {
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.xml');
$container->setParameter('shadoware_piwik.base_url', $config['base_url']);
$container->setParameter('shadoware_piwik.id_site', $config['id_site']);
$container->setParameter('shadoware_piwik.hidePiwik', $config['hidePiwik']);
$container->setParameter('shadoware_piwik.tokenId', $config['tokenId']);
$container->setParameter('shadoware_piwik.async', $config['async']);
$container->setParameter('shadoware_piwik.heartbeat.enabled', $config['heartbeat']['enabled']);
$container->setParameter('shadoware_piwik.heartbeat.minimumVisitLength', $config['heartbeat']['minimumVisitLength']);
$container->setParameter('shadoware_piwik.heartbeat.delay', $config['heartbeat']['delay']);
}
}

+ 38
- 0
EventListener/PiwikCustomVariable.php View File

@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\EventListener;

class PiwikCustomVariable {
private $name;
private $value;
public function __construct($name, $value) {
$this->name = $name;
$this->value = $value;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setValue($value) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
}

+ 24
- 0
EventListener/PiwikEcommerceCartUpdate.php View File

@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\EventListener;

class PiwikEcommerceCartUpdate {
private $amount;
public function __construct($amount) {
$this->amount = $amount;
}
public function getAmount() {
return $this->amount;
}
}

+ 38
- 0
EventListener/PiwikEcommerceItem.php View File

@@ -0,0 +1,38 @@
<?php
namespace Shadoware\PiwikBundle\EventListener;

class PiwikEcommerceItem {
private $productSKU;
private $productName;
private $productCategory;
private $price;
private $quantiy;
public function __construct($productSKU, $productName, $productCategory, $price, $quantity) {
$this->productSKU = $productSKU;
$this->productName = $productName;
$this->productCategory = $productCategory;
$this->price = $price;
$this->quantiy = $quantity;
}
public function getProductSKU() {
return $this->productSKU;
}
public function getProductName() {
return $this->productName;
}
public function getProductCategory() {
return $this->productCategory;
}
public function getPrice() {
return $this->price;
}
public function getQuantity() {
return $this->quantiy;
}
}

+ 54
- 0
EventListener/PiwikEcommerceOrder.php View File

@@ -0,0 +1,54 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\EventListener;

class PiwikEcommerceOrder {
private $orderId;
private $grandTotal;
private $subTotal;
private $tax;
private $shipping;
private $discount;
public function __construct($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount) {
$this->orderId = $orderId;
$this->grandTotal = $grandTotal;
$this->subTotal = $subTotal;
$this->tax = $tax;
$this->shipping = $shipping;
$this->discount = $discount;
}
public function getOrderId() {
return $this->orderId;
}
public function getGrandTotal() {
return $this->grandTotal;
}
public function getSubTotal() {
return $this->subTotal;
}
public function getTax() {
return $this->tax;
}
public function getShipping() {
return $this->shipping;
}
public function getDiscount() {
return $this->discount;
}
}

+ 42
- 0
EventListener/PiwikEcommerceView.php View File

@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\EventListener;

class PiwikEcommerceView {
private $productSKU;
private $productName;
private $productCategory;
private $productPrice;
public function __construct($productSKU, $productName, $productCategory, $productPrice) {
$this->productSKU = $productSKU;
$this->productName = $productName;
$this->productCategory = $productCategory;
$this->productPrice = $productPrice;
}
public function getProductSKU() {
return $this->productSKU;
}
public function getProductName() {
return $this->productName;
}
public function getProductCategory() {
return $this->productCategory;
}
public function getProductPrice() {
return $this->productPrice;
}
}

+ 210
- 0
EventListener/PiwikListener.php View File

@@ -0,0 +1,210 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Response;

class PiwikListener implements EventSubscriberInterface {
protected $templating;
protected $piwikUrl;
protected $piwikId;
protected $tokenId;
protected $async;
protected $hidePiwik;
protected $heartbeatEnabled = false;
protected $heartbeatMinimumVisitLength = 15;
protected $heartbeatDelay = 30;
protected $trackSiteSearchKeyword = "";
protected $trackSiteSearchCategory = "";
protected $trackSiteSearchCount = false;
protected $customVariables = array();
protected $ecommerceItems = array();
protected $ecommerceView = null;
protected $ecommerceOrder = null;
protected $ecommerceCartUpdate = null;
public function __construct(TwigEngine $templating, $piwikUrl, $piwikId, $hidePiwik = false, $tokenId = "", $async = false, $heartbeatEnabled = false, $heartbeatMinimumVisitLength = 15, $heartbeatDelay = 30)
{
$this->templating = $templating;
$this->piwikUrl = $piwikUrl;
$this->piwikId = (integer) $piwikId;
$this->hidePiwik = (Boolean) $hidePiwik;
$this->tokenId = $tokenId;
$this->async = (Boolean) $async;
$this->heartbeatEnabled = $heartbeatEnabled;
$this->heartbeatMinimumVisitLength = $heartbeatMinimumVisitLength;
$this->heartbeatDelay = $heartbeatDelay;
}
public function getPiwikURL() {
return $this->piwikUrl;
}
public function getPiwikId() {
return $this->piwikId;
}
public function getTokenId() {
return $this->tokenId;
}
public function setCustomVariable ($index, $name, $value, $scope = "visit") {
$this->customVariables[$scope][$index] = new PiwikCustomVariable($name, $value);
}
public function deleteCustomVariable($index, $scope) {
unset($this->customVariables[$scope][$index]);
}
public function getCustomVariable($index, $scope = "visit") {
return $this->customVariables[$scope][$index];
}
public function trackSiteSearch($keyword, $category = '', $count = false) {
$this->trackSiteSearchKeyword = $keyword;
$this->trackSiteSearchCategory = $category;
$this->trackSiteSearchCount = $count;
}
public function addEcommerceItem($productSKU, $productName = null, $productCategory = null, $price = null, $quantity = null) {
$this->ecommerceItems[] = new PiwikEcommerceItem($productSKU, $productName, $productCategory, $price, $quantity);
}
public function trackEcommerceOrder($orderId, $grandTotal, $subTotal = null, $tax = null, $shipping = null, $discount = null) {
$this->ecommerceOrder = new PiwikEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount);
}
public function trackEcommerceCartUpdate($amount) {
$this->ecommerceCartUpdate = new PiwikEcommerceCartUpdate($amount);
}
public function setEcommerceView($productSKU, $productName = null, $categoryName = null, $price = null) {
$this->ecommerceView = new PiwikEcommerceView($productSKU, $productName, $categoryName, $price);
}
public function onKernelResponse(FilterResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$response = $event->getResponse();
$request = $event->getRequest();
// do not capture redirects or modify XML HTTP Requests
if ($request->isXmlHttpRequest()) {
return;
}
$this->injectPiwik($response);
}
protected function injectPiwik(Response $response)
{
if (function_exists('mb_stripos')) {
$posrFunction = 'mb_strripos';
$substrFunction = 'mb_substr';
} else {
$posrFunction = 'strripos';
$substrFunction = 'substr';
}
$content = $response->getContent();
$posEndHead = $posrFunction($content, '</head>');
if (false !== $posEndHead) {
if ($this->async == true) {
$piwik_js = "\n".str_replace("\n", '', $this->templating->render(
'ShadowarePiwikBundle:Default:async_piwik_js.html.twig',
array(
'url' => $this->piwikUrl,
'id_site' => $this->piwikId,
'heartbeatEnabled' => $this->heartbeatEnabled,
'heartbeatMinimumVisitLength' => $this->heartbeatMinimumVisitLength,
'heartbeatDelay' => $this->heartbeatDelay,
'customVariables' => $this->customVariables,
'trackSiteSearchKeyword' => $this->trackSiteSearchKeyword,
'trackSiteSearchCategory' => $this->trackSiteSearchCategory,
'trackSiteSearchCount' => $this->trackSiteSearchCount,
'ecommerceView' => $this->ecommerceView,
'ecommerceItems' => $this->ecommerceItems,
'ecommerceOrder' => $this->ecommerceOrder,
'ecommerceCartUpdate' => $this->ecommerceCartUpdate,
'hidePiwik' => $this->hidePiwik,
)
))."\n";
}
else {
$piwik_js = "";
}
$content = $substrFunction($content, 0, $posEndHead).$piwik_js.$substrFunction($content, $posEndHead);
$response->setContent($content);
}
$posEndBody = $posrFunction($content, '</body>');
if (false !== $posEndBody) {
if ($this->async == false) {
$piwik_js = "\n".str_replace("\n", '', $this->templating->render(
'ShadowarePiwikBundle:Default:sync_piwik_js.html.twig',
array(
'url' => $this->piwikUrl,
'id_site' => $this->piwikId,
'heartbeatEnabled' => $this->heartbeatEnabled,
'heartbeatMinimumVisitLength' => $this->heartbeatMinimumVisitLength,
'heartbeatDelay' => $this->heartbeatDelay,
'customVariables' => $this->customVariables,
'trackSiteSearchKeyword' => $this->trackSiteSearchKeyword,
'trackSiteSearchCategory' => $this->trackSiteSearchCategory,
'trackSiteSearchCount' => $this->trackSiteSearchCount,
'ecommerceView' => $this->ecommerceView,
'ecommerceItems' => $this->ecommerceItems,
'ecommerceOrder' => $this->ecommerceOrder,
'ecommerceCartUpdate' => $this->ecommerceCartUpdate,
'hidePiwik' => $this->hidePiwik,
)
))."\n";
}
else {
$piwik_js = "";
}
if (! $this->hidePiwik) {
$piwik_nojs = "\n".str_replace("\n", '', $this->templating->render(
'ShadowarePiwikBundle:Default:piwik_nojs.html.twig',
array(
'url' => $this->piwikUrl,
'id_site' => $this->piwikId,
)
))."\n";
}
else {
$piwik_nojs = "";
}
$content = $substrFunction($content, 0, $posEndBody).$piwik_js.$piwik_nojs.$substrFunction($content, $posEndBody);
$response->setContent($content);
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::RESPONSE => array('onKernelResponse', -128),
);
}
}

+ 19
- 0
LICENCE View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 VANDENHEKKE Ulrich

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 45
- 0
README.rst View File

@@ -0,0 +1,45 @@
SHADOWARE PIWIK BUNDLE
======================

Introduction
------------

This bundle can be used to add Piwik javascript to your symfony2 application.

Features are :
- Add javascript tracking before </body> or asynchronous javascript before </head>.
- Can hide the address of the piwik server, by proxying the JS and call to the PHP script.
- Easily add
- search tracking
- product and category ecommerce view.
- basket and sell javascript
Call can be made from Twig or directly from controller.

Configuration
-------------

In the file config.yml

shadoware_piwik:
base_url: %piwik_base_url%
id_site: %piwik_id_site%
hidePiwik: true
tokenId: %piwik_tokenId%
async: true
heartbeat:
enabled: true
minimumVisitLength: 15
delay: 30

In the file parameters.yml

parameters:
paypal_username: ~
paypal_password: ~
paypal_signature: ~

piwik_base_url: ~
piwik_id_site: ~
piwik_tokenId: ~

+ 39
- 0
Resources/config/services.xml View File

@@ -0,0 +1,39 @@
<?xml version="1.0"?>

<!--
This file is part of the ShadowarePiwikBundle package.
(c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
-->

<container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd" >

<parameters>
<parameter key="shadoware_piwik.service.class" >Shadoware\PiwikBundle\EventListener\PiwikListener</parameter>
<parameter key="shadoware_piwik.twig.class" >Shadoware\PiwikBundle\Twig\PiwikExtension</parameter>
</parameters>

<services>
<service id="shadoware_piwik.service" class="%shadoware_piwik.service.class%" >
<tag name="kernel.event_subscriber" />
<argument type="service" id="templating.engine.twig" />
<argument>%shadoware_piwik.base_url%</argument>
<argument>%shadoware_piwik.id_site%</argument>
<argument>%shadoware_piwik.hidePiwik%</argument>
<argument>%shadoware_piwik.tokenId%</argument>
<argument>%shadoware_piwik.async%</argument>
<argument>%shadoware_piwik.heartbeat.enabled%</argument>
<argument>%shadoware_piwik.heartbeat.minimumVisitLength%</argument>
<argument>%shadoware_piwik.heartbeat.delay%</argument>
</service>
<service id="shadoware_piwik.twig" class="%shadoware_piwik.twig.class%">
<tag name="twig.extension"/>
<argument type="service" id="service_container" />
</service>
</services>
</container>

+ 94
- 0
Resources/views/Default/async_piwik_js.html.twig View File

@@ -0,0 +1,94 @@
{#
# This file is part of the ShadowarePiwikBundle package.
#
# (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
#
# For the full copyright and license information, please view the LICENSE
# file that was distributed with this source code.
#}
{% spaceless %}
<!-- Piwik -->
<script type="text/javascript">/*<![CDATA[*/
var _paq = _paq || [];
{% if not hidePiwik %}
(function(){ var u=(("https:" == document.location.protocol) ? "https://{{ url }}/" : "http://{{ url }}/");
_paq.push(['setTrackerUrl', u+'piwik.php']);
{% else %}
(function(){ _paq.push(['setTrackerUrl', '{{ url('shadoware_piwik_default_track') }}']);
{% endif %}
_paq.push(['setSiteId', {{ id_site }}]);

{# Heartbeat #}
{% if heartbeatEnabled %}
_paq.push(['setHeartBeatTimer', {{ heartbeatMinimumVisitLength }}, {{ heartbeatDelay }}]);
{% endif %}

{# Custom variables #}
{% for scope, array in customVariables %}
{% for index, customVariable in array %}
_paq.push(['setCustomVariable', {{ index }}, '{{ customVariable.name }}', '{{ customVariable.value }}', '{{ scope }}']);
{% endfor %}
{% endfor %}

{# Search keyword #}
{% if not trackSiteSearchKeyword is empty %}
_paq.push(['trackSiteSearch', '{{ trackSiteSearchKeyword }}', '{{ trackSiteSearchCategory }}', {% if not trackSiteSearchCount %}false{% else %}{{ trackSiteSearchCount }}{% endif %}]);
{% endif %}

{# E-Commerce - View #}
{% if ecommerceView %}
_paq.push(['setEcommerceView',
{% if not ecommerceView.productSKU %}false{% else %}'{{ ecommerceView.productSKU }}'{% endif %},
{% if not ecommerceView.productName %}false{% else %}'{{ ecommerceView.productName }}'{% endif %},
{% if not ecommerceView.productCategory %}''{% else %}'{{ ecommerceView.productCategory }}'{% endif %}
{% if ecommerceView.productPrice %},{{ ecommerceView.productPrice }}{% endif %}
]);
{% endif %}

{# E-Commerce - Items #}
{% for item in ecommerceItems %}
_paq.push(['addEcommerceItem',
'{{ item.productSKU }}',
{% if not item.productName %}''{% else %}'{{ item.productName }}'{% endif %},
{% if not item.productCategory %}''{% else %}'{{ item.productCategory }}'{% endif %},
{% if not item.price %}''{% else %}{{ item.price }}{% endif %}
{% if item.quantity %},{{ item.quantity }}{% endif %}
]);
{% endfor %}

{# E-Commerce - Final order #}
{% if ecommerceOrder and ecommerceItems | length > 0 %}
_paq.push(['trackEcommerceOrder',
'{{ ecommerceOrder.orderId }}',
{{ ecommerceOrder.grandTotal }},
{% if ecommerceOrder.subTotal %}{{ ecommerceOrder.subTotal }}{% else %}''{% endif %},
{% if ecommerceOrder.tax %}{{ ecommerceOrder.tax }}{% else %}''{% endif %},
{% if ecommerceOrder.shipping %}{{ ecommerceOrder.shipping }}{% else %}''{% endif %},
{% if ecommerceOrder.discount %}{{ ecommerceOrder.discount }}{% else %}false{% endif %}
]);
{% endif %}

{# E-Commerce - Basket #}
{% if ecommerceCartUpdate and ecommerceItems | length > 0 %}
_paq.push(['trackEcommerceCartUpdate',
{{ ecommerceCartUpdate.amount }}
]);
{% endif %}

{# Enable link tracking #}
_paq.push(['trackPageView']);

{# Really track the page #}
_paq.push(['enableLinkTracking']);

{# Definition of the Piwik script #}
{% if not hidePiwik %}
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); })();
{% else %}
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.type='text/javascript'; g.defer=true; g.async=true; g.src='{{ url('shadoware_piwik_default_tracker', {'_format': 'js'}) }}';
s.parentNode.insertBefore(g,s); })();
{% endif %}
/*]]>*/</script>
<!-- End Piwik Code -->
{% endspaceless %}

+ 9
- 0
Resources/views/Default/piwik_nojs.html.twig View File

@@ -0,0 +1,9 @@
{#
# This file is part of the ShadowarePiwikBundle package.
#
# (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
#
# For the full copyright and license information, please view the LICENSE
# file that was distributed with this source code.
#}
<noscript><p><img src="http://{{ url }}/piwik.php?idsite={{ id_site }}" style="border:0" alt="" /></p></noscript>

+ 95
- 0
Resources/views/Default/sync_piwik_js.html.twig View File

@@ -0,0 +1,95 @@
{#
# This file is part of the ShadowarePiwikBundle package.
#
# (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
#
# For the full copyright and license information, please view the LICENSE
# file that was distributed with this source code.
#}
{% spaceless %}
<!-- Piwik -->
{# Definition of the Piwik script #}
<script type="text/javascript">/*<![CDATA[*/
{% if not hidePiwik %}
var pkBaseURL = (("https:" == document.location.protocol) ? "https://{{ url }}/" : "http://{{ url }}/");
document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
{% else %}
document.write(unescape("%3Cscript src='{{ url('shadoware_piwik_default_tracker', {'_format': 'js'}) }}' type='text/javascript'%3E%3C/script%3E"));
{% endif %}
/*]]>*/</script>
{# Tracking the page #}
<script type="text/javascript">/*<![CDATA[*/
try {
{# Piwik Tracker #}
{% if not hidePiwik %}
var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", {{ id_site }});
{% else %}
var piwikTracker = Piwik.getTracker("{{ url('shadoware_piwik_default_track') }}", {{ id_site }});
{% endif %}

{# Heartbeat #}
{% if heartbeatEnabled %}
piwikTracker.setHeartBeatTimer({{ heartbeatMinimumVisitLength }}, {{ heartbeatDelay }});
{% endif %}

{# Custom variables #}
{% for scope, array in customVariables %}
{% for index, customVariable in array %}
piwikTracker.setCustomVariable({{ index }}, '{{ customVariable.name }}', '{{ customVariable.value }}', '{{ scope }}');
{% endfor %}
{% endfor %}

{# Search keyword #}
{% if not trackSiteSearchKeyword is empty %}
piwikTracker.trackSiteSearch('{{ trackSiteSearchKeyword }}', '{{ trackSiteSearchCategory }}', {% if not trackSiteSearchCount %}false{% else %}{{ trackSiteSearchCount }}{% endif %});
{% endif %}

{# E-Commerce - View #}
{% if ecommerceView %}
piwikTracker.setEcommerceView(
{% if not ecommerceView.productSKU %}false{% else %}'{{ ecommerceView.productSKU }}'{% endif %},
{% if not ecommerceView.productName %}false{% else %}'{{ ecommerceView.productName }}'{% endif %},
{% if not ecommerceView.productCategory %}''{% else %}'{{ ecommerceView.productCategory }}'{% endif %}
{% if ecommerceView.productPrice %},{{ ecommerceView.productPrice }}{% endif %}
);
{% endif %}

{# E-Commerce - Items #}
{% for item in ecommerceItems %}
piwikTracker.addEcommerceItem(
'{{ item.productSKU }}',
{% if not item.productName %}''{% else %}'{{ item.productName }}'{% endif %},
{% if not item.productCategory %}''{% else %}'{{ item.productCategory }}'{% endif %},
{% if not item.price %}''{% else %}{{ item.price }}{% endif %}
{% if item.quantity %},{{ item.quantity }}{% endif %}
);
{% endfor %}

{# E-Commerce - Final order #}
{% if ecommerceOrder and ecommerceItems | length > 0 %}
piwikTracker.trackEcommerceOrder(
'{{ ecommerceOrder.orderId }}',
{{ ecommerceOrder.grandTotal }},
{% if ecommerceOrder.subTotal %}{{ ecommerceOrder.subTotal }}{% else %}''{% endif %},
{% if ecommerceOrder.tax %}{{ ecommerceOrder.tax }}{% else %}''{% endif %},
{% if ecommerceOrder.shipping %}{{ ecommerceOrder.shipping }}{% else %}''{% endif %},
{% if ecommerceOrder.discount %}{{ ecommerceOrder.discount }}{% else %}false{% endif %}
);
{% endif %}

{# E-Commerce - Basket #}
{% if ecommerceCartUpdate and ecommerceItems | length > 0 %}
piwikTracker.trackEcommerceCartUpdate(
{{ ecommerceCartUpdate.amount }}
);
{% endif %}

{# Enable link tracking #}
piwikTracker.enableLinkTracking();

{# Really track the page #}
piwikTracker.trackPageView();
} catch( err ) {}
/*]]>*/</script>
<!-- End Piwik Tracking Code -->
{% endspaceless %}

+ 9
- 0
ShadowarePiwikBundle.php View File

@@ -0,0 +1,9 @@
<?php

namespace Shadoware\PiwikBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class ShadowarePiwikBundle extends Bundle
{
}

+ 69
- 0
Twig/PiwikExtension.php View File

@@ -0,0 +1,69 @@
<?php

/*
* This file is part of the ShadowarePiwikBundle package.
*
* (c) Ulrich VANDENHEKKE <ulrich.vdh@shadoware.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Shadoware\PiwikBundle\Twig;
use Symfony\Component\DependencyInjection\ContainerInterface;

class PiwikExtension extends \Twig_Extension {
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getFunctions() {
return array(
'setCustomVariable' => new \Twig_Function_Method($this, 'setCustomVariable'),
'getCustomVariableJS' => new \Twig_Function_Method($this, 'getCustomVariable'),
'trackSiteSearch' => new \Twig_Function_Method($this, 'trackSiteSearch'),
'addEcommerceItem' => new \Twig_Function_Method($this, 'addEcommerceItem'),
'trackEcommerceOrder' => new \Twig_Function_Method($this, 'trackEcommerceOrder'),
'trackEcommerceCartUpdate' => new \Twig_Function_Method($this, 'trackEcommerceCartUpdate'),
'setEcommerceView' => new \Twig_Function_Method($this, 'setEcommerceView'),
);
}
public function setCustomVariable($index, $name, $value, $scope = "visit") {
$this->container->get('shadoware_piwik.service')->setCustomVariable($index, $name, $value, $scope);
}
public function getCustomVariable($index, $scope = "visit") {
return "piwikTracker.getCustomVariable(".$index.", '".$scope."')";
}
public function trackSiteSearch($keyword, $category = '', $count = false) {
$this->container->get('shadoware_piwik.service')->trackSiteSearch($keyword, $category, $count);
}
public function addEcommerceItem($productSKU, $productName = null, $productCategory = null, $price = null, $quantity = null) {
$this->container->get('shadoware_piwik.service')->addEcommerceItem($productSKU, $productName, $productCategory, $price, $quantity);
}
public function trackEcommerceOrder($orderId, $grandTotal, $subTotal = null, $tax = null, $shipping = null, $discount = null) {
$this->container->get('shadoware_piwik.service')->trackEcommerceOrder($orderId, $grandTotal, $subTotal, $tax, $shipping, $discount);
}
public function trackEcommerceCartUpdate($ammount) {
$this->container->get('shadoware_piwik.service')->trackEcommerceCartUpdate($ammount);
}
public function setEcommerceView($productSKU, $productName = null, $categoryName = null, $price = null) {
$this->container->get('shadoware_piwik.service')->setEcommerceView($productSKU, $productName, $categoryName, $price);
}
public function getName() {
return 'piwik_extension';
}
}

+ 23
- 0
composer.json View File

@@ -0,0 +1,23 @@
{
"name": "shadoware/piwikbundle",
"type": "symfony-bundle";
"description": "Plugin to use Piwik in your symfony2 project.",
"keywords": ["symfony2", "bundles", "piwik", "analytic"],
"homepage": "https://hg.shadoware.org/Software/Symfony2Bundle/PiwikBundle",
"licence": "MIT",
"authors": [{
"name": "VANDENHEKKE Ulrich",
"email": "ulrich.vdh@shadoware.org"
}],
"require": {
"php": ">= 5.3.3"
},
"require-dev": {
"symfony/framework-bundle": ">=2.1,<2.2-dev"
},
"autoload": {
"psr-0": { "Shadoware\\PiwikBundle" }
},
"minimum-stability": "dev",
"target-dir": "Shadoware/PiwikBundle"
}

Loading…
Cancel
Save