diff --git a/config/config.ini.php b/config/config.ini.php index d850e8e2d6599505a0c19fad9760f7757e87b151..782d20195c29f4311b8174b9275d796d67320e68 100755 --- a/config/config.ini.php +++ b/config/config.ini.php @@ -7,7 +7,7 @@ host = localhost username = root password = nintendo dbname = piwiktrunk -adapter = PDO_MYSQL +adapter = PDO_MYSQL ; PDO_MYSQL or MYSQLI tables_prefix = piwik_ profiler = true @@ -15,6 +15,10 @@ profiler = true dbname = piwiktests tables_prefix = piwiktests_ +[LogStats] +; set to 0 if you want to stop tracking the visitors. Useful if you need to stop all the connections on the DB. +record_statistics = 1 + [log] diff --git a/index.php b/index.php index 6f4246f90b4819870e9d298e598c5c1438dfbd4d..5aa67895f5f4f66e85ea34a42e52559a3b645f5d 100755 --- a/index.php +++ b/index.php @@ -1,16 +1,11 @@ <?php -/* +/** * PHP Configuration init */ error_reporting(E_ALL|E_NOTICE); date_default_timezone_set('Europe/London'); define('PIWIK_INCLUDE_PATH', '.'); -require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php"; -set_error_handler('Piwik_ErrorHandler'); -require_once PIWIK_INCLUDE_PATH . "/modules/ExceptionHandler.php"; -set_exception_handler('Piwik_ExceptionHandler'); - set_include_path(PIWIK_INCLUDE_PATH . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/' . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/' @@ -22,7 +17,15 @@ assert_options(ASSERT_ACTIVE, 1); assert_options(ASSERT_WARNING, 1); assert_options(ASSERT_BAIL, 1); -/* +/** + * Error / exception handling functions + */ +require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php"; +set_error_handler('Piwik_ErrorHandler'); +require_once PIWIK_INCLUDE_PATH . "/modules/ExceptionHandler.php"; +set_exception_handler('Piwik_ExceptionHandler'); + +/** * Zend classes */ include "Zend/Exception.php"; @@ -35,7 +38,7 @@ Zend_Loader::loadClass('Zend_Debug'); Zend_Loader::loadClass('Zend_Auth'); Zend_Loader::loadClass('Zend_Auth_Adapter_DbTable'); -/* +/** * Piwik classes */ Zend_Loader::loadClass('Piwik_Access'); @@ -103,4 +106,21 @@ function main() $api->UsersManager->addUser("login", "password", "email@geage.com"); } -?> \ No newline at end of file +?> + + + +<!-- Piwik --> +<a href="http://piwik.org" title="Web analytics" onclick="window.open(this.href);return(false);"> +<script language="javascript" src="piwik.js" type="text/javascript"></script> +<script type="text/javascript"> +<!-- + piwik_action_name = ''; + piwik_idsite = 1; + piwik_url = "http://localhost/dev/piwiktrunk/piwik.php"; + piwik_log(piwik_action_name, piwik_idsite, piwik_url); +//--> +</script><object> +<noscript><p>Web analytics<img src="http://localhost/dev/piwiktrunk/piwik.php" style="border:0" /></p> +</noscript></object></a> +<!-- /Piwik --> \ No newline at end of file diff --git a/libs/Event/Dispatcher.php b/libs/Event/Dispatcher.php new file mode 100755 index 0000000000000000000000000000000000000000..1fa91f85a5203e823b272eef05beca117b5e4cd2 --- /dev/null +++ b/libs/Event/Dispatcher.php @@ -0,0 +1,478 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2005, Bertrand Mansion | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o 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.| +// | o The names of the authors may not 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 | +// | OWNER 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. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Bertrand Mansion <bmansion@mamasam.com> | +// | Stephan Schmidt <schst@php.net> | +// +-----------------------------------------------------------------------+ +// +// $Id: Dispatcher.php,v 1.4 2005/09/22 15:37:10 schst Exp $ + +require_once 'Event/Notification.php'; + +/** + * Pseudo 'static property' for Notification object + * @global array $GLOBALS["_Event_Dispatcher"] + */ +$GLOBALS['_Event_Dispatcher'] = array( + 'NotificationClass' => 'Event_Notification' + ); + +/** + * Registers a global observer + */ +define('EVENT_DISPATCHER_GLOBAL', ''); + +/** + * Dispatch notifications using PHP callbacks + * + * The Event_Dispatcher acts acts as a notification dispatch table. + * It is used to notify other objects of interesting things, if + * they meet certain criteria. This information is encapsulated + * in {@link Event_Notification} objects. Client objects register + * themselves with the Event_Dispatcher as observers of specific + * notifications posted by other objects. When an event occurs, + * an object posts an appropriate notification to the Event_Dispatcher. + * The Event_Dispatcher dispatches a message to each + * registered observer, passing the notification as the sole argument. + * + * The Event_Dispatcher is actually a combination of three design + * patterns: the Singleton, {@link http://c2.com/cgi/wiki?MediatorPattern Mediator}, + * and Observer patterns. The idea behind Event_Dispatcher is borrowed from + * {@link http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework}. + * + * @category Event + * @package Event_Dispatcher + * @author Bertrand Mansion <bmansion@mamasam.com> + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/Event_Dispatcher + */ +class Event_Dispatcher +{ + /** + * Registered observer callbacks + * @var array + * @access private + */ + var $_ro = array(); + + /** + * Pending notifications + * @var array + * @access private + */ + var $_pending = array(); + + /** + * Nested observers + * @var array + * @access private + */ + var $_nestedDispatchers = array(); + + /** + * Name of the dispatcher + * @var string + * @access private + */ + var $_name = null; + + /** + * Class used for notifications + * @var string + * @access private + */ + var $_notificationClass = null; + + /** + * PHP4 constructor + * + * Please use {@link getInstance()} instead. + * + * @access private + * @param string Name of the notification dispatcher. + */ + function Event_Dispatcher($name) + { + Event_Dispatcher::__construct($name); + } + + /** + * PHP5 constructor + * + * Please use {@link getInstance()} instead. + * + * @access private + * @param string Name of the notification dispatcher. + */ + function __construct($name) + { + $this->_name = $name; + $this->_notificationClass = $GLOBALS['_Event_Dispatcher']['NotificationClass']; + } + + /** + * Returns a notification dispatcher singleton + * + * There is usually no need to have more than one notification + * center for an application so this is the recommended way + * to get a Event_Dispatcher object. + * + * @param string Name of the notification dispatcher. + * The default notification dispatcher is named __default. + * + * @return object Event_Dispatcher + */ + function &getInstance($name = '__default') + { + static $dispatchers = array(); + + if (!isset($dispatchers[$name])) { + $dispatchers[$name] = new Event_Dispatcher($name); + } + + return $dispatchers[$name]; + } + + /** + * Registers an observer callback + * + * This method registers a {@link http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback callback} + * which is called when the notification corresponding to the + * criteria given at registration time is posted. + * The criteria are the notification name and eventually the + * class of the object posted with the notification. + * + * If there are any pending notifications corresponding to the criteria + * given here, the callback will be called straight away. + * + * If the notification name is empty, the observer will receive all the + * posted notifications. Same goes for the class name. + * + * @access public + * @param mixed A PHP callback + * @param string Expected notification name, serves as a filter + * @param string Expected contained object class, serves as a filter + * @return void + */ + function addObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null) + { + if (is_array($callback)) { + if (is_object($callback[0])) { + // Note : PHP4 does not allow correct object comparison so + // only the class name is used for registration checks. + $reg = get_class($callback[0]).'::'.$callback[1]; + } else { + $reg = $callback[0].'::'.$callback[1]; + } + } else { + $reg = $callback; + } + + $this->_ro[$nName][$reg] = array( + 'callback' => $callback, + 'class' => $class + ); + + // Post eventual pending notifications for this observer + if (isset($this->_pending[$nName])) { + foreach (array_keys($this->_pending[$nName]) as $k) { + $notification =& $this->_pending[$nName][$k]; + if (!$notification->isNotificationCancelled()) { + $objClass = get_class($notification->getNotificationObject()); + if (empty($class) || strcasecmp($class, $objClass) == 0) { + call_user_func_array($callback, array(&$notification)); + $notification->increaseNotificationCount(); + } + } + } + } + } + + /** + * Creates and posts a notification object + * + * The purpose of the optional associated object is generally to pass + * the object posting the notification to the observers, so that the + * observers can query the posting object for more information about + * the event. + * + * Notifications are by default added to a pending notification list. + * This way, if an observer is not registered by the time they are + * posted, it will still be notified when it is added as an observer. + * This behaviour can be turned off in order to make sure that only + * the registered observers will be notified. + * + * The info array serves as a container for any kind of useful + * information. It is added to the notification object and posted along. + * + * @access public + * @param object Notification associated object + * @param string Notification name + * @param array Optional user information + * @param bool Whether the notification is pending + * @param bool Whether you want the notification to bubble up + * @return object The notification object + */ + function &post(&$object, $nName, $info = array(), $pending = true, $bubble = true) + { + $notification =& new $this->_notificationClass($object, $nName, $info); + return $this->postNotification($notification, $pending, $bubble); + } + + /** + * Posts the {@link Event_Notification} object + * + * @access public + * @param object The Notification object + * @param bool Whether to post the notification immediately + * @param bool Whether you want the notification to bubble up + * @see Event_Dispatcher::post() + * @return object The notification object + */ + function &postNotification(&$notification, $pending = true, $bubble = true) + { + $nName = $notification->getNotificationName(); + if ($pending === true) { + $this->_pending[$nName][] =& $notification; + } + $objClass = get_class($notification->getNotificationObject()); + + // Find the registered observers + if (isset($this->_ro[$nName])) { + foreach (array_keys($this->_ro[$nName]) as $k) { + $rObserver =& $this->_ro[$nName][$k]; + if ($notification->isNotificationCancelled()) { + return $notification; + } + if (empty($rObserver['class']) || + strcasecmp($rObserver['class'], $objClass) == 0) { + call_user_func_array($rObserver['callback'], array(&$notification)); + $notification->increaseNotificationCount(); + } + } + } + + // Notify globally registered observers + if (isset($this->_ro[EVENT_DISPATCHER_GLOBAL])) { + foreach (array_keys($this->_ro[EVENT_DISPATCHER_GLOBAL]) as $k) { + $rObserver =& $this->_ro[EVENT_DISPATCHER_GLOBAL][$k]; + if ($notification->isNotificationCancelled()) { + return $notification; + } + if (empty($rObserver['class']) || + strcasecmp($rObserver['class'], $objClass) == 0) { + call_user_func_array($rObserver['callback'], array(&$notification)); + $notification->increaseNotificationCount(); + } + } + } + + if ($bubble === false) { + return $notification; + } + + // Notify in nested dispatchers + foreach (array_keys($this->_nestedDispatchers) as $nested) { + $notification =& $this->_nestedDispatchers[$nested]->postNotification($notification, $pending); + } + + return $notification; + } + + /** + * Removes a registered observer that correspond to the given criteria + * + * @access public + * @param mixed A PHP callback + * @param string Notification name + * @param string Contained object class + * @return bool True if an observer was removed, false otherwise + */ + function removeObserver($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null) + { + if (is_array($callback)) { + if (is_object($callback[0])) { + $reg = get_class($callback[0]).'::'.$callback[1]; + } else { + $reg = $callback[0].'::'.$callback[1]; + } + } else { + $reg = $callback; + } + + $removed = false; + if (isset($this->_ro[$nName][$reg])) { + if (!empty($class)) { + if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) { + unset($this->_ro[$nName][$reg]); + $removed = true; + } + } else { + unset($this->_ro[$nName][$reg]); + $removed = true; + } + } + + if (isset($this->_ro[$nName]) && count($this->_ro[$nName]) == 0) { + unset($this->_ro[$nName]); + } + return $removed; + } + + /** + * Check, whether the specified observer has been registered with the + * dispatcher + * + * @access public + * @param mixed A PHP callback + * @param string Notification name + * @param string Contained object class + * @return bool True if the observer has been registered, false otherwise + */ + function observerRegistered($callback, $nName = EVENT_DISPATCHER_GLOBAL, $class = null) + { + if (is_array($callback)) { + if (is_object($callback[0])) { + $reg = get_class($callback[0]).'::'.$callback[1]; + } else { + $reg = $callback[0].'::'.$callback[1]; + } + } else { + $reg = $callback; + } + + if (!isset($this->_ro[$nName][$reg])) { + return false; + } + if (empty($class)) { + return true; + } + if (strcasecmp($this->_ro[$nName][$reg]['class'], $class) == 0) { + return true; + } + return false; + } + + /** + * Get all observers, that have been registered for a notification + * + * @access public + * @param string Notification name + * @param string Contained object class + * @return array List of all observers + */ + function getObservers($nName = EVENT_DISPATCHER_GLOBAL, $class = null) + { + $observers = array(); + if (!isset($this->_ro[$nName])) { + return $observers; + } + foreach ($this->_ro[$nName] as $reg => $observer) { + if ($class == null || $observer['class'] == null || strcasecmp($observer['class'], $class) == 0) { + $observers[] = $reg; + } + } + return $observers; + } + + /** + * Get the name of the dispatcher. + * + * The name is the unique identifier of a dispatcher. + * + * @access public + * @return string name of the dispatcher + */ + function getName() + { + return $this->_name; + } + + /** + * add a new nested dispatcher + * + * Notifications will be broadcasted to this dispatcher as well, which + * allows you to create event bubbling. + * + * @access public + * @param Event_Dispatcher The nested dispatcher + */ + function addNestedDispatcher(&$dispatcher) + { + $name = $dispatcher->getName(); + $this->_nestedDispatchers[$name] =& $dispatcher; + } + + /** + * remove a nested dispatcher + * + * @access public + * @param Event_Dispatcher Dispatcher to remove + * @return boolean + */ + function removeNestedDispatcher($dispatcher) + { + if (is_object($dispatcher)) { + $dispatcher = $dispatcher->getName(); + } + if (!isset($this->_nestedDispatchers[$dispatcher])) { + return false; + } + unset($this->_nestedDispatchers[$dispatcher]); + return true; + } + + /** + * Changes the class used for notifications + * + * You may call this method on an object to change it for a single + * dispatcher or statically, to set the default for all dispatchers + * that will be created. + * + * @access public + * @param string name of the notification class + * @return boolean + */ + function setNotificationClass($class) + { + if (isset($this) && is_a($this, 'Event_Dispatcher')) { + $this->_notificationClass = $class; + return true; + } + $GLOBALS['_Event_Dispatcher']['NotificationClass'] = $class; + return true; + } + +} +?> \ No newline at end of file diff --git a/libs/Event/Notification.php b/libs/Event/Notification.php new file mode 100755 index 0000000000000000000000000000000000000000..ca3f3833f77d1bd53f9279c4a13d86da70afee8b --- /dev/null +++ b/libs/Event/Notification.php @@ -0,0 +1,194 @@ +<?php +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2005, Bertrand Mansion | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o 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.| +// | o The names of the authors may not 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 | +// | OWNER 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. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Bertrand Mansion <bmansion@mamasam.com> | +// | Stephan Schmidt <schst@php.net> | +// +-----------------------------------------------------------------------+ +// +// $Id: Notification.php,v 1.2 2005/02/14 10:35:18 mansion Exp $ + +/** + * Default state of the notification + */ +define('EVENT_NOTIFICATION_STATE_DEFAULT', 0); + +/** + * Notification has been cancelled + */ +define('EVENT_NOTIFICATION_STATE_CANCELLED', 1); + +/** + * A Notification object + * + * The Notification object can be easily subclassed and serves as a container + * for the information about the notification. It holds an object which is + * usually a reference to the object that posted the notification, + * a notification name used to identify the notification and some user + * information which can be anything you need. + * + * @category Event + * @package Event_Dispatcher + * @author Bertrand Mansion <bmansion@mamasam.com> + * @author Stephan Schmidt <schst@php.net> + * @copyright 1997-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/Event_Dispatcher + */ +class Event_Notification +{ + /** + * name of the notofication + * @var string + * @access private + */ + var $_notificationName; + + /** + * object of interesed (the sender of the notification, in most cases) + * @var object + * @access private + */ + var $_notificationObject; + + /** + * additional information about the notification + * @var mixed + * @access private + */ + var $_notificationInfo = array(); + + /** + * state of the notification + * + * This may be: + * - EVENT_NOTIFICATION_STATE_DEFAULT + * - EVENT_NOTIFICATION_STATE_CANCELLED + * + * @var integer + * @access private + */ + var $_notificationState = EVENT_NOTIFICATION_STATE_DEFAULT; + + /** + * amount of observers that received this notification + * @var mixed + * @access private + */ + var $_notificationCount = 0; + + /** + * Constructor + * + * @access public + * @param object The object of interest for the notification, + * usually is the posting object + * @param string Notification name + * @param array Free information array + */ + function Event_Notification(&$object, $name, $info = array()) + { + $this->_notificationObject =& $object; + $this->_notificationName = $name; + $this->_notificationInfo = $info; + } + + /** + * Returns the notification name + * @return string Notification name + */ + function getNotificationName() + { + return $this->_notificationName; + } + + /** + * Returns the contained object + * @return object Contained object + */ + function &getNotificationObject() + { + return $this->_notificationObject; + } + + /** + * Returns the user info array + * @return array user info + */ + function getNotificationInfo() + { + return $this->_notificationInfo; + } + + /** + * Increase the internal count + * + * @access public + */ + function increaseNotificationCount() + { + ++$this->_notificationCount; + } + + /** + * Get the number of posted notifications + * + * @access public + * @return int + */ + function getNotificationCount() + { + return $this->_notificationCount; + } + + /** + * Cancel the notification + * + * @access public + * @return void + */ + function cancelNotification() + { + $this->_notificationState = EVENT_NOTIFICATION_STATE_CANCELLED; + } + + /** + * Checks whether the notification has been cancelled + * + * @access public + * @return boolean + */ + function isNotificationCancelled() + { + return ($this->_notificationState === EVENT_NOTIFICATION_STATE_CANCELLED); + } +} +?> \ No newline at end of file diff --git a/libs/Zend/Db/Adapter/Mysqli.php b/libs/Zend/Db/Adapter/Mysqli.php index 51b8f29c27d18f7fbdb1e3bc7bcad928231b19af..903a8041be48554af979c01dbf8103e9d2b51382 100755 --- a/libs/Zend/Db/Adapter/Mysqli.php +++ b/libs/Zend/Db/Adapter/Mysqli.php @@ -243,9 +243,18 @@ class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract $port = null; } + if(!class_exists("mysqli")) + { + /** + * @see Zend_Db_Adapter_Mysqli_Exception + */ + require_once 'Zend/Db/Adapter/Mysqli/Exception.php'; + throw new Zend_Db_Adapter_Mysqli_Exception("The extension mysqli is not installed."); + } + // Suppress connection warnings here. // Throw an exception instead. - @$this->_connection = new mysqli( + $this->_connection = new mysqli( $this->_config['host'], $this->_config['username'], $this->_config['password'], diff --git a/modules/ExceptionHandler.php b/modules/ExceptionHandler.php index f6fd67e0f112ff2d3f1409ea26a5fd75e139c397..edcfc212854a3aecd1ac590f0b7c481f209a1e3f 100644 --- a/modules/ExceptionHandler.php +++ b/modules/ExceptionHandler.php @@ -5,9 +5,11 @@ function Piwik_ExceptionHandler(Exception $exception) try { Zend_Registry::get('logger_exception')->log($exception); } catch(Exception $e) { - print("<br> -------------------------- <br>An exception occured while dealing with an uncaught exception... <br>"); + print("<br> <b>Exception</b>: '". $exception->getMessage()."'"); + + print("<br> -------------------------- <br> + This exception occured and also raised this exception: "); print("'" . $e->getMessage()."'"); - print("<br> The initial exception was:Â <br>'". $exception->getMessage()."'"); } } ?> diff --git a/piwik.js b/piwik.js new file mode 100644 index 0000000000000000000000000000000000000000..032b5224020920c5038713f60e35b0a731e5ec82 --- /dev/null +++ b/piwik.js @@ -0,0 +1,103 @@ +//-- Web analytics by Piwik -- http://piwik.org +//-- Copyleft 2007, All rights reversed. + +var _pk_use_title_as_name = 0; + +//-- Beginning script +function _pk_plug_normal(_pk_pl) { + if (_pk_tm.indexOf(_pk_pl) != -1 && (navigator.mimeTypes[_pk_pl].enabledPlugin != null)) + return '1'; + return '0'; +} + +function _pk_plug_ie(_pk_pl){ + _pk_find = false; + document.write('<SCR' + 'IPT LANGUAGE=VBScript>\n on error resume next \n _pk_find = IsObject(CreateObject("' + _pk_pl + '")) </SCR' + 'IPT>\n'); + if (_pk_find) return '1'; + return '0'; +} + +var _pk_jav = '0'; if(navigator.javaEnabled()) _pk_jav='1'; +var _pk_agent = navigator.userAgent.toLowerCase(); +var _pk_moz = (navigator.appName.indexOf("Netscape") != -1); +var _pk_ie = (_pk_agent.indexOf("msie") != -1); +var _pk_win = ((_pk_agent.indexOf("win") != -1) || (_pk_agent.indexOf("32bit") != -1)); +var _pk_cookie = (navigator.cookieEnabled)? '1' : '0'; +if((typeof (navigator.cookieEnabled) == "undefined") && (_pk_cookie == '0')) { + document.cookie="_pk_testcookie" + _pk_cookie=(document.cookie.indexOf("_pk_testcookie")!=-1)? '1' : '0'; +} + +var _pk_dir='0',_pk_fla='0',_pk_pdf='0',_pk_qt = '0',_pk_rea = '0',_pk_wma='0'; +if (_pk_win && _pk_ie){ + _pk_dir = _pk_plug_ie("SWCtl.SWCtl.1"); + _pk_fla = _pk_plug_ie("ShockwaveFlash.ShockwaveFlash.1"); + if (_pk_plug_ie("PDF.PdfCtrl.1") == '1' || _pk_plug_ie('PDF.PdfCtrl.5') == '1' || _pk_plug_ie('PDF.PdfCtrl.6') == '1') + _pk_pdf = '1'; + _pk_qt = _pk_plug_ie("Quicktime.Quicktime"); // Old : "QuickTimeCheckObject.QuickTimeCheck.1" + _pk_rea = _pk_plug_ie("rmocx.RealPlayer G2 Control.1"); + _pk_wma = _pk_plug_ie("wmplayer.ocx"); // Old : "MediaPlayer.MediaPlayer.1" +} else { + var _pk_tm = ''; + for (var i=0; i < navigator.mimeTypes.length; i++) + _pk_tm += navigator.mimeTypes[i].type.toLowerCase(); + _pk_dir = _pk_plug_normal("application/x-director"); + _pk_fla = _pk_plug_normal("application/x-shockwave-flash"); + _pk_pdf = _pk_plug_normal("application/pdf"); + _pk_qt = _pk_plug_normal("video/quicktime"); + _pk_rea = _pk_plug_normal("audio/x-pn-realaudio-plugin"); + _pk_wma = _pk_plug_normal("application/x-mplayer2"); +} + +var _pk_rtu = ''; +try { + _pk_rtu = top.document.referrer; +} catch(e1) { + if(parent){ + try{ _pk_rtu = parent.document.referrer; } catch(e2) { _pk_rtu=''; } + } +} +if(_pk_rtu == '') { + _pk_rtu = document.referrer; +} + +function _pk_escape(_pk_str){ + if (typeof(encodeURIComponent) == 'function') { + return encodeURIComponent(_pk_str); + } else { + return escape(_pk_str); + } +} +var _pk_called; + +function _pk_getUrlLog( _pk_action_name, _pk_site, _pk_pkurl ) +{ + var _pk_url = document.location.href; + var _pk_da = new Date(); + var _pk_src = _pk_pkurl + +'?url='+_pk_escape(_pk_url) + +'&aname='+_pk_escape(_pk_action_name) + +'&id='+_pk_site + +'&res='+screen.width+'x'+screen.height + +'&col='+screen.colorDepth + +'&h='+_pk_da.getHours()+'&m='+_pk_da.getMinutes()+'&s='+_pk_da.getSeconds() + +'&flash='+_pk_fla + +'&director='+_pk_dir + +'&quicktime='+_pk_qt + +'&realplayer='+_pk_rea + +'&pdf='+_pk_pdf + +'&windowsmedia='+_pk_wma + +'&java='+_pk_jav + +'&cookie='+_pk_cookie + +'&urlref='+_pk_escape(_pk_rtu); + return _pk_src; +} + +function piwik_log( _pk_action_name, _pk_site, _pk_pkurl ) +{ + if(_pk_called && (!_pk_action_name || _pk_action_name=="")) return; + var _pk_src = _pk_getUrlLog(_pk_action_name, _pk_site, _pk_pkurl ); + document.writeln('<img src="'+_pk_src+'" alt="Piwik" style="border:0" />'); + if(!_pk_action_name || _pk_action_name=="") _pk_called=1; +} + diff --git a/piwik.php b/piwik.php index 58436c979c5c608f970795679a0bceba65d58f3d..ebadb700a354aa499b2fdc6b9c3ba7134b787cc2 100644 --- a/piwik.php +++ b/piwik.php @@ -1,5 +1,123 @@ <?php +error_reporting(E_ALL|E_NOTICE); +define('PIWIK_INCLUDE_PATH', '.'); + +@ignore_user_abort(true); +@set_time_limit(0); +set_include_path(PIWIK_INCLUDE_PATH + . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/' + . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/' + . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/modules' + . PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core/models' + . PATH_SEPARATOR . get_include_path() ); + +require_once "Event/Dispatcher.php"; + +function printDebug( $str = '' ) +{ + print($str . "<br>\n"); +} + + + +/* + * Some benchmarks + * + * - with the config parsing + db connection + * Requests per second: 471.91 [#/sec] (mean) + * + * + */ + +/** + * Simple database PDO wrapper + * + */ + +//Constante com caminhos para includes +define("PATH_INCLUDES", $_SERVER['DOCUMENT_ROOT']."/conn"); + +class Piwik_LogStats_Db +{ + private $connection; + private $username; + private $password; + + public function __construct( $host, $username, $password, $dbname) + { + $this->dsn = "mysql:dbname=$dbname;host=$host"; + $this->username = $username; + $this->password = $password; + } + + public function connect() + { + try { + $pdoConnect = new PDO($this->dsn, $this->username, $this->password); + $pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->connection = $pdoConnect; + } catch (PDOException $e) { + throw new Exception("Error connecting database: ".$e->getMessage()); + } + } + + public function fetchAll( $query ) + { + try { + $sth = $this->connexion->prepare($query); + $sth->execute(); + return $sth->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + throw new Exception("Error connecting database: ".$e->getMessage()); + } + } + + public function query($query) + { + if (!$this->connection->query($query)) { + throw new Exception($this->connection->errorInfo()); + } else { + return true; + } + } +} + +class Piwik_LogStats_Config +{ + static private $instance = null; + + static public function getInstance() + { + if (self::$instance == null) + { + $c = __CLASS__; + self::$instance = new $c(); + } + return self::$instance; + } + + public $config = array(); + + private function __construct() + { + $pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php'; + $this->config = parse_ini_file($pathIniFile, true); + } + + public function __get( $name ) + { + if(isset($this->config[$name])) + { + return $this->config[$name]; + } + else + { + throw new Exception("The config element $name is not available in the configuration (check the configuration file)."); + } + } +} + /** * Plugin specification for a statistics logging plugin @@ -22,7 +140,189 @@ * - register to hooks in other plugins * - generally a plugin method can modify data (filter) and add/remove data * + * + */ + +class Piwik_PluginsManager +{ + public $dispatcher; + private $pluginsPath; + + static private $instance = null; + + static public function getInstance() + { + if (self::$instance == null) + { + $c = __CLASS__; + self::$instance = new $c(); + } + return self::$instance; + } + + private function __construct() + { + $this->pluginsPath = 'plugins/'; + $this->pluginsCategory = 'LogsStats/'; + + $this->dispatcher = Event_Dispatcher::getInstance(); + $this->loadPlugins(); + } + + /** + * Load the plugins classes installed. + * Register the observers for every plugin. + * + */ + public function loadPlugins() + { + $defaultPlugins = array( + array( 'fileName' => 'Provider', 'className' => 'Piwik_Plugin_LogStats_Provider' ), + // 'Piwik_Plugin_LogStats_UserSettings', + ); + + foreach($defaultPlugins as $pluginInfo) + { + $pluginFileName = $pluginInfo['fileName']; + $pluginClassName = $pluginInfo['className']; + /* + // TODO make sure the plugin name is secure + $path = PIWIK_INCLUDE_PATH + . $this->pluginsPath + . $this->pluginsCategory + . $pluginFileName . ".php"; + + if(is_file($path)) + { + throw new Exception("The plugin file $path couldn't be found."); + } + + require_once $path; + */ + + $newPlugin = new $pluginClassName; + + $this->addPluginObservers( $newPlugin ); + } + } + + /** + * For the given plugin, add all the observers of this plugin. + */ + private function addPluginObservers( Piwik_Plugin $plugin ) + { + $hooks = $plugin->getListHooksRegistered(); + + foreach($hooks as $hookName => $methodToCall) + { + $this->dispatcher->addObserver( array( $plugin, $methodToCall) ); + } + } + +} + +/** + * Post an event to the dispatcher which will notice the observers */ +function Piwik_PostEvent( $eventName, $object = null, $info = array() ) +{ + printDebug("Dispatching event $eventName..."); + Piwik_PluginsManager::getInstance()->dispatcher->post( $object, $eventName, $info, false, false ); +} + + +abstract class Piwik_Plugin +{ + /** + * Returns the plugin details + */ + abstract function getInformation(); + + /** + * Returns the list of hooks registered with the methods names + */ + abstract function getListHooksRegistered(); + + /** + * Returns the names of the required plugins + */ + public function getListRequiredPlugins() + { + return array(); + } + + /** + * Install the plugin + * - create tables + * - update existing tables + * - etc. + */ + public function install() + { + return; + } + + /** + * Remove the created resources during the install + */ + public function uninstall() + { + return; + } +} + + + +class Piwik_Plugin_LogStats_Provider extends Piwik_Plugin +{ + public function __construct() + { + } + + public function getInformation() + { + $info = array( + 'name' => 'LogProvider', + 'description' => 'Log in the DB the hostname looked up from the IP', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/plugins/LogProvider', + 'version' => '0.1', + ); + + return $info; + } + + function install() + { + // add column hostname / hostname ext in the visit table + } + + function uninstall() + { + // add column hostname / hostname ext in the visit table + } + + function getListHooksRegistered() + { + $hooks = array( + 'LogsStats.NewVisitor' => 'detectHostname' + ); + return $hooks; + } + + function detectHostname( $notification ) + { + $object = $notification->getNotificationObject(); + var_dump($object);printDebug(); + } +} +/* +class Piwik_Plugin_LogStats_UserSettings extends Piwik_Plugin +{ + +}*/ + +Piwik_PostEvent( 'LogsStats.NewVisitor' ); /** * To maximise the performance of the logging module, we use different techniques. @@ -55,7 +355,6 @@ /** * Configuration options for the statsLogEngine module: - * - record_logs ; defines if the logs are saved * - use_cookie ; defines if we try to get/set a cookie to help recognize a unique visitor * - */ @@ -92,12 +391,27 @@ class Piwik_LogStats_Action class Piwik_LogStats_Visit { + + function __construct() + { + } + // test if the visitor is excluded because of // - IP // - cookie // - configuration option? - public function isExcluded() - {} + private function isExcluded() + { + $excluded = 0; + + if($excluded) + { + printDebug("Visitor excluded."); + return true; + } + + return false; + } /** * Handles the visitor. @@ -126,28 +440,62 @@ class Piwik_LogStats_Visit * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration. * We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website. */ - + private function recognizeTheVisitor() + { + + } + + private function isVisitorKnown() + { + + } + /** * Once we have the visitor information, we have to define if the visit is a new or a known visit. * * 1) When the last action was done more than 30min ago, * or if the visitor is new, then this is a new visit. - * - * In the case of a new visit, then the time spent - * during the last action of the previous visit is unknown. - * - * In the case of a new visit but with a known visitor, - * we can set the 'returning visitor' flag. - * + * * 2) If the last action is less than 30min ago, then the same visit is going on. - * Because the visit goes on, we can get the time spent during the last action. + * Because the visit goes on, we can get the time spent during the last action. + * + * NB: + * - In the case of a new visit, then the time spent + * during the last action of the previous visit is unknown. + * + * - In the case of a new visit but with a known visitor, + * we can set the 'returning visitor' flag. + * */ /** * In all the cases we set a cookie to the visitor with the new information. */ public function handle() - {} + { + if(!$this->isExcluded()) + { + $this->recognizeTheVisitor(); + + // known visitor + if($this->isVisitorKnown()) + { + if($this->isLastActionInTheSameVisit()) + { + $this->handleKnownVisit(); + } + else + { + $this->handleNewVisit(); + } + } + // new visitor + else + { + $this->handleNewVisit(); + } + } + } /** * In the case of a known visit, we have to do the following actions: @@ -157,7 +505,9 @@ class Piwik_LogStats_Visit * 2) Update the visit information */ private function handleKnownVisit() - {} + { + printDebug("Visit known."); + } /** * In the case of a new visit, we have to do the following actions: @@ -167,41 +517,99 @@ class Piwik_LogStats_Visit * 2) Insert the visit information */ private function handleNewVisit() - {} + { + printDebug("New Visit."); + } } class Piwik_LogStats -{ - // load the configuration file - function loadConfigFile() - {} +{ + private $stateValid; + + const NOT_SPECIFIED = 1; + const LOGGING_DISABLE = 10; + const NO_GET_VARIABLE = 11; // create the database object function connectDatabase() - {} + { + $configDb = Piwik_LogStats_Config::getInstance()->database; + $db = new Piwik_LogStats_Db( $configDb['host'], + $configDb['username'], + $configDb['password'], + $configDb['dbname'] + ); + $db->connect(); + $this->stateValid = self::NOT_SPECIFIED; + } - // in case of any error during the logging, - // log the errors in DB or file depending on the config file - function logMessage() - {} - - // set some php configuration - function init() - {} + private function initProcess() + { + + $saveStats = Piwik_LogStats_Config::getInstance()->LogStats['record_statistics']; + if($saveStats == 0) + { + $this->setState(self::LOGGING_DISABLE); + } + + if( count($_GET) == 0) + { + $this->setState(self::NO_GET_VARIABLE); + } + } + + private function processVisit() + { + return $this->stateValid === true; + } + private function getState() + { + return $this->stateValid; + } + private function setState( $value ) + { + $this->stateValid = $value; + } // main algorithm // => input : variables filtered // => action : read cookie, read database, database logging, cookie writing function main() - {} + { + $this->initProcess(); + + if( $this->processVisit() ) + { + $visit = new Piwik_LogStats_Visit; + $visit->handle(); + } + $this->endProcess(); + } // display the logo or pixel 1*1 GIF // or a marketing page if no parameters in the url // or redirect to a url (transmit the cookie as well) // or load a URL (rss feed) (transmit the cookie as well) - public function endProcess() - {} + private function endProcess() + { + switch($this->getState()) + { + case self::LOGGING_DISABLE: + printDebug("Logging disabled, display transparent logo"); + break; + case self::NO_GET_VARIABLE: + printDebug("No get variables => piwik page"); + break; + case self::NOT_SPECIFIED: + default: + printDebug("Unknown state => default behaviour"); + break; + } + printDebug("End of the page."); + } } +$process = new Piwik_LogStats; +$process->main(); ?> diff --git a/tests/config_test.php b/tests/config_test.php index 62e33b03f19ab8045bc859e780699771f9e03314..f36342e7ba992fc26b202fe5526c694abd7e9962 100755 --- a/tests/config_test.php +++ b/tests/config_test.php @@ -28,12 +28,13 @@ set_include_path(PATH_TEST_TO_ROOT . PATH_SEPARATOR . PATH_TEST_TO_ROOT . '/core/models' . PATH_SEPARATOR . get_include_path()); +require_once "Zend/Exception.php"; +require_once "Zend/Loader.php"; + require_once PIWIK_INCLUDE_PATH . "/modules/ErrorHandler.php"; //set_error_handler('Piwik_ErrorHandler'); -require_once "Zend/Exception.php"; -require_once "Zend/Loader.php"; Zend_Loader::loadClass('Zend_Registry'); Zend_Loader::loadClass('Zend_Config_Ini');