Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rwxr-xr-xconfig/config.ini.php16
-rwxr-xr-xindex.php341
-rw-r--r--lang/en.php5
-rwxr-xr-xlibs/PEAR.php10
-rw-r--r--libs/XML/Serializer.php1025
-rw-r--r--libs/XML/Unserializer.php856
-rw-r--r--libs/XML/Util.php752
-rwxr-xr-xmodules/API/Proxy.php6
-rw-r--r--modules/API/Request.php49
-rw-r--r--modules/Archive.php36
-rw-r--r--modules/ArchiveProcessing.php2
-rw-r--r--modules/ArchiveProcessing/Period.php3
-rw-r--r--modules/DataFiles/SearchEngines.php38
-rw-r--r--modules/DataTable.php26
-rw-r--r--modules/DataTable/Filter.php2
-rw-r--r--modules/DataTable/Filter/ColumnCallbackAddDetail.php9
-rw-r--r--modules/DataTable/Filter/DetailCallbackAddDetail.php36
-rw-r--r--modules/DataTable/Filter/ExcludeLowPopulation.php7
-rw-r--r--modules/DataTable/Filter/Limit.php2
-rw-r--r--modules/DataTable/Filter/ReplaceColumnNames.php51
-rw-r--r--modules/DataTable/Filter/Sort.php2
-rw-r--r--modules/DataTable/Renderer/HTML.php10
-rw-r--r--modules/DataTable/Renderer/PHP.php35
-rw-r--r--modules/DataTable/Renderer/XML.php51
-rw-r--r--modules/DataTable/Row.php20
-rw-r--r--modules/DataTable/Simple.php20
-rwxr-xr-xmodules/ErrorHandler.php24
-rw-r--r--modules/Period.php4
-rwxr-xr-xmodules/Piwik.php9
-rw-r--r--modules/Plugin.php37
-rw-r--r--modules/PluginsManager.php14
-rw-r--r--modules/Translate.php52
-rw-r--r--plugins/Actions.php35
-rw-r--r--plugins/Actions/API.php40
-rw-r--r--plugins/ExamplePlugin.php9
-rw-r--r--plugins/ExamplePlugin/API.php32
-rw-r--r--plugins/Provider/API.php67
-rw-r--r--plugins/Referers.php1
-rw-r--r--plugins/Referers/API.php160
-rw-r--r--plugins/Referers/lang/en.php10
-rw-r--r--plugins/UserCountry.php1
-rw-r--r--plugins/UserCountry/API.php78
-rw-r--r--plugins/UserCountry/lang/en.php263
-rw-r--r--plugins/UserSettings/API.php65
-rw-r--r--plugins/VisitFrequency/API.php37
-rw-r--r--plugins/VisitTime.php7
-rw-r--r--plugins/VisitTime/API.php48
-rw-r--r--plugins/VisitorInterest.php8
-rw-r--r--plugins/VisitorInterest/API.php84
-rw-r--r--tests/modules/DataTable.test.php51
51 files changed, 4326 insertions, 223 deletions
diff --git a/TODO b/TODO
index c2d9fa9e89..c41f4cb001 100644
--- a/TODO
+++ b/TODO
@@ -27,7 +27,8 @@ BUGS
TODO MISC
=========
- when the plugins are not used in the piwik.php logging script, don't load the related files
-
+- Archive::build must have the same parameters order as all the API calls that use idSite, Period, Date
+- change all .png images to GIF
CHANGES DONE TO LIBRARIES
diff --git a/config/config.ini.php b/config/config.ini.php
index ca0dd57dae..bc34f5c60a 100755
--- a/config/config.ini.php
+++ b/config/config.ini.php
@@ -15,15 +15,19 @@ profiler = true
dbname = piwiktests
tables_prefix = piwiktests_
+[Language]
+current = en
+default = en
+
[Plugins]
enabled[] = UserSettings
enabled[] = Actions
-;enabled[] = UserCountry
-;enabled[] = Provider
-;enabled[] = Referers
-;enabled[] = VisitFrequency
-;enabled[] = VisitTime
-;enabled[] = VisitorInterest
+enabled[] = Provider
+enabled[] = UserCountry
+enabled[] = Referers
+enabled[] = VisitFrequency
+enabled[] = VisitTime
+enabled[] = VisitorInterest
[Plugins_LogStats]
enabled[] = Provider
diff --git a/index.php b/index.php
index ab71c933fd..49767be382 100755
--- a/index.php
+++ b/index.php
@@ -5,6 +5,8 @@
error_reporting(E_ALL|E_NOTICE);
date_default_timezone_set('Europe/London');
define('PIWIK_INCLUDE_PATH', '.');
+define('PIWIK_PLUGINS_PATH', PIWIK_INCLUDE_PATH . '/plugins');
+define('PIWIK_DATAFILES_INCLUDE_PATH', PIWIK_INCLUDE_PATH . "/modules/DataFiles");
set_include_path(PIWIK_INCLUDE_PATH
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs/'
@@ -16,12 +18,6 @@ assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 1);
assert_options(ASSERT_BAIL, 1);
-//ini_set('xdebug.collect_vars', 'on');
-//ini_set('xdebug.collect_params', '4');
-//ini_set('xdebug.dump_globals', 'on');
-//ini_set('xdebug.dump.SERVER', 'REQUEST_URI');
-//ini_set('xdebug.show_local_vars', 'on');
-
/**
* Error / exception handling functions
*/
@@ -51,6 +47,7 @@ require_once "Access.php";
require_once "Auth.php";
require_once "API/Proxy.php";
require_once "Site.php";
+require_once "Translate.php";
//move into a init() method
Piwik::createConfigObject();
@@ -103,41 +100,6 @@ Zend_Registry::get('access')->loadAccess();
Zend_Loader::loadClass('Piwik_Archive');
Zend_Loader::loadClass('Piwik_Date');
-Piwik::printMemoryUsage('Before archiving');
-//$test = new Piwik_Archive;
-//$period = new Piwik_Period_Day( Piwik_Date::today() );
-//$site = new Piwik_Site(1);
-//$test->setPeriod($period);
-//$test->setSite($site);
-//
-//
-//$test = new Piwik_Archive;
-//$period = new Piwik_Period_Month(Piwik_Date::today());
-//$site = new Piwik_Site(1);
-//$test->setPeriod($period);
-//$test->setSite($site);
-//Piwik::log("visits=".$test->get('nb_visits'));
-//Piwik::log("max_actions=".$test->get('max_actions'));
-//Piwik::log("UserSettings_resolution = ".$test->getDataTable('UserSettings_resolution'));
-
-
-$test = new Piwik_Archive;
-//Piwik::printMemoryUsage('after archive instanciation');
-$period = new Piwik_Period_Week(Piwik_Date::today());
-//Piwik::printMemoryUsage('after period');
-$site = new Piwik_Site(1);
-//Piwik::printMemoryUsage('after site');
-$test->setPeriod($period);
-$test->setSite($site);
-$test->get('nb_visits');
-//Piwik::printMemoryUsage('after first get');
-//$test->get('nb_visits');
-//$test->get('nb_visits');
-//$test->get('nb_visits');
-//$test->get('toto12');
-//Piwik::log("Referers_keywordBySearchEngine = ". $test->getDataTableExpanded('Referers_keywordBySearchEngine'));
-//Piwik::log("Referers_keywordBySearchEngine = ". $test->getDataTable('Referers_keywordBySearchEngine'));
-
main();
displayProfiler();
Piwik::printMemoryUsage();
@@ -173,8 +135,8 @@ function displayProfiler()
function main()
{
Piwik::log(
- '<a href="http://localhost/dev/piwiktrunk/?method=UserSettings.getResolution&idSite=1&date=yesterday&period=week&format=console&filter_limit=&filter_offset=&filter_column=label&filter_pattern=12">
- http://localhost/dev/piwiktrunk/?method=UserSettings.getResolution&idSite=1&date=yesterday&period=week&format=console&filter_limit=&filter_offset=&filter_column=label&filter_pattern=12
+ '<a href="http://localhost/dev/piwiktrunk/?method=UserSettings.getResolution&idSite=1&date=yesterday&period=week&format=xml&filter_limit=&filter_offset=&filter_column=label&filter_pattern=12">
+ http://localhost/dev/piwiktrunk/?method=UserSettings.getResolution&idSite=1&date=yesterday&period=week&format=xml&filter_limit=&filter_offset=&filter_column=label&filter_pattern=12
</a>
<br>'
);
@@ -197,32 +159,102 @@ function main()
require_once "API/Request.php";
Piwik::log("getResolution");
- $request = new Piwik_API_Request('method=UserSettings.getResolution&idSite=1&date=yesterday&period=week&format=console&filter_limit=&filter_offset=&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getResolution
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=console
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ print(($request->process()));
Piwik::log("getOS");
- $request = new Piwik_API_Request('filter_sort_column=1&filter_sort_order=asc&method=UserSettings.getOS&idSite=1&date=yesterday&period=week&format=html&filter_limit=&filter_offset=&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('method=UserSettings.getOS
+
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getConfiguration");
- $request = new Piwik_API_Request('method=UserSettings.getConfiguration&idSite=1&date=yesterday&period=week&format=console&filter_limit=10&filter_offset=0&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getConfiguration
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=10
+ &filter_offset=0
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getBrowser");
- $request = new Piwik_API_Request('method=UserSettings.getBrowser&idSite=1&date=yesterday&period=week&format=console&filter_limit=10&filter_offset=0&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getBrowser
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getBrowserType");
- $request = new Piwik_API_Request('method=UserSettings.getBrowserType&idSite=1&date=yesterday&period=week&format=console&filter_limit=10&filter_offset=0&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getBrowserType
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getWideScreen");
- $request = new Piwik_API_Request('method=UserSettings.getWideScreen&idSite=1&date=yesterday&period=week&format=console&filter_limit=10&filter_offset=0&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getWideScreen
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getPlugin");
- $request = new Piwik_API_Request('method=UserSettings.getPlugin&idSite=1&date=yesterday&period=week&format=console&filter_limit=10&filter_offset=0&filter_column=label&filter_pattern=');
- echo $return = $request->process();
+ $request = new Piwik_API_Request('
+ method=UserSettings.getPlugin
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=
+ &filter_offset=
+ &filter_column=label
+ &filter_pattern=
+ ');
+ dump(htmlentities($request->process()));
Piwik::log("getActions");
$request = new Piwik_API_Request(
@@ -231,16 +263,205 @@ function main()
&date=yesterday
&period=month
&format=html
- &filter_limit=20
+ &filter_limit=10
&filter_offset=0
+ '
+ );
+// echo(($request->process()));
+
+ Piwik::log("getActions EXPANDED");
+ $request = new Piwik_API_Request(
+ 'method=Actions.getActions
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=html
+ &expanded=true
&filter_column=label
- &filter_pattern=
+ &filter_pattern=a
+ &filter_limit=10
+ &filter_offset=0
+
+ '
+ );
+// echo(($request->process()));
+
+ Piwik::log("getActions EXPANDED SUBTABLE");
+ $request = new Piwik_API_Request(
+ 'method=Actions.getActions
+ &idSubtable=5477
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=html
+ &expanded=false
+
'
);
- echo $return = $request->process();
+// echo(($request->process()));
+
+ Piwik::log("getDownloads");
+ $request = new Piwik_API_Request(
+ 'method=Actions.getDownloads
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+// dump(htmlentities($request->process()));
+ Piwik::log("getOutlinks");
+ $request = new Piwik_API_Request(
+ 'method=Actions.getOutlinks
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+// dump(htmlentities($request->process()));
+ Piwik::log("getProvider");
+ $request = new Piwik_API_Request(
+ 'method=Provider.getProvider
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getCountry");
+ $request = new Piwik_API_Request(
+ 'method=UserCountry.getCountry
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ &filter_limit=10
+ &filter_offset=0
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getContinent");
+ $request = new Piwik_API_Request(
+ 'method=UserCountry.getContinent
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+
+ Piwik::log("getContinent");
+ $request = new Piwik_API_Request(
+ 'method=VisitFrequency.getSummary
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getNumberOfVisitsPerVisitDuration");
+ $request = new Piwik_API_Request(
+ 'method=VisitorInterest.getNumberOfVisitsPerVisitDuration
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getNumberOfVisitsPerPage");
+ $request = new Piwik_API_Request(
+ 'method=VisitorInterest.getNumberOfVisitsPerPage
+ &idSite=1
+ &date=yesterday
+ &period=month
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+
+
+ Piwik::log("getVisitInformationPerServerTime");
+ $request = new Piwik_API_Request(
+ 'method=VisitTime.getVisitInformationPerServerTime
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+
+ Piwik::log("getRefererType");
+ $request = new Piwik_API_Request(
+ 'method=Referers.getRefererType
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getKeywords");
+ $request = new Piwik_API_Request(
+ 'method=Referers.getKeywords
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=10
+ &filter_offset=0
+ '
+ );
+ dump(htmlentities($request->process()));
+ Piwik::log("getSearchEnginesFromKeywordId");
+ $request = new Piwik_API_Request(
+ 'method=Referers.getSearchEnginesFromKeywordId
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &idSubtable=1886
+ &filter_limit=10
+ &filter_offset=0
+ '
+ );
+ dump(htmlentities($request->process()));
+
+ Piwik::log("getSearchEngines");
+ $request = new Piwik_API_Request(
+ 'method=Referers.getSearchEngines
+ &idSite=1
+ &date=yesterday
+ &period=week
+ &format=xml
+ &filter_limit=10
+ &filter_offset=0
+ '
+ );
+ dump(htmlentities($request->process()));
+
}
+function dump($var)
+{
+ print("<pre>");
+ var_export($var);
+ print("</pre>");
+}
?>
diff --git a/lang/en.php b/lang/en.php
new file mode 100644
index 0000000000..ffc7e0e69e
--- /dev/null
+++ b/lang/en.php
@@ -0,0 +1,5 @@
+<?php
+$translations = array(
+ 'General_Unknown' => 'Unknown',
+);
+?>
diff --git a/libs/PEAR.php b/libs/PEAR.php
index fc879a0a78..f14d8a06f1 100755
--- a/libs/PEAR.php
+++ b/libs/PEAR.php
@@ -227,7 +227,7 @@ class PEAR
* @return mixed A reference to the variable. If not set it will be
* auto initialised to NULL.
*/
- function &getStaticProperty($class, $var)
+ static function &getStaticProperty($class, $var)
{
static $properties;
if (!isset($properties[$class])) {
@@ -278,7 +278,7 @@ class PEAR
*/
function isError($data, $code = null)
{
- if (is_a($data, 'PEAR_Error')) {
+ if ($data instanceof PEAR_Error) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
@@ -521,7 +521,7 @@ class PEAR
* @see PEAR::setErrorHandling
* @since PHP 4.0.5
*/
- function &raiseError($message = null,
+ static function &raiseError($message = null,
$code = null,
$mode = null,
$options = null,
@@ -566,10 +566,10 @@ class PEAR
$ec = 'PEAR_Error';
}
if ($skipmsg) {
- $a = &new $ec($code, $mode, $options, $userinfo);
+ $a = new $ec($code, $mode, $options, $userinfo);
return $a;
} else {
- $a = &new $ec($message, $code, $mode, $options, $userinfo);
+ $a = new $ec($message, $code, $mode, $options, $userinfo);
return $a;
}
}
diff --git a/libs/XML/Serializer.php b/libs/XML/Serializer.php
new file mode 100644
index 0000000000..9b2e9f6cfb
--- /dev/null
+++ b/libs/XML/Serializer.php
@@ -0,0 +1,1025 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * XML_Serializer
+ *
+ * Creates XML documents from PHP data structures like arrays, objects or scalars.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @author Stephan Schmidt <schst@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: Serializer.php,v 1.47 2005/09/30 13:40:30 schst Exp $
+ * @link http://pear.php.net/package/XML_Serializer
+ * @see XML_Unserializer
+ */
+
+/**
+ * uses PEAR error management
+ */
+require_once 'PEAR.php';
+
+/**
+ * uses XML_Util to create XML tags
+ */
+require_once 'XML/Util.php';
+
+/**
+ * option: string used for indentation
+ *
+ * Possible values:
+ * - any string (default is any string)
+ */
+define('XML_SERIALIZER_OPTION_INDENT', 'indent');
+
+/**
+ * option: string used for linebreaks
+ *
+ * Possible values:
+ * - any string (default is \n)
+ */
+define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
+
+/**
+ * option: enable type hints
+ *
+ * Possible values:
+ * - true
+ * - false
+ */
+define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
+
+/**
+ * option: add an XML declaration
+ *
+ * Possible values:
+ * - true
+ * - false
+ */
+define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl');
+
+/**
+ * option: encoding of the document
+ *
+ * Possible values:
+ * - any valid encoding
+ * - null (default)
+ */
+define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding');
+
+/**
+ * option: default name for tags
+ *
+ * Possible values:
+ * - any string (XML_Serializer_Tag is default)
+ */
+define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName');
+
+/**
+ * option: use classname for objects in indexed arrays
+ *
+ * Possible values:
+ * - true (default)
+ * - false
+ */
+define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName');
+
+/**
+ * option: attribute where original key is stored
+ *
+ * Possible values:
+ * - any string (default is _originalKey)
+ */
+define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
+
+/**
+ * option: attribute for type (only if typeHints => true)
+ *
+ * Possible values:
+ * - any string (default is _type)
+ */
+define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
+
+/**
+ * option: attribute for class (only if typeHints => true)
+ *
+ * Possible values:
+ * - any string (default is _class)
+ */
+define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
+
+/**
+ * option: scalar values (strings, ints,..) will be serialized as attribute
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ * - array which sets this option on a per-tag basis
+ */
+define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes');
+
+/**
+ * option: prepend string for attributes
+ *
+ * Possible values:
+ * - any string (default is any string)
+ */
+define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes');
+
+/**
+ * option: indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes');
+
+/**
+ * option: use 'simplexml' to use parent name as tagname if transforming an indexed array
+ *
+ * Possible values:
+ * - XML_SERIALIZER_MODE_DEFAULT (default)
+ * - XML_SERIALIZER_MODE_SIMPLEXML
+ */
+define('XML_SERIALIZER_OPTION_MODE', 'mode');
+
+/**
+ * option: add a doctype declaration
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype');
+
+/**
+ * option: supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
+ *
+ * Possible values:
+ * - string
+ * - array
+ */
+define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
+
+/**
+ * option: name of the root tag
+ *
+ * Possible values:
+ * - string
+ * - null (default)
+ */
+define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
+
+/**
+ * option: attributes of the root tag
+ *
+ * Possible values:
+ * - array
+ */
+define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes');
+
+/**
+ * option: all values in this key will be treated as attributes
+ *
+ * Possible values:
+ * - array
+ */
+define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray');
+
+/**
+ * option: this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
+ *
+ * Possible values:
+ * - string
+ * - null (default)
+ */
+define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName');
+
+/**
+ * option: this value will be used in a comment, instead of creating a new tag
+ *
+ * Possible values:
+ * - string
+ * - null (default)
+ */
+define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
+
+/**
+ * option: tag names that will be changed
+ *
+ * Possible values:
+ * - array
+ */
+define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap');
+
+/**
+ * option: function that will be applied before serializing
+ *
+ * Possible values:
+ * - any valid PHP callback
+ */
+define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction');
+
+/**
+ * option: function that will be applied before serializing
+ *
+ * Possible values:
+ * - string
+ * - null (default)
+ */
+define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace');
+
+/**
+ * option: type of entities to replace
+ *
+ * Possible values:
+ * - XML_SERIALIZER_ENTITIES_NONE
+ * - XML_SERIALIZER_ENTITIES_XML (default)
+ * - XML_SERIALIZER_ENTITIES_XML_REQUIRED
+ * - XML_SERIALIZER_ENTITIES_HTML
+ */
+define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities');
+
+/**
+ * option: whether to return the result of the serialization from serialize()
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
+
+/**
+ * option: whether to ignore properties that are set to null
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull');
+
+/**
+ * option: whether to use cdata sections for character data
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata');
+
+
+/**
+ * default mode
+ */
+define('XML_SERIALIZER_MODE_DEFAULT', 'default');
+
+/**
+ * SimpleXML mode
+ *
+ * When serializing indexed arrays, the key of the parent value is used as a tagname.
+ */
+define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
+
+/**
+ * error code for no serialization done
+ */
+define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
+
+/**
+ * do not replace entitites
+ */
+define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
+
+/**
+ * replace all XML entitites
+ * This setting will replace <, >, ", ' and &
+ */
+define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
+
+/**
+ * replace only required XML entitites
+ * This setting will replace <, " and &
+ */
+define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
+
+/**
+ * replace HTML entitites
+ * @link http://www.php.net/htmlentities
+ */
+define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
+
+/**
+ * Creates XML documents from PHP data structures like arrays, objects or scalars.
+ *
+ * this class can be used in two modes:
+ *
+ * 1. create an XML document from an array or object that is processed by other
+ * applications. That means, you can create a RDF document from an array in the
+ * following format:
+ *
+ * $data = array(
+ * 'channel' => array(
+ * 'title' => 'Example RDF channel',
+ * 'link' => 'http://www.php-tools.de',
+ * 'image' => array(
+ * 'title' => 'Example image',
+ * 'url' => 'http://www.php-tools.de/image.gif',
+ * 'link' => 'http://www.php-tools.de'
+ * ),
+ * array(
+ * 'title' => 'Example item',
+ * 'link' => 'http://example.com'
+ * ),
+ * array(
+ * 'title' => 'Another Example item',
+ * 'link' => 'http://example.org'
+ * )
+ * )
+ * );
+ *
+ * to create a RDF document from this array do the following:
+ *
+ * require_once 'XML/Serializer.php';
+ *
+ * $options = array(
+ * XML_SERIALIZER_OPTION_INDENT => "\t", // indent with tabs
+ * XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // use UNIX line breaks
+ * XML_SERIALIZER_OPTION_ROOT_NAME => 'rdf:RDF', // root tag
+ * XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item' // tag for values with numeric keys
+ * );
+ *
+ * $serializer = new XML_Serializer($options);
+ * $rdf = $serializer->serialize($data);
+ *
+ * You will get a complete XML document that can be processed like any RDF document.
+ *
+ * 2. this classes can be used to serialize any data structure in a way that it can
+ * later be unserialized again.
+ * XML_Serializer will store the type of the value and additional meta information
+ * in attributes of the surrounding tag. This meat information can later be used
+ * to restore the original data structure in PHP. If you want XML_Serializer
+ * to add meta information to the tags, add
+ *
+ * XML_SERIALIZER_OPTION_TYPEHINTS => true
+ *
+ * to the options array in the constructor.
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @author Stephan Schmidt <schst@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/XML_Serializer
+ * @see XML_Unserializer
+ */
+class XML_Serializer extends PEAR
+{
+ /**
+ * list of all available options
+ *
+ * @access private
+ * @var array
+ */
+ var $_knownOptions = array(
+ XML_SERIALIZER_OPTION_INDENT,
+ XML_SERIALIZER_OPTION_LINEBREAKS,
+ XML_SERIALIZER_OPTION_TYPEHINTS,
+ XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
+ XML_SERIALIZER_OPTION_XML_ENCODING,
+ XML_SERIALIZER_OPTION_DEFAULT_TAG,
+ XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
+ XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
+ XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
+ XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
+ XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
+ XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
+ XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
+ XML_SERIALIZER_OPTION_MODE,
+ XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
+ XML_SERIALIZER_OPTION_DOCTYPE,
+ XML_SERIALIZER_OPTION_ROOT_NAME,
+ XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
+ XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
+ XML_SERIALIZER_OPTION_CONTENT_KEY,
+ XML_SERIALIZER_OPTION_COMMENT_KEY,
+ XML_SERIALIZER_OPTION_TAGMAP,
+ XML_SERIALIZER_OPTION_ENCODE_FUNC,
+ XML_SERIALIZER_OPTION_NAMESPACE,
+ XML_SERIALIZER_OPTION_ENTITIES,
+ XML_SERIALIZER_OPTION_RETURN_RESULT,
+ XML_SERIALIZER_OPTION_IGNORE_NULL,
+ XML_SERIALIZER_OPTION_CDATA_SECTIONS
+ );
+
+ /**
+ * default options for the serialization
+ *
+ * @access private
+ * @var array
+ */
+ var $_defaultOptions = array(
+ XML_SERIALIZER_OPTION_INDENT => '', // string used for indentation
+ XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // string used for newlines
+ XML_SERIALIZER_OPTION_TYPEHINTS => false, // automatically add type hin attributes
+ XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false, // add an XML declaration
+ XML_SERIALIZER_OPTION_XML_ENCODING => null, // encoding specified in the XML declaration
+ XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names
+ XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false, // use classname for objects in indexed arrays
+ XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey', // attribute where original key is stored
+ XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type', // attribute for type (only if typeHints => true)
+ XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class', // attribute for class of objects (only if typeHints => true)
+ XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false, // scalar values (strings, ints,..) will be serialized as attribute
+ XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '', // prepend string for attributes
+ XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
+ XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT, // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname if transforming an indexed array
+ XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false, // add a doctype declaration
+ XML_SERIALIZER_OPTION_DOCTYPE => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
+ XML_SERIALIZER_OPTION_ROOT_NAME => null, // name of the root tag
+ XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(), // attributes of the root tag
+ XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null, // all values in this key will be treated as attributes
+ XML_SERIALIZER_OPTION_CONTENT_KEY => null, // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
+ XML_SERIALIZER_OPTION_COMMENT_KEY => null, // this value will be used directly as comment, instead of creating a new tag, may only be used in conjuction with attributesArray
+ XML_SERIALIZER_OPTION_TAGMAP => array(), // tag names that will be changed
+ XML_SERIALIZER_OPTION_ENCODE_FUNC => null, // function that will be applied before serializing
+ XML_SERIALIZER_OPTION_NAMESPACE => null, // namespace to use
+ XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML, // type of entities to replace,
+ XML_SERIALIZER_OPTION_RETURN_RESULT => false, // serialize() returns the result of the serialization instead of true
+ XML_SERIALIZER_OPTION_IGNORE_NULL => false, // ignore properties that are set to null
+ XML_SERIALIZER_OPTION_CDATA_SECTIONS => false // Whether to use cdata sections for plain character data
+ );
+
+ /**
+ * options for the serialization
+ *
+ * @access public
+ * @var array
+ */
+ var $options = array();
+
+ /**
+ * current tag depth
+ *
+ * @access private
+ * @var integer
+ */
+ var $_tagDepth = 0;
+
+ /**
+ * serilialized representation of the data
+ *
+ * @access private
+ * @var string
+ */
+ var $_serializedData = null;
+
+ /**
+ * constructor
+ *
+ * @access public
+ * @param mixed $options array containing options for the serialization
+ */
+ function XML_Serializer( $options = null )
+ {
+ $this->PEAR();
+ if (is_array($options)) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = $this->_defaultOptions;
+ }
+ }
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ function apiVersion()
+ {
+ return '@package_version@';
+ }
+
+ /**
+ * reset all options to default options
+ *
+ * @access public
+ * @see setOption(), XML_Serializer()
+ */
+ function resetOptions()
+ {
+ $this->options = $this->_defaultOptions;
+ }
+
+ /**
+ * set an option
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Serializer()
+ */
+ function setOption($name, $value)
+ {
+ $this->options[$name] = $value;
+ }
+
+ /**
+ * sets several options at once
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Unserializer(), setOption()
+ */
+ function setOptions($options)
+ {
+ $this->options = array_merge($this->options, $options);
+ }
+
+ /**
+ * serialize data
+ *
+ * @access public
+ * @param mixed $data data to serialize
+ * @return boolean true on success, pear error on failure
+ */
+ function serialize($data, $options = null)
+ {
+ // if options have been specified, use them instead
+ // of the previously defined ones
+ if (is_array($options)) {
+ $optionsBak = $this->options;
+ if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = array_merge($this->options, $options);
+ }
+ } else {
+ $optionsBak = null;
+ }
+
+ // start depth is zero
+ $this->_tagDepth = 0;
+
+ $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
+ if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE]) && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
+ $rootAttributes['xmlns:'.$this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
+ }
+
+ $this->_serializedData = '';
+ // serialize an array
+ if (is_array($data)) {
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
+ $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
+ } else {
+ $tagName = 'array';
+ }
+
+ $this->_serializedData .= $this->_serializeArray($data, $tagName, $rootAttributes);
+ } elseif (is_object($data)) {
+ // serialize an object
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
+ $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
+ } else {
+ $tagName = get_class($data);
+ }
+ $this->_serializedData .= $this->_serializeObject($data, $tagName, $rootAttributes);
+ } else {
+ $tag = array();
+ if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
+ } else {
+ $tag['qname'] = gettype($data);
+ }
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
+ $rootAttributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
+ }
+ @settype($data, 'string');
+ $tag['content'] = $data;
+ $tag['attributes'] = $rootAttributes;
+ $this->_serializedData = $this->_createXMLTag($tag);
+ }
+
+ // add doctype declaration
+ if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
+ $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options[XML_SERIALIZER_OPTION_DOCTYPE])
+ . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
+ . $this->_serializedData;
+ }
+
+ // build xml declaration
+ if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
+ $atts = array();
+ $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
+ . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
+ . $this->_serializedData;
+ }
+
+ if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
+ $result = $this->_serializedData;
+ } else {
+ $result = true;
+ }
+
+ if ($optionsBak !== null) {
+ $this->options = $optionsBak;
+ }
+
+ return $result;
+ }
+
+ /**
+ * get the result of the serialization
+ *
+ * @access public
+ * @return string serialized XML
+ */
+ function getSerializedData()
+ {
+ if ($this->_serializedData == null) {
+ return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
+ }
+ return $this->_serializedData;
+ }
+
+ /**
+ * serialize any value
+ *
+ * This method checks for the type of the value and calls the appropriate method
+ *
+ * @access private
+ * @param mixed $value
+ * @param string $tagName
+ * @param array $attributes
+ * @return string
+ */
+ function _serializeValue($value, $tagName = null, $attributes = array())
+ {
+ if (is_array($value)) {
+ $xml = $this->_serializeArray($value, $tagName, $attributes);
+ } elseif (is_object($value)) {
+ $xml = $this->_serializeObject($value, $tagName);
+ } else {
+ $tag = array(
+ 'qname' => $tagName,
+ 'attributes' => $attributes,
+ 'content' => $value
+ );
+ $xml = $this->_createXMLTag($tag);
+ }
+ return $xml;
+ }
+
+ /**
+ * serialize an array
+ *
+ * @access private
+ * @param array $array array to serialize
+ * @param string $tagName name of the root tag
+ * @param array $attributes attributes for the root tag
+ * @return string $string serialized data
+ * @uses XML_Util::isValidName() to check, whether key has to be substituted
+ */
+ function _serializeArray(&$array, $tagName = null, $attributes = array())
+ {
+ $_content = null;
+ $_comment = null;
+
+ // check for comment
+ if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])) {
+ $_comment = $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
+ unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
+ }
+ }
+
+ /**
+ * check for special attributes
+ */
+ if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])) {
+ $attributes = $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
+ unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
+ }
+ /**
+ * check for special content
+ */
+ if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
+ if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])) {
+ $_content = $array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]];
+ unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
+ }
+ }
+ }
+
+ if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
+ foreach (array_keys($array) as $key) {
+ if (is_null($array[$key])) {
+ unset($array[$key]);
+ }
+ }
+ }
+
+ /*
+ * if mode is set to simpleXML, check whether
+ * the array is associative or indexed
+ */
+ if (is_array($array) && !empty($array) && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
+ $indexed = true;
+ foreach ($array as $key => $val) {
+ if (!is_int($key)) {
+ $indexed = false;
+ break;
+ }
+ }
+
+ if ($indexed && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
+ $string = '';
+ foreach ($array as $key => $val) {
+ $string .= $this->_serializeValue( $val, $tagName, $attributes);
+
+ $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
+ // do indentation
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
+ $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
+ }
+ }
+ return rtrim($string);
+ }
+ }
+
+ $scalarAsAttributes = false;
+ if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]) && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName])) {
+ $scalarAsAttributes = $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
+ } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true) {
+ $scalarAsAttributes = true;
+ }
+
+ if ($scalarAsAttributes === true) {
+ $this->expectError('*');
+ foreach ($array as $key => $value) {
+ if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
+ unset($array[$key]);
+ $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
+ }
+ }
+ $this->popExpect();
+ } elseif (is_array($scalarAsAttributes)) {
+ $this->expectError('*');
+ foreach ($scalarAsAttributes as $key) {
+ if (!isset($array[$key])) {
+ continue;
+ }
+ $value = $array[$key];
+ if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
+ unset($array[$key]);
+ $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
+ }
+ }
+ $this->popExpect();
+ }
+
+ // check for empty array => create empty tag
+ if (empty($array)) {
+ $tag = array(
+ 'qname' => $tagName,
+ 'content' => $_content,
+ 'attributes' => $attributes
+ );
+ } else {
+ $this->_tagDepth++;
+ $tmp = $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
+ foreach ($array as $key => $value) {
+ // do indentation
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
+ $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
+ }
+
+ if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
+ $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
+ }
+
+ // copy key
+ $origKey = $key;
+ $this->expectError('*');
+ // key cannot be used as tagname => use default tag
+ $valid = XML_Util::isValidName($key);
+ $this->popExpect();
+ if (PEAR::isError($valid)) {
+ if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME] && is_object($value)) {
+ $key = get_class($value);
+ } else {
+ $key = $this->_getDefaultTagname($tagName);
+ }
+ }
+ $atts = array();
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
+ $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($value);
+ if ($key !== $origKey) {
+ $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] = (string)$origKey;
+ }
+ }
+
+ $tmp .= $this->_createXMLTag(array(
+ 'qname' => $key,
+ 'attributes' => $atts,
+ 'content' => $value )
+ );
+ $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
+ }
+
+ $this->_tagDepth--;
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
+ $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
+ }
+
+ if (trim($tmp) === '') {
+ $tmp = null;
+ }
+
+ $tag = array(
+ 'qname' => $tagName,
+ 'content' => $tmp,
+ 'attributes' => $attributes
+ );
+ }
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
+ if (!isset($tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])) {
+ $tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
+ }
+ }
+
+ $string = '';
+ if (!is_null($_comment)) {
+ $string .= XML_Util::createComment($_comment);
+ $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
+ $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
+ }
+ }
+ $string .= $this->_createXMLTag($tag, false);
+ return $string;
+ }
+
+ /**
+ * get the name of the default tag.
+ *
+ * The name of the parent tag needs to be passed as the
+ * default name can depend on the context.
+ *
+ * @param string name of the parent tag
+ * @return string default tag name
+ */
+ function _getDefaultTagname($parent)
+ {
+ if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
+ }
+ if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
+ } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'])) {
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
+ } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'])) {
+ // keep this for BC
+ return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
+ }
+ return 'XML_Serializer_Tag';
+ }
+
+ /**
+ * serialize an object
+ *
+ * @access private
+ * @param object $object object to serialize
+ * @return string $string serialized data
+ */
+ function _serializeObject(&$object, $tagName = null, $attributes = array())
+ {
+ // check for magic function
+ if (method_exists($object, '__sleep')) {
+ $propNames = $object->__sleep();
+ if (is_array($propNames)) {
+ $properties = array();
+ foreach ($propNames as $propName) {
+ $properties[$propName] = $object->$propName;
+ }
+ } else {
+ $properties = get_object_vars($object);
+ }
+ } else {
+ $properties = get_object_vars($object);
+ }
+
+ if (empty($tagName)) {
+ $tagName = get_class($object);
+ }
+
+ // typehints activated?
+ if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
+ $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'object';
+ $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] = get_class($object);
+ }
+ $string = $this->_serializeArray($properties, $tagName, $attributes);
+ return $string;
+ }
+
+ /**
+ * create a tag from an array
+ * this method awaits an array in the following format
+ * array(
+ * 'qname' => $tagName,
+ * 'attributes' => array(),
+ * 'content' => $content, // optional
+ * 'namespace' => $namespace // optional
+ * 'namespaceUri' => $namespaceUri // optional
+ * )
+ *
+ * @access private
+ * @param array $tag tag definition
+ * @param boolean $replaceEntities whether to replace XML entities in content or not
+ * @return string $string XML tag
+ */
+ function _createXMLTag($tag, $firstCall = true)
+ {
+ // build fully qualified tag name
+ if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
+ if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0] . ':' . $tag['qname'];
+ } else {
+ $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE] . ':' . $tag['qname'];
+ }
+ }
+
+ // attribute indentation
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
+ $multiline = true;
+ $indent = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
+
+ if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
+ $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
+
+ } else {
+ $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
+ }
+ } else {
+ $multiline = false;
+ $indent = false;
+ }
+
+ if (is_array($tag['content'])) {
+ if (empty($tag['content'])) {
+ $tag['content'] = '';
+ }
+ } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
+ $tag['content'] = '';
+ }
+
+ // replace XML entities (only needed, if this is not a nested call)
+ if ($firstCall === true) {
+ if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
+ $replaceEntities = XML_UTIL_CDATA_SECTION;
+ } else {
+ $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
+ }
+ } else {
+ $replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
+ }
+ if (is_scalar($tag['content']) || is_null($tag['content'])) {
+ if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
+ if ($firstCall === true) {
+ $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
+ }
+ $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
+ }
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
+ } elseif (is_array($tag['content'])) {
+ $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
+ } elseif (is_object($tag['content'])) {
+ $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
+ } elseif (is_resource($tag['content'])) {
+ settype($tag['content'], 'string');
+ if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
+ if ($replaceEntities === true) {
+ $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
+ }
+ $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
+ }
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
+ }
+ return $tag;
+ }
+}
+?> \ No newline at end of file
diff --git a/libs/XML/Unserializer.php b/libs/XML/Unserializer.php
new file mode 100644
index 0000000000..a5a03dcfc6
--- /dev/null
+++ b/libs/XML/Unserializer.php
@@ -0,0 +1,856 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * XML_Unserializer
+ *
+ * Parses any XML document into PHP data structures.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt. If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @author Stephan Schmidt <schst@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version CVS: $Id: Unserializer.php,v 1.39 2005/09/28 11:19:56 schst Exp $
+ * @link http://pear.php.net/package/XML_Serializer
+ * @see XML_Unserializer
+ */
+
+/**
+ * uses PEAR error managemt
+ */
+require_once 'PEAR.php';
+
+/**
+ * uses XML_Parser to unserialize document
+ */
+require_once 'XML/Parser.php';
+
+/**
+ * option: Convert nested tags to array or object
+ *
+ * Possible values:
+ * - array
+ * - object
+ * - associative array to define this option per tag name
+ */
+define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType');
+
+/**
+ * option: Name of the attribute that stores the original key
+ *
+ * Possible values:
+ * - any string
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
+
+/**
+ * option: Name of the attribute that stores the type
+ *
+ * Possible values:
+ * - any string
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
+
+/**
+ * option: Name of the attribute that stores the class name
+ *
+ * Possible values:
+ * - any string
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
+
+/**
+ * option: Whether to use the tag name as a class name
+ *
+ * Possible values:
+ * - true or false
+ */
+define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass');
+
+/**
+ * option: Name of the default class
+ *
+ * Possible values:
+ * - any string
+ */
+define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass');
+
+/**
+ * option: Whether to parse attributes
+ *
+ * Possible values:
+ * - true or false
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes');
+
+/**
+ * option: Key of the array to store attributes (if any)
+ *
+ * Possible values:
+ * - any string
+ * - false (disabled)
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray');
+
+/**
+ * option: string to prepend attribute name (if any)
+ *
+ * Possible values:
+ * - any string
+ * - false (disabled)
+ */
+define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes');
+
+/**
+ * option: key to store the content, if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used
+ *
+ * Possible values:
+ * - any string
+ */
+define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName');
+
+/**
+ * option: map tag names
+ *
+ * Possible values:
+ * - associative array
+ */
+define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap');
+
+/**
+ * option: list of tags that will always be enumerated
+ *
+ * Possible values:
+ * - indexed array
+ */
+define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum');
+
+/**
+ * option: Encoding of the XML document
+ *
+ * Possible values:
+ * - UTF-8
+ * - ISO-8859-1
+ */
+define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding');
+
+/**
+ * option: Desired target encoding of the data
+ *
+ * Possible values:
+ * - UTF-8
+ * - ISO-8859-1
+ */
+define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding');
+
+/**
+ * option: Callback that will be applied to textual data
+ *
+ * Possible values:
+ * - any valid PHP callback
+ */
+define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction');
+
+/**
+ * option: whether to return the result of the unserialization from unserialize()
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
+
+/**
+ * option: set the whitespace behaviour
+ *
+ * Possible values:
+ * - XML_UNSERIALIZER_WHITESPACE_KEEP
+ * - XML_UNSERIALIZER_WHITESPACE_TRIM
+ * - XML_UNSERIALIZER_WHITESPACE_NORMALIZE
+ */
+define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace');
+
+/**
+ * Keep all whitespace
+ */
+define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep');
+
+/**
+ * remove whitespace from start and end of the data
+ */
+define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim');
+
+/**
+ * normalize whitespace
+ */
+define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize');
+
+/**
+ * option: whether to ovverride all options that have been set before
+ *
+ * Possible values:
+ * - true
+ * - false (default)
+ */
+define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions');
+
+/**
+ * option: list of tags, that will not be used as keys
+ */
+define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys');
+
+/**
+ * option: whether to use type guessing for scalar values
+ */
+define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes');
+
+/**
+ * error code for no serialization done
+ */
+define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151);
+
+/**
+ * XML_Unserializer
+ *
+ * class to unserialize XML documents that have been created with
+ * XML_Serializer. To unserialize an XML document you have to add
+ * type hints to the XML_Serializer options.
+ *
+ * If no type hints are available, XML_Unserializer will guess how
+ * the tags should be treated, that means complex structures will be
+ * arrays and tags with only CData in them will be strings.
+ *
+ * <code>
+ * require_once 'XML/Unserializer.php';
+ *
+ * // be careful to always use the ampersand in front of the new operator
+ * $unserializer = &new XML_Unserializer();
+ *
+ * $unserializer->unserialize($xml);
+ *
+ * $data = $unserializer->getUnserializedData();
+ * <code>
+ *
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @author Stephan Schmidt <schst@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/XML_Serializer
+ * @see XML_Serializer
+ */
+class XML_Unserializer extends PEAR
+{
+ /**
+ * list of all available options
+ *
+ * @access private
+ * @var array
+ */
+ var $_knownOptions = array(
+ XML_UNSERIALIZER_OPTION_COMPLEXTYPE,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS,
+ XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME,
+ XML_UNSERIALIZER_OPTION_DEFAULT_CLASS,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY,
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND,
+ XML_UNSERIALIZER_OPTION_CONTENT_KEY,
+ XML_UNSERIALIZER_OPTION_TAG_MAP,
+ XML_UNSERIALIZER_OPTION_FORCE_ENUM,
+ XML_UNSERIALIZER_OPTION_ENCODING_SOURCE,
+ XML_UNSERIALIZER_OPTION_ENCODING_TARGET,
+ XML_UNSERIALIZER_OPTION_DECODE_FUNC,
+ XML_UNSERIALIZER_OPTION_RETURN_RESULT,
+ XML_UNSERIALIZER_OPTION_WHITESPACE,
+ XML_UNSERIALIZER_OPTION_IGNORE_KEYS,
+ XML_UNSERIALIZER_OPTION_GUESS_TYPES
+ );
+ /**
+ * default options for the serialization
+ *
+ * @access private
+ * @var array
+ */
+ var $_defaultOptions = array(
+ XML_UNSERIALIZER_OPTION_COMPLEXTYPE => 'array', // complex types will be converted to arrays, if no type hint is given
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey', // get array key/property name from this attribute
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type', // get type from this attribute
+ XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class', // get class from this attribute (if not given, use tag name)
+ XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME => true, // use the tagname as the classname
+ XML_UNSERIALIZER_OPTION_DEFAULT_CLASS => 'stdClass', // name of the class that is used to create objects
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE => false, // parse the attributes of the tag into an array
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false, // parse them into sperate array (specify name of array here)
+ XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND => '', // prepend attribute names with this string
+ XML_UNSERIALIZER_OPTION_CONTENT_KEY => '_content', // put cdata found in a tag that has been converted to a complex type in this key
+ XML_UNSERIALIZER_OPTION_TAG_MAP => array(), // use this to map tagnames
+ XML_UNSERIALIZER_OPTION_FORCE_ENUM => array(), // these tags will always be an indexed array
+ XML_UNSERIALIZER_OPTION_ENCODING_SOURCE => null, // specify the encoding character of the document to parse
+ XML_UNSERIALIZER_OPTION_ENCODING_TARGET => null, // specify the target encoding
+ XML_UNSERIALIZER_OPTION_DECODE_FUNC => null, // function used to decode data
+ XML_UNSERIALIZER_OPTION_RETURN_RESULT => false, // unserialize() returns the result of the unserialization instead of true
+ XML_UNSERIALIZER_OPTION_WHITESPACE => XML_UNSERIALIZER_WHITESPACE_TRIM, // remove whitespace around data
+ XML_UNSERIALIZER_OPTION_IGNORE_KEYS => array(), // List of tags that will automatically be added to the parent, instead of adding a new key
+ XML_UNSERIALIZER_OPTION_GUESS_TYPES => false // Whether to use type guessing
+ );
+
+ /**
+ * current options for the serialization
+ *
+ * @access public
+ * @var array
+ */
+ var $options = array();
+
+ /**
+ * unserialized data
+ *
+ * @access private
+ * @var string
+ */
+ var $_unserializedData = null;
+
+ /**
+ * name of the root tag
+ *
+ * @access private
+ * @var string
+ */
+ var $_root = null;
+
+ /**
+ * stack for all data that is found
+ *
+ * @access private
+ * @var array
+ */
+ var $_dataStack = array();
+
+ /**
+ * stack for all values that are generated
+ *
+ * @access private
+ * @var array
+ */
+ var $_valStack = array();
+
+ /**
+ * current tag depth
+ *
+ * @access private
+ * @var int
+ */
+ var $_depth = 0;
+
+ /**
+ * XML_Parser instance
+ *
+ * @access private
+ * @var object XML_Parser
+ */
+ var $_parser = null;
+
+ /**
+ * constructor
+ *
+ * @access public
+ * @param mixed $options array containing options for the unserialization
+ */
+ function XML_Unserializer($options = null)
+ {
+ if (is_array($options)) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = $this->_defaultOptions;
+ }
+ }
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ function apiVersion()
+ {
+ return '@package_version@';
+ }
+
+ /**
+ * reset all options to default options
+ *
+ * @access public
+ * @see setOption(), XML_Unserializer(), setOptions()
+ */
+ function resetOptions()
+ {
+ $this->options = $this->_defaultOptions;
+ }
+
+ /**
+ * set an option
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Unserializer(), setOptions()
+ */
+ function setOption($name, $value)
+ {
+ $this->options[$name] = $value;
+ }
+
+ /**
+ * sets several options at once
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Unserializer(), setOption()
+ */
+ function setOptions($options)
+ {
+ $this->options = array_merge($this->options, $options);
+ }
+
+ /**
+ * unserialize data
+ *
+ * @access public
+ * @param mixed $data data to unserialize (string, filename or resource)
+ * @param boolean $isFile data should be treated as a file
+ * @param array $options options that will override the global options for this call
+ * @return boolean $success
+ */
+ function unserialize($data, $isFile = false, $options = null)
+ {
+ $this->_unserializedData = null;
+ $this->_root = null;
+
+ // if options have been specified, use them instead
+ // of the previously defined ones
+ if (is_array($options)) {
+ $optionsBak = $this->options;
+ if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS]) && $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = array_merge($this->options, $options);
+ }
+ } else {
+ $optionsBak = null;
+ }
+
+ $this->_valStack = array();
+ $this->_dataStack = array();
+ $this->_depth = 0;
+
+ $this->_createParser();
+
+ if (is_string($data)) {
+ if ($isFile) {
+ $result = $this->_parser->setInputFile($data);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $result = $this->_parser->parse();
+ } else {
+ $result = $this->_parser->parseString($data,true);
+ }
+ } else {
+ $this->_parser->setInput($data);
+ $result = $this->_parser->parse();
+ }
+
+ if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) {
+ $return = $this->_unserializedData;
+ } else {
+ $return = true;
+ }
+
+ if ($optionsBak !== null) {
+ $this->options = $optionsBak;
+ }
+
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ return $return;
+ }
+
+ /**
+ * get the result of the serialization
+ *
+ * @access public
+ * @return string $serializedData
+ */
+ function getUnserializedData()
+ {
+ if ($this->_root === null) {
+ return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
+ }
+ return $this->_unserializedData;
+ }
+
+ /**
+ * get the name of the root tag
+ *
+ * @access public
+ * @return string $rootName
+ */
+ function getRootName()
+ {
+ if ($this->_root === null) {
+ return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
+ }
+ return $this->_root;
+ }
+
+ /**
+ * Start element handler for XML parser
+ *
+ * @access private
+ * @param object $parser XML parser object
+ * @param string $element XML element
+ * @param array $attribs attributes of XML tag
+ * @return void
+ */
+ function startHandler($parser, $element, $attribs)
+ {
+ if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])) {
+ $type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]];
+ $guessType = false;
+ } else {
+ $type = 'string';
+ if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
+ $guessType = true;
+ } else {
+ $guessType = false;
+ }
+ }
+
+ if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
+ $attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $attribs);
+ }
+
+ $this->_depth++;
+ $this->_dataStack[$this->_depth] = null;
+
+ if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP]) && isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])) {
+ $element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element];
+ }
+
+ $val = array(
+ 'name' => $element,
+ 'value' => null,
+ 'type' => $type,
+ 'guessType' => $guessType,
+ 'childrenKeys' => array(),
+ 'aggregKeys' => array()
+ );
+
+ if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true && (count($attribs) > 0)) {
+ $val['children'] = array();
+ $val['type'] = $this->_getComplexType($element);
+ $val['class'] = $element;
+
+ if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
+ $attribs = $this->_guessAndSetTypes($attribs);
+ }
+ if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false) {
+ $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs;
+ } else {
+ foreach ($attribs as $attrib => $value) {
+ $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND].$attrib] = $value;
+ }
+ }
+ }
+
+ $keyAttr = false;
+
+ if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY];
+ } elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element])) {
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element];
+ } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default'])) {
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default'];
+ } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default'])) {
+ // keep this for BC
+ $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default'];
+ }
+ }
+
+ if ($keyAttr !== false && isset($attribs[$keyAttr])) {
+ $val['name'] = $attribs[$keyAttr];
+ }
+
+ if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])) {
+ $val['class'] = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]];
+ }
+
+ array_push($this->_valStack, $val);
+ }
+
+ /**
+ * Try to guess the type of several values and
+ * set them accordingly
+ *
+ * @access private
+ * @param array array, containing the values
+ * @return array array, containing the values with their correct types
+ */
+ function _guessAndSetTypes($array)
+ {
+ foreach ($array as $key => $value) {
+ $array[$key] = $this->_guessAndSetType($value);
+ }
+ return $array;
+ }
+
+ /**
+ * Try to guess the type of a value and
+ * set it accordingly
+ *
+ * @access private
+ * @param string character data
+ * @return mixed value with the best matching type
+ */
+ function _guessAndSetType($value)
+ {
+ if ($value === 'true') {
+ return true;
+ }
+ if ($value === 'false') {
+ return false;
+ }
+ if ($value === 'NULL') {
+ return null;
+ }
+ if (preg_match('/^[-+]?[0-9]{1,}$/', $value)) {
+ return intval($value);
+ }
+ if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}$/', $value)) {
+ return doubleval($value);
+ }
+ return (string)$value;
+ }
+
+ /**
+ * End element handler for XML parser
+ *
+ * @access private
+ * @param object XML parser object
+ * @param string
+ * @return void
+ */
+ function endHandler($parser, $element)
+ {
+ $value = array_pop($this->_valStack);
+ switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) {
+ case XML_UNSERIALIZER_WHITESPACE_KEEP:
+ $data = $this->_dataStack[$this->_depth];
+ break;
+ case XML_UNSERIALIZER_WHITESPACE_NORMALIZE:
+ $data = trim(preg_replace('/\s\s+/m', ' ', $this->_dataStack[$this->_depth]));
+ break;
+ case XML_UNSERIALIZER_WHITESPACE_TRIM:
+ default:
+ $data = trim($this->_dataStack[$this->_depth]);
+ break;
+ }
+
+ // adjust type of the value
+ switch(strtolower($value['type'])) {
+
+ // unserialize an object
+ case 'object':
+ if (isset($value['class'])) {
+ $classname = $value['class'];
+ } else {
+ $classname = '';
+ }
+ // instantiate the class
+ if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true && class_exists($classname)) {
+ $value['value'] = &new $classname;
+ } else {
+ $value['value'] = &new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS];
+ }
+ if (trim($data) !== '') {
+ if ($value['guessType'] === true) {
+ $data = $this->_guessAndSetType($data);
+ }
+ $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
+ }
+
+ // set properties
+ foreach ($value['children'] as $prop => $propVal) {
+ // check whether there is a special method to set this property
+ $setMethod = 'set'.$prop;
+ if (method_exists($value['value'], $setMethod)) {
+ call_user_func(array(&$value['value'], $setMethod), $propVal);
+ } else {
+ $value['value']->$prop = $propVal;
+ }
+ }
+ // check for magic function
+ if (method_exists($value['value'], '__wakeup')) {
+ $value['value']->__wakeup();
+ }
+ break;
+
+ // unserialize an array
+ case 'array':
+ if (trim($data) !== '') {
+ if ($value['guessType'] === true) {
+ $data = $this->_guessAndSetType($data);
+ }
+ $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
+ }
+ if (isset($value['children'])) {
+ $value['value'] = $value['children'];
+ } else {
+ $value['value'] = array();
+ }
+ break;
+
+ // unserialize a null value
+ case 'null':
+ $data = null;
+ break;
+
+ // unserialize a resource => this is not possible :-(
+ case 'resource':
+ $value['value'] = $data;
+ break;
+
+ // unserialize any scalar value
+ default:
+ if ($value['guessType'] === true) {
+ $data = $this->_guessAndSetType($data);
+ } else {
+ settype($data, $value['type']);
+ }
+
+ $value['value'] = $data;
+ break;
+ }
+ $parent = array_pop($this->_valStack);
+ if ($parent === null) {
+ $this->_unserializedData = &$value['value'];
+ $this->_root = &$value['name'];
+ return true;
+ } else {
+ // parent has to be an array
+ if (!isset($parent['children']) || !is_array($parent['children'])) {
+ $parent['children'] = array();
+ if (!in_array($parent['type'], array('array', 'object'))) {
+ $parent['type'] = $this->_getComplexType($parent['name']);
+ if ($parent['type'] == 'object') {
+ $parent['class'] = $parent['name'];
+ }
+ }
+ }
+
+ if (in_array($element, $this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])) {
+ $ignoreKey = true;
+ } else {
+ $ignoreKey = false;
+ }
+
+ if (!empty($value['name']) && $ignoreKey === false) {
+ // there already has been a tag with this name
+ if (in_array($value['name'], $parent['childrenKeys']) || in_array($value['name'], $this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])) {
+ // no aggregate has been created for this tag
+ if (!in_array($value['name'], $parent['aggregKeys'])) {
+ if (isset($parent['children'][$value['name']])) {
+ $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
+ } else {
+ $parent['children'][$value['name']] = array();
+ }
+ array_push($parent['aggregKeys'], $value['name']);
+ }
+ array_push($parent['children'][$value['name']], $value['value']);
+ } else {
+ $parent['children'][$value['name']] = &$value['value'];
+ array_push($parent['childrenKeys'], $value['name']);
+ }
+ } else {
+ array_push($parent['children'], $value['value']);
+ }
+ array_push($this->_valStack, $parent);
+ }
+
+ $this->_depth--;
+ }
+
+ /**
+ * Handler for character data
+ *
+ * @access private
+ * @param object XML parser object
+ * @param string CDATA
+ * @return void
+ */
+ function cdataHandler($parser, $cdata)
+ {
+ if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
+ $cdata = call_user_func($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata);
+ }
+ $this->_dataStack[$this->_depth] .= $cdata;
+ }
+
+ /**
+ * get the complex type, that should be used for a specified tag
+ *
+ * @access private
+ * @param string name of the tag
+ * @return string complex type ('array' or 'object')
+ */
+ function _getComplexType($tagname)
+ {
+ if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) {
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE];
+ }
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) {
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname];
+ }
+ if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) {
+ return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'];
+ }
+ return 'array';
+ }
+
+ /**
+ * create the XML_Parser instance
+ *
+ * @access private
+ * @return boolean
+ */
+ function _createParser()
+ {
+ if (is_object($this->_parser)) {
+ $this->_parser->free();
+ unset($this->_parser);
+ }
+ $this->_parser = &new XML_Parser($this->options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE], 'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]);
+ $this->_parser->folding = false;
+ $this->_parser->setHandlerObj($this);
+ return true;
+ }
+}
+?> \ No newline at end of file
diff --git a/libs/XML/Util.php b/libs/XML/Util.php
new file mode 100644
index 0000000000..133de882c9
--- /dev/null
+++ b/libs/XML/Util.php
@@ -0,0 +1,752 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stephan Schmidt <schst@php-tools.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Util.php,v 1.28 2006/12/16 09:42:56 schst Exp $
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_CHARS", 51);
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_START", 52);
+
+/**
+ * error code for non-scalar tag content
+ */
+define("XML_UTIL_ERROR_NON_SCALAR_CONTENT", 60);
+
+/**
+ * error code for missing tag name
+ */
+define("XML_UTIL_ERROR_NO_TAG_NAME", 61);
+
+/**
+ * replace XML entities
+ */
+define("XML_UTIL_REPLACE_ENTITIES", 1);
+
+/**
+ * embedd content in a CData Section
+ */
+define("XML_UTIL_CDATA_SECTION", 5);
+
+/**
+ * do not replace entitites
+ */
+define("XML_UTIL_ENTITIES_NONE", 0);
+
+/**
+ * replace all XML entitites
+ * This setting will replace <, >, ", ' and &
+ */
+define("XML_UTIL_ENTITIES_XML", 1);
+
+/**
+ * replace only required XML entitites
+ * This setting will replace <, " and &
+ */
+define("XML_UTIL_ENTITIES_XML_REQUIRED", 2);
+
+/**
+ * replace HTML entitites
+ * @link http://www.php.net/htmlentities
+ */
+define("XML_UTIL_ENTITIES_HTML", 3);
+
+/**
+ * Collapse all empty tags.
+ */
+define("XML_UTIL_COLLAPSE_ALL", 1);
+
+/**
+ * Collapse only empty XHTML tags that have no end tag.
+ */
+define("XML_UTIL_COLLAPSE_XHTML_ONLY", 2);
+
+/**
+ * utility class for working with XML documents
+ *
+ * @category XML
+ * @package XML_Util
+ * @version 1.1.0
+ * @author Stephan Schmidt <schst@php.net>
+ */
+class XML_Util {
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ static function apiVersion()
+ {
+ return '1.1';
+ }
+
+ /**
+ * replace XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be replaced.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // replace XML entites:
+ * $string = XML_Util::replaceEntities("This string contains < & >.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see reverseEntities()
+ */
+ static function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&' => '&amp;',
+ '>' => '&gt;',
+ '<' => '&lt;',
+ '"' => '&quot;',
+ '\'' => '&apos;' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&' => '&amp;',
+ '<' => '&lt;',
+ '"' => '&quot;' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ return htmlentities($string);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * reverse XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be reversed.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // reverse XML entites:
+ * $string = XML_Util::reverseEntities("This string contains &lt; &amp; &gt;.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see replaceEntities()
+ */
+ static function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&amp;' => '&',
+ '&gt;' => '>',
+ '&lt;' => '<',
+ '&quot;' => '"',
+ '&apos;' => '\'' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&amp;' => '&',
+ '&lt;' => '<',
+ '&quot;' => '"' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ $arr = array_flip(get_html_translation_table(HTML_ENTITIES));
+ return strtr($string, $arr);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * build an xml declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get an XML declaration:
+ * $xmlDecl = XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $version xml version
+ * @param string $encoding character encoding
+ * @param boolean $standAlone document is standalone (or not)
+ * @return string $decl xml declaration
+ * @uses XML_Util::attributesToString() to serialize the attributes of the XML declaration
+ */
+ static function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
+ {
+ $attributes = array(
+ "version" => $version,
+ );
+ // add encoding
+ if ($encoding !== null) {
+ $attributes["encoding"] = $encoding;
+ }
+ // add standalone, if specified
+ if ($standalone !== null) {
+ $attributes["standalone"] = $standalone ? "yes" : "no";
+ }
+
+ return sprintf("<?xml%s?>", XML_Util::attributesToString($attributes, false));
+ }
+
+ /**
+ * build a document type declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get a doctype declaration:
+ * $xmlDecl = XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $root name of the root tag
+ * @param string $uri uri of the doctype definition (or array with uri and public id)
+ * @param string $internalDtd internal dtd entries
+ * @return string $decl doctype declaration
+ * @since 0.2
+ */
+ static function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
+ {
+ if (is_array($uri)) {
+ $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
+ } elseif (!empty($uri)) {
+ $ref = sprintf( ' SYSTEM "%s"', $uri );
+ } else {
+ $ref = "";
+ }
+
+ if (empty($internalDtd)) {
+ return sprintf("<!DOCTYPE %s%s>", $root, $ref);
+ } else {
+ return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
+ }
+ }
+
+ /**
+ * create string representation of an attribute list
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // build an attribute string
+ * $att = array(
+ * "foo" => "bar",
+ * "argh" => "tomato"
+ * );
+ *
+ * $attList = XML_Util::attributesToString($att);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $attributes attribute array
+ * @param boolean|array $sort sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
+ * @param boolean $multiline use linebreaks, if more than one attribute is given
+ * @param string $indent string used for indentation of multiline attributes
+ * @param string $linebreak string used for linebreaks of multiline attributes
+ * @param integer $entities setting for entities in attribute values (one of XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string representation of the attributes
+ * @uses XML_Util::replaceEntities() to replace XML entities in attribute values
+ * @todo allow sort also to be an options array
+ */
+ static function attributesToString($attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
+ {
+ /**
+ * second parameter may be an array
+ */
+ if (is_array($sort)) {
+ if (isset($sort['multiline'])) {
+ $multiline = $sort['multiline'];
+ }
+ if (isset($sort['indent'])) {
+ $indent = $sort['indent'];
+ }
+ if (isset($sort['linebreak'])) {
+ $multiline = $sort['linebreak'];
+ }
+ if (isset($sort['entities'])) {
+ $entities = $sort['entities'];
+ }
+ if (isset($sort['sort'])) {
+ $sort = $sort['sort'];
+ } else {
+ $sort = true;
+ }
+ }
+ $string = '';
+ if (is_array($attributes) && !empty($attributes)) {
+ if ($sort) {
+ ksort($attributes);
+ }
+ if( !$multiline || count($attributes) == 1) {
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ if ($entities === XML_UTIL_CDATA_SECTION) {
+ $entities = XML_UTIL_ENTITIES_XML;
+ }
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ $string .= ' '.$key.'="'.$value.'"';
+ }
+ } else {
+ $first = true;
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ if ($first) {
+ $string .= " ".$key.'="'.$value.'"';
+ $first = false;
+ } else {
+ $string .= $linebreak.$indent.$key.'="'.$value.'"';
+ }
+ }
+ }
+ }
+ return $string;
+ }
+
+ /**
+ * Collapses empty tags.
+ *
+ * @access public
+ * @static
+ * @param string $xml XML
+ * @param integer $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL) or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
+ * @return string $xml XML
+ */
+ static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL) {
+ if ($mode == XML_UTIL_COLLAPSE_XHTML_ONLY) {
+ return preg_replace(
+ '/<(area|base|br|col|hr|img|input|link|meta|param)([^>]*)><\/\\1>/s',
+ '<\\1\\2 />',
+ $xml
+ );
+ } else {
+ return preg_replace(
+ '/<(\w+)([^>]*)><\/\\1>/s',
+ '<\\1\\2 />',
+ $xml
+ );
+ }
+ }
+
+ /**
+ * create a tag
+ *
+ * This method will call XML_Util::createTagFromArray(), which
+ * is more flexible.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML tag:
+ * $tag = XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param mixed $content
+ * @param string $namespaceUri URI of the namespace
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML tag
+ * @see XML_Util::createTagFromArray()
+ * @uses XML_Util::createTagFromArray() to create the tag
+ */
+ static function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $sortAttributes = true)
+ {
+ $tag = array(
+ "qname" => $qname,
+ "attributes" => $attributes
+ );
+
+ // add tag content
+ if ($content !== null) {
+ $tag["content"] = $content;
+ }
+
+ // add namespace Uri
+ if ($namespaceUri !== null) {
+ $tag["namespaceUri"] = $namespaceUri;
+ }
+
+ return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $sortAttributes);
+ }
+
+ /**
+ * create a tag from an array
+ * this method awaits an array in the following format
+ * <pre>
+ * array(
+ * "qname" => $qname // qualified name of the tag
+ * "namespace" => $namespace // namespace prefix (optional, if qname is specified or no namespace)
+ * "localpart" => $localpart, // local part of the tagname (optional, if qname is specified)
+ * "attributes" => array(), // array containing all attributes (optional)
+ * "content" => $content, // tag content (optional)
+ * "namespaceUri" => $namespaceUri // namespaceUri for the given namespace (optional)
+ * )
+ * </pre>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * $tag = array(
+ * "qname" => "foo:bar",
+ * "namespaceUri" => "http://foo.com",
+ * "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
+ * "content" => "I'm inside the tag",
+ * );
+ * // creating a tag with qualified name and namespaceUri
+ * $string = XML_Util::createTagFromArray($tag);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $tag tag definition
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML tag
+ * @see XML_Util::createTag()
+ * @uses XML_Util::attributesToString() to serialize the attributes of the tag
+ * @uses XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
+ */
+ static function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $sortAttributes = true)
+ {
+ if (isset($tag['content']) && !is_scalar($tag['content'])) {
+ return XML_Util::raiseError( 'Supplied non-scalar value as tag content', XML_UTIL_ERROR_NON_SCALAR_CONTENT );
+ }
+
+ if (!isset($tag['qname']) && !isset($tag['localPart'])) {
+ return XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', XML_UTIL_ERROR_NO_TAG_NAME );
+ }
+
+ // if no attributes hav been set, use empty attributes
+ if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
+ $tag["attributes"] = array();
+ }
+
+ if (isset($tag['namespaces'])) {
+ foreach ($tag['namespaces'] as $ns => $uri) {
+ $tag['attributes']['xmlns:'.$ns] = $uri;
+ }
+ }
+
+ // qualified name is not given
+ if (!isset($tag["qname"])) {
+ // check for namespace
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
+ } else {
+ $tag["qname"] = $tag["localPart"];
+ }
+ // namespace URI is set, but no namespace
+ } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
+ $parts = XML_Util::splitQualifiedName($tag["qname"]);
+ $tag["localPart"] = $parts["localPart"];
+ if (isset($parts["namespace"])) {
+ $tag["namespace"] = $parts["namespace"];
+ }
+ }
+
+ if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
+ // is a namespace given
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
+ } else {
+ // define this Uri as the default namespace
+ $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
+ }
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($tag["qname"])+2));
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($tag['attributes'], $sortAttributes, $multiline, $indent, $linebreak, $replaceEntities );
+ if (!isset($tag['content']) || (string)$tag['content'] == '') {
+ $tag = sprintf('<%s%s />', $tag['qname'], $attList);
+ } else {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_NONE:
+ break;
+ case XML_UTIL_CDATA_SECTION:
+ $tag['content'] = XML_Util::createCDataSection($tag['content']);
+ break;
+ default:
+ $tag['content'] = XML_Util::replaceEntities($tag['content'], $replaceEntities);
+ break;
+ }
+ $tag = sprintf('<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'], $tag['qname'] );
+ }
+ return $tag;
+ }
+
+ /**
+ * create a start element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param string $namespaceUri URI of the namespace
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @param boolean $sortAttributes Whether to sort the attributes or not
+ * @return string $string XML start element
+ * @see XML_Util::createEndElement(), XML_Util::createTag()
+ */
+ static function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n", $sortAttributes = true)
+ {
+ // if no attributes hav been set, use empty attributes
+ if (!isset($attributes) || !is_array($attributes)) {
+ $attributes = array();
+ }
+
+ if ($namespaceUri != null) {
+ $parts = XML_Util::splitQualifiedName($qname);
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($qname)+2));
+ }
+ }
+
+ if ($namespaceUri != null) {
+ // is a namespace given
+ if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
+ $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
+ } else {
+ // define this Uri as the default namespace
+ $attributes["xmlns"] = $namespaceUri;
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($attributes, $sortAttributes, $multiline, $indent, $linebreak);
+ $element = sprintf("<%s%s>", $qname, $attList);
+ return $element;
+ }
+
+ /**
+ * create an end element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createEndElement("myNs:myTag");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @return string $string XML end element
+ * @see XML_Util::createStartElement(), XML_Util::createTag()
+ */
+ static function createEndElement($qname)
+ {
+ $element = sprintf("</%s>", $qname);
+ return $element;
+ }
+
+ /**
+ * create an XML comment
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createComment("I am a comment");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $content content of the comment
+ * @return string $comment XML comment
+ */
+ static function createComment($content)
+ {
+ $comment = sprintf("<!-- %s -->", $content);
+ return $comment;
+ }
+
+ /**
+ * create a CData section
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create a CData section
+ * $tag = XML_Util::createCDataSection("I am content.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $data data of the CData section
+ * @return string $string CData section with content
+ */
+ static function createCDataSection($data)
+ {
+ return sprintf("<![CDATA[%s]]>", $data);
+ }
+
+ /**
+ * split qualified name and return namespace and local part
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // split qualified tag
+ * $parts = XML_Util::splitQualifiedName("xslt:stylesheet");
+ * </code>
+ * the returned array will contain two elements:
+ * <pre>
+ * array(
+ * "namespace" => "xslt",
+ * "localPart" => "stylesheet"
+ * );
+ * </pre>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tag name
+ * @param string $defaultNs default namespace (optional)
+ * @return array $parts array containing namespace and local part
+ */
+ static function splitQualifiedName($qname, $defaultNs = null)
+ {
+ if (strstr($qname, ':')) {
+ $tmp = explode(":", $qname);
+ return array(
+ "namespace" => $tmp[0],
+ "localPart" => $tmp[1]
+ );
+ }
+ return array(
+ "namespace" => $defaultNs,
+ "localPart" => $qname
+ );
+ }
+
+ /**
+ * check, whether string is valid XML name
+ *
+ * <p>XML names are used for tagname, attribute names and various
+ * other, lesser known entities.</p>
+ * <p>An XML name may only consist of alphanumeric characters,
+ * dashes, undescores and periods, and has to start with a letter
+ * or an underscore.
+ * </p>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // verify tag name
+ * $result = XML_Util::isValidName("invalidTag?");
+ * if (XML_Util::isError($result)) {
+ * print "Invalid XML name: " . $result->getMessage();
+ * }
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $string string that should be checked
+ * @return mixed $valid true, if string is a valid XML name, PEAR error otherwise
+ * @todo support for other charsets
+ */
+ static function isValidName($string)
+ {
+ // check for invalid chars
+ if (!preg_match('/^[[:alpha:]_]$/', $string{0})) {
+ return XML_Util::raiseError('XML names may only start with letter or underscore', XML_UTIL_ERROR_INVALID_START);
+ }
+
+ // check for invalid chars
+ if (!preg_match('/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?[[:alpha:]_]([[:alnum:]\_\-\.]+)?$/', $string)) {
+ return XML_Util::raiseError('XML names may only contain alphanumeric chars, period, hyphen, colon and underscores', XML_UTIL_ERROR_INVALID_CHARS);
+ }
+ // XML name is valid
+ return true;
+ }
+
+ /**
+ * replacement for XML_Util::raiseError
+ *
+ * Avoids the necessity to always require
+ * PEAR.php
+ *
+ * @access public
+ * @param string error message
+ * @param integer error code
+ * @return object PEAR_Error
+ */
+ static function raiseError($msg, $code)
+ {
+ require_once 'PEAR.php';
+ return PEAR::raiseError($msg, $code);
+ }
+}
+?> \ No newline at end of file
diff --git a/modules/API/Proxy.php b/modules/API/Proxy.php
index d1c2c70a3b..f051024eee 100755
--- a/modules/API/Proxy.php
+++ b/modules/API/Proxy.php
@@ -4,6 +4,8 @@ class Piwik_API_Proxy
static $classCalled = null;
protected $alreadyRegistered = array();
private $api = null;
+
+ const NO_DEFAULT_VALUE = null;
static private $instance = null;
protected function __construct()
@@ -85,7 +87,7 @@ class Piwik_API_Proxy
{
$nameVariable = $parameter->getName();
- $defaultValue = '';
+ $defaultValue = Piwik_API_Proxy::NO_DEFAULT_VALUE;
if($parameter->isDefaultValueAvailable())
{
$defaultValue = $parameter->getDefaultValue();
@@ -94,7 +96,7 @@ class Piwik_API_Proxy
$aParameters[$nameVariable] = $defaultValue;
}
$this->api[$class][$name]['parameters'] = $aParameters;
- $this->api[$class][$name]['numberOfRequiredParameters'] = $method->getNumberOfRequiredParameters();
+ $this->api[$class][$name]['numberOfRequiredParameters'] = $method->getNumberOfParameters();
Piwik::log("- $name is public ".$this->getStrListParameters($class, $name));
}
diff --git a/modules/API/Request.php b/modules/API/Request.php
index 1127362e97..96211e1bc3 100644
--- a/modules/API/Request.php
+++ b/modules/API/Request.php
@@ -31,7 +31,11 @@ class Piwik_API_Request
$moduleMethod = Piwik_Common::getRequestVar('method', null, null, $this->requestToUse);
list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
-
+
+ if(!Piwik_PluginsManager::getInstance()->isPluginEnabled($module))
+ {
+ throw new Exception("The plugin '$module' is not enabled.");
+ }
// call the method via the PublicAPI class
$api = Piwik_Api_Proxy::getInstance();
$api->registerClass($module);
@@ -47,15 +51,19 @@ class Piwik_API_Request
$finalParameters = array();
foreach($parameters as $name => $defaultValue)
{
- if(!empty($defaultValue))
- {
- $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $this->requestToUse);
- }
- else
- {
- $requestValue = Piwik_Common::getRequestVar($name, null, null, $this->requestToUse);
- }
-
+ try{
+ // there is a default value specified
+ if($defaultValue !== Piwik_API_Proxy::NO_DEFAULT_VALUE)
+ {
+ $requestValue = Piwik_Common::getRequestVar($name, $defaultValue, null, $this->requestToUse);
+ }
+ else
+ {
+ $requestValue = Piwik_Common::getRequestVar($name, null, null, $this->requestToUse);
+ }
+ } catch(Exception $e) {
+ Piwik::error("The required variable '$name' is not correct or has not been found in the API Request. <br>\n ".var_export($this->requestToUse, true));
+ }
$finalParameters[] = $requestValue;
}
@@ -71,6 +79,7 @@ class Piwik_API_Request
$dataTable = $returnedValue;
$this->applyDataTableGenericFilters($dataTable);
+ $dataTable->applyQueuedFilters();
$toReturn = $this->getRenderedDataTable($dataTable);
}
@@ -94,22 +103,28 @@ class Piwik_API_Request
// Generic filters
// PatternFileName => Parameter names to match to constructor parameters
+ /*
+ * Order to apply the filters:
+ * 1 - Filter that remove filtered rows
+ * 2 - Filter that sort the remaining rows
+ * 3 - Filter that keep only a subset of the results
+ */
$genericFilters = array(
- 'Limit' => array(
- 'filter_offset' => 'integer',
- 'filter_limit' => 'integer',
- ),
'Pattern' => array(
'filter_column' => 'string',
'filter_pattern' => 'string',
),
+ 'ExcludeLowPopulation' => array(
+ 'filter_excludelowpop' => 'string',
+ 'filter_excludelowpop_value' => 'float',
+ ),
'Sort' => array(
'filter_sort_column' => 'string',
'filter_sort_order' => 'string',
),
- 'ExcludeLowPopulation' => array(
- 'filter_excludelowpop' => 'string',
- 'filter_excludelowpop_value' => 'float',
+ 'Limit' => array(
+ 'filter_offset' => 'integer',
+ 'filter_limit' => 'integer',
),
);
diff --git a/modules/Archive.php b/modules/Archive.php
index ce883e1da1..b4dfcd737c 100644
--- a/modules/Archive.php
+++ b/modules/Archive.php
@@ -206,7 +206,7 @@ class Piwik_Archive
}
- public function loadSubDataTables($name, Piwik_DataTable $dataTableToLoad)
+ public function loadSubDataTables($name, Piwik_DataTable $dataTableToLoad, $addDetailSubtableId = false)
{
// we have to recursively load all the subtables associated to this table's rows
// and update the subtableID so that it matches the newly instanciated table
@@ -220,16 +220,24 @@ class Piwik_Archive
$this->loadSubDataTables($name, $subDataTableLoaded);
+ // we edit the subtable ID so that it matches the newly table created in memory
+ // NB:
+ // we dont do that in the case we are displaying the table expanded.
+ // in this case we wan't the user to see the REAL dataId in the database
+ if($addDetailSubtableId)
+ {
+ $row->addDetail('databaseSubtableId', $row->getIdSubDataTable());
+ }
$row->setSubtable( $subDataTableLoaded );
}
}
}
- public function getDataTableExpanded($name)
+ public function getDataTableExpanded($name, $idSubTable = null)
{
$this->preFetchBlob($name);
- $dataTableToLoad = $this->getDataTable($name);
- $this->loadSubDataTables($name, $dataTableToLoad);
+ $dataTableToLoad = $this->getDataTable($name, $idSubTable);
+ $this->loadSubDataTables($name, $dataTableToLoad, $addDetailSubtableId = true);
return $dataTableToLoad;
}
@@ -258,6 +266,26 @@ class Piwik_Archive
return $table;
}
+ public function getDataTableFromNumeric( $fields )
+ {
+ require_once "DataTable/Simple.php";
+ if(!is_array($fields))
+ {
+ $fields = array($fields);
+ }
+
+ $values = array();
+ foreach($fields as $field)
+ {
+ $values[$field] = $this->getNumeric($field);
+ }
+
+ $table = new Piwik_DataTable_Simple;
+ $table->loadFromArray($values);
+
+ return $table;
+ }
+
public function getNumeric( $name )
{
diff --git a/modules/ArchiveProcessing.php b/modules/ArchiveProcessing.php
index fa7d122561..46b8628870 100644
--- a/modules/ArchiveProcessing.php
+++ b/modules/ArchiveProcessing.php
@@ -188,7 +188,7 @@ abstract class Piwik_ArchiveProcessing
protected function insertRecord($record)
{
// table to use to save the data
- if(is_numeric($record->value))
+ if(Piwik::isNumeric($record->value))
{
$table = $this->tableArchiveNumeric;
}
diff --git a/modules/ArchiveProcessing/Period.php b/modules/ArchiveProcessing/Period.php
index 7a7a6e6519..f08726daba 100644
--- a/modules/ArchiveProcessing/Period.php
+++ b/modules/ArchiveProcessing/Period.php
@@ -104,11 +104,10 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing
$archive->loadSubDataTables($name, $datatableToSum);
-// echo $datatableToSum;
$table->addDataTable($datatableToSum);
+
$archive->freeBlob($name);
}
-// echo $table;
return $table;
}
diff --git a/modules/DataFiles/SearchEngines.php b/modules/DataFiles/SearchEngines.php
index 94e61df515..f88a8ad7d3 100644
--- a/modules/DataFiles/SearchEngines.php
+++ b/modules/DataFiles/SearchEngines.php
@@ -51,6 +51,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"all.by" => array("All.by", "query"),
// Altavista
+ "www.altavista.com" => array("AltaVista", "q"),
"listings.altavista.com" => array("AltaVista", "q"),
"www.altavista.de" => array("AltaVista", "q"),
"altavista.fr" => array("AltaVista", "q"),
@@ -67,18 +68,17 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"us.altavista.com" => array("AltaVista", "q"),
"nl.altavista.com" => array("Altavista", "q"),
"ch.altavista.com" => array("AltaVista", "q"),
- "www.altavista.com" => array("AltaVista", "q"),
// APOLLO7
"www.apollo7.de" => array("Apollo7", "query"),
"apollo7.de" => array("Apollo7", "query"),
// AOL
+ "aolsearch.aol.com" => array("AOL", "query"),
"www.aolrecherche.aol.fr" => array("AOL", "q"),
"www.aolrecherches.aol.fr" => array("AOL", "query"),
"www.aolimages.aol.fr" => array("AOL", "query"),
"www.recherche.aol.fr" => array("AOL", "q"),
- "aolsearch.aol.com" => array("AOL", "query"),
"aolsearcht.aol.com" => array("AOL", "query"),
"find.web.aol.com" => array("AOL", "query"),
"recherche.aol.ca" => array("AOL", "query"),
@@ -101,6 +101,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"arianna.libero.it" => array("Arianna", "query"),
// Ask
+ "www.ask.com" => array("Ask", "ask"),
"web.ask.com" => array("Ask", "ask"),
"www.ask.co.uk" => array("Ask", "q"),
"uk.ask.com" => array("Ask", "q"),
@@ -110,7 +111,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"it.ask.com" => array("Ask", "q"),
"nl.ask.com" => array("Ask", "q"),
"ask.jp" => array("Ask", "q"),
- "www.ask.com" => array("Ask", "ask"),
// Atlas
"search.atlas.cz" => array("Atlas", "q", "windows-1250"),
@@ -195,10 +195,10 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"fr.dir.com" => array("dir.com", "req"),
// dmoz
+ "dmoz.org" => array("dmoz", "search"),
"editors.dmoz.org" => array("dmoz", "search"),
"search.dmoz.org" => array("dmoz", "search"),
"www.dmoz.org" => array("dmoz", "search"),
- "dmoz.org" => array("dmoz", "search"),
// Dogpile
"search.dogpile.com" => array("Dogpile", "q"),
@@ -241,8 +241,8 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"www.feedster.com" => array("Feedster", ""),
// Francite
- "antisearch.francite.com" => array("Francite", "KEYWORDS"),
"recherche.francite.com" => array("Francite", "name"),
+ "antisearch.francite.com" => array("Francite", "KEYWORDS"),
// Fireball
"suche.fireball.de" => array("Fireball", "query"),
@@ -266,8 +266,8 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"suche.freenet.de" => array("Freenet", "query"),
//Froogle
- "froogle.google.de" => array("Google (Froogle)", "q"),
"froogle.google.com" => array("Google (Froogle)", "q"),
+ "froogle.google.de" => array("Google (Froogle)", "q"),
"froogle.google.co.uk" => array("Google (Froogle)", "q"),
//GAIS
@@ -315,6 +315,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"search.sweetim.com" => array("Google", "q"),
// Google
+ "www.google.com" => array("Google", "q"),
"gogole.fr" => array("Google", "q"),
"www.gogole.fr" => array("Google", "q"),
"wwwgoogle.fr" => array("Google", "q"),
@@ -468,9 +469,9 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"www.google.co.za" => array("Google", "q"),
"www.google.co.ma" => array("Google", "q"),
"www.goggle.com" => array("Google", "q"),
- "www.google.com" => array("Google", "q"),
//Google Blogsearch
+ "blogsearch.google.com" => array("Google Blogsearch", "q"),
"blogsearch.google.de" => array("Google Blogsearch", "q"),
"blogsearch.google.fr" => array("Google Blogsearch", "q"),
"blogsearch.google.co.uk" => array("Google Blogsearch", "q"),
@@ -483,7 +484,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"blogsearch.google.at" => array("Google Blogsearch", "q"),
"blogsearch.google.ch" => array("Google Blogsearch", "q"),
"blogsearch.google.pl" => array("Google Blogsearch", "q"),
- "blogsearch.google.com" => array("Google Blogsearch", "q"),
// Google translation
@@ -493,6 +493,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"directory.google.com" => array("Google Directory", " "),
// Google Images
+ "images.google.com" => array("Google Images", "q"),
"images.google.fr" => array("Google Images", "q"),
"images.google.be" => array("Google Images", "q"),
"images.google.ca" => array("Google Images", "q"),
@@ -545,9 +546,9 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"images.google.ru" => array("Google Images", "q"),
"images.google.se" => array("Google Images", "q"),
"images.google.sk" => array("Google Images", "q"),
- "images.google.com" => array("Google Images", "q"),
// Google News
+ "news.google.com" => array("Google News", "q"),
"news.google.se" => array("Google News", "q"),
"news.google.com" => array("Google News", "q"),
"news.google.es" => array("Google News", "q"),
@@ -574,7 +575,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"news.google.com.ly" => array("Google News", "q"),
"news.google.it" => array("Google News", "q"),
"news.google.sm" => array("Google News", "q"),
- "news.google.com" => array("Google News", "q"),
// Goyellow.de
"www.goyellow.de" => array("GoYellow.de", "MDN"),
@@ -704,6 +704,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"www.mozbot.com" => array("mozbot", "q"),
// MSN
+ "search.msn.com" => array("MSN", "q"),
"beta.search.msn.fr" => array("MSN", "q"),
"search.msn.fr" => array("MSN", "q"),
"search.msn.es" => array("MSN", "q"),
@@ -736,7 +737,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"search.ninemsn.com.au" => array("MSN", "q"),
"search.msn.dk" => array("MSN", "q"),
"search.arabia.msn.com" => array("MSN", "q"),
- "search.msn.com" => array("MSN", "q"),
"search.prodigy.msn.com" => array("MSN", "q"),
// El Mundo
@@ -890,10 +890,10 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"search.supereva.com" => array("Supereva", "q"),
// Sympatico
+ "search.sympatico.msn.ca" => array("Sympatico", "q"),
"search.sli.sympatico.ca" => array("Sympatico", "q"),
"search.fr.sympatico.msn.ca" => array("Sympatico", "q"),
"sea.search.fr.sympatico.msn.ca"=> array("Sympatico", "q"),
- "search.sympatico.msn.ca" => array("Sympatico", "q"),
// Suchmaschine.com
"www.suchmaschine.com" => array("Suchmaschine.com", "suchstr"),
@@ -931,11 +931,11 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"search.virgilio.it" => array("Virgilio", "qs"),
// Voila
+ "search.voila.com" => array("Voila", "kw"),
"search.ke.voila.fr" => array("Voila", "rdata"),
"moteur.voila.fr" => array("Voila", "kw"),
"search.voila.fr" => array("Voila", "kw"),
"beta.voila.fr" => array("Voila", "kw"),
- "search.voila.com" => array("Voila", "kw"),
// Volny
"web.volny.cz" => array("Volny", "search", "windows-1250"),
@@ -955,6 +955,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"www.x-recherche.com" => array("X-Recherche", "mots"),
// Yahoo
+ "search.yahoo.com" => array("Yahoo!", "p"),
"ink.yahoo.com" => array("Yahoo!", "p"),
"ink.yahoo.fr" => array("Yahoo!", "p"),
"fr.ink.yahoo.com" => array("Yahoo!", "p"),
@@ -977,7 +978,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
"cade.search.yahoo.com" => array("Yahoo!", "p"),
"tw.search.yahoo.com" => array("Yahoo!", "p"),
"www.yahoo.com.cn" => array("Yahoo!", "p"),
- "search.yahoo.com" => array("Yahoo!", "p"),
"de.dir.yahoo.com" => array("Yahoo! Webverzeichnis", ""),
"cf.dir.yahoo.com" => array("Yahoo! Directory", ""),
@@ -1036,5 +1036,15 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
// Zoznam
"www.zoznam.sk" => array("Zoznam", "s"),
);
+
+ $GLOBALS['Piwik_SearchEngines_NameToUrl'] = array();
+ foreach($GLOBALS['Piwik_SearchEngines'] as $url => $info)
+ {
+ if(!isset($GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]]))
+ {
+ $GLOBALS['Piwik_SearchEngines_NameToUrl'][$info[0]] = $url;
+ }
+ }
+
}
?>
diff --git a/modules/DataTable.php b/modules/DataTable.php
index e92451ad91..c0cb2c312c 100644
--- a/modules/DataTable.php
+++ b/modules/DataTable.php
@@ -120,6 +120,7 @@ class Piwik_DataTable
protected $currentId;
protected $depthLevel = 0;
protected $indexNotUpToDate = false;
+ protected $queuedFilters = array();
const MAXIMUM_DEPTH_LEVEL_ALLOWED = 20;
@@ -134,6 +135,31 @@ class Piwik_DataTable
usort(&$this->rows, $functionCallback);
}
+ public function queueFilter( $className, $parameters = array() )
+ {
+ if(!is_array($parameters))
+ {
+ $parameters = array($parameters);
+ }
+ $this->queuedFilters[] = array('className' => $className, 'parameters' => $parameters);
+ }
+ public function applyQueuedFilters()
+ {
+ foreach($this->queuedFilters as $filter)
+ {
+ // make a reflection object
+ $reflectionObj = new ReflectionClass($filter['className']);
+
+ // the first parameter of a filter is the DataTable
+ // we add the current datatable as the parameter
+ $filter['parameters'] = array_merge(array($this), $filter['parameters']);
+
+ // use Reflection to create a new instance, using the $args
+ $filter = $reflectionObj->newInstanceArgs($filter['parameters']);
+ }
+ $this->queuedFilters = array();
+ }
+
public function rebuildIndex()
{
foreach($this->getRows() as $id => $row)
diff --git a/modules/DataTable/Filter.php b/modules/DataTable/Filter.php
index cd71b8378b..d3a09464d2 100644
--- a/modules/DataTable/Filter.php
+++ b/modules/DataTable/Filter.php
@@ -23,4 +23,6 @@ require_once "DataTable/Filter/Empty.php";
require_once "DataTable/Filter/ColumnCallback.php";
require_once "DataTable/Filter/ColumnCallbackReplace.php";
require_once "DataTable/Filter/ColumnCallbackAddDetail.php";
+require_once "DataTable/Filter/DetailCallbackAddDetail.php";
require_once "DataTable/Filter/ExcludeLowPopulation.php";
+require_once "DataTable/Filter/ReplaceColumnNames.php";
diff --git a/modules/DataTable/Filter/ColumnCallbackAddDetail.php b/modules/DataTable/Filter/ColumnCallbackAddDetail.php
index 2e367d635b..4668303f1f 100644
--- a/modules/DataTable/Filter/ColumnCallbackAddDetail.php
+++ b/modules/DataTable/Filter/ColumnCallbackAddDetail.php
@@ -9,15 +9,16 @@
*/
class Piwik_DataTable_Filter_ColumnCallbackAddDetail extends Piwik_DataTable_Filter
{
- private $columnToFilter;
+ private $columnToRead;
private $functionToApply;
+ private $detailToAdd;
- public function __construct( $table, $columnToRead, $columnToAdd, $functionToApply )
+ public function __construct( $table, $columnToRead, $detailToAdd, $functionToApply )
{
parent::__construct($table);
$this->functionToApply = $functionToApply;
$this->columnToRead = $columnToRead;
- $this->columnToAdd = $columnToAdd;
+ $this->detailToAdd = $detailToAdd;
$this->filter();
}
@@ -27,7 +28,7 @@ class Piwik_DataTable_Filter_ColumnCallbackAddDetail extends Piwik_DataTable_Fil
{
$oldValue = $row->getColumn($this->columnToRead);
$newValue = call_user_func( $this->functionToApply, $oldValue);
- $row->addDetail($this->columnToAdd, $newValue);
+ $row->addDetail($this->detailToAdd, $newValue);
}
}
}
diff --git a/modules/DataTable/Filter/DetailCallbackAddDetail.php b/modules/DataTable/Filter/DetailCallbackAddDetail.php
new file mode 100644
index 0000000000..67a53d0983
--- /dev/null
+++ b/modules/DataTable/Filter/DetailCallbackAddDetail.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * Add a new detail to the table based on the value resulting
+ * from a callback function with the parameter being another detail's value
+ *
+ * For example for the searchEngine we have a "details" information that gives
+ * the URL of the search engine. We use this URL to add a new "details" that gives
+ * the path of the logo for this search engine URL.
+ */
+class Piwik_DataTable_Filter_DetailCallbackAddDetail extends Piwik_DataTable_Filter
+{
+ private $detailToRead;
+ private $functionToApply;
+ private $detailToAdd;
+
+ public function __construct( $table, $detailToRead, $detailToAdd, $functionToApply )
+ {
+ parent::__construct($table);
+ $this->functionToApply = $functionToApply;
+ $this->detailToRead = $detailToRead;
+ $this->detailToAdd = $detailToAdd;
+ $this->filter();
+ }
+
+ protected function filter()
+ {
+ foreach($this->table->getRows() as $key => $row)
+ {
+ $oldValue = $row->getDetail($this->detailToRead);
+ $newValue = call_user_func( $this->functionToApply, $oldValue);
+ $row->addDetail($this->detailToAdd, $newValue);
+ }
+ }
+}
+
diff --git a/modules/DataTable/Filter/ExcludeLowPopulation.php b/modules/DataTable/Filter/ExcludeLowPopulation.php
index bd5e681521..d7630ea1ed 100644
--- a/modules/DataTable/Filter/ExcludeLowPopulation.php
+++ b/modules/DataTable/Filter/ExcludeLowPopulation.php
@@ -13,21 +13,18 @@ class Piwik_DataTable_Filter_ExcludeLowPopulation extends Piwik_DataTable_Filter
function filter()
{
$function = array("Piwik_DataTable_Filter_ExcludeLowPopulation","excludeLowPopulation");
-// echo "AVANT LOW FILTER".$this->table;
+
$filter = new Piwik_DataTable_Filter_ColumnCallback(
$this->table,
$this->columnToFilter,
$function
);
-// echo "APRES LOW FILTER".$this->table;
}
static public function excludeLowPopulation($value)
{
- $test = self::$minimumValue;
- $return = $value >= $test;
- return $return;
+ return $value >= self::$minimumValue;
}
}
?>
diff --git a/modules/DataTable/Filter/Limit.php b/modules/DataTable/Filter/Limit.php
index 3a3adecd7d..6ec00244a5 100644
--- a/modules/DataTable/Filter/Limit.php
+++ b/modules/DataTable/Filter/Limit.php
@@ -21,7 +21,7 @@ class Piwik_DataTable_Filter_Limit extends Piwik_DataTable_Filter
// - from 0 to offset
// at this point the array has offset less elements
- // - from limit - offset to the end - offset
+ // - from limit to the end
$table->deleteRowsOffset( 0, $this->offset );
$table->deleteRowsOffset( $this->limit );
}
diff --git a/modules/DataTable/Filter/ReplaceColumnNames.php b/modules/DataTable/Filter/ReplaceColumnNames.php
new file mode 100644
index 0000000000..6309b16938
--- /dev/null
+++ b/modules/DataTable/Filter/ReplaceColumnNames.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Delete all rows of when a given function returns false for a given column
+ */
+class Piwik_DataTable_Filter_ReplaceColumnNames extends Piwik_DataTable_Filter
+{
+ /*
+ * old column name => new column name
+ */
+ protected $mappingToApply = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 'nb_unique_visitors',
+ Piwik_Archive::INDEX_NB_VISITS => 'nb_visits',
+ Piwik_Archive::INDEX_NB_ACTIONS => 'nb_actions',
+ Piwik_Archive::INDEX_MAX_ACTIONS => 'max_actions',
+ Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length',
+ Piwik_Archive::INDEX_BOUNCE_COUNT => 'bounce_count',
+ );
+
+ public function __construct( $table, $mappingToApply = null )
+ {
+ parent::__construct($table);
+ if(!is_null($mappingToApply))
+ {
+ $this->mappingToApply = $mappingToApply;
+ }
+
+ $this->filter();
+ }
+
+ protected function filter()
+ {
+ foreach($this->table->getRows() as $key => $row)
+ {
+ $columns = $row->getColumns();
+
+ foreach($this->mappingToApply as $oldName => $newName)
+ {
+ // if the old column is there
+ if(isset($columns[$oldName]))
+ {
+ $columns[$newName] = $columns[$oldName];
+ unset($columns[$oldName]);
+ }
+ }
+
+ $row->setColumns($columns);
+ }
+ }
+}
+
diff --git a/modules/DataTable/Filter/Sort.php b/modules/DataTable/Filter/Sort.php
index 7f2e0c595b..a5c2463505 100644
--- a/modules/DataTable/Filter/Sort.php
+++ b/modules/DataTable/Filter/Sort.php
@@ -5,7 +5,7 @@ class Piwik_DataTable_Filter_Sort extends Piwik_DataTable_Filter
private $columnToSort;
private $order;
- public function __construct( $table, $columnToSort, $order )
+ public function __construct( $table, $columnToSort, $order = 'desc' )
{
parent::__construct($table);
$this->columnToSort = $columnToSort;
diff --git a/modules/DataTable/Renderer/HTML.php b/modules/DataTable/Renderer/HTML.php
index 9ee991006f..0e27fc25bd 100644
--- a/modules/DataTable/Renderer/HTML.php
+++ b/modules/DataTable/Renderer/HTML.php
@@ -64,6 +64,16 @@ class Piwik_DataTable_Renderer_HTML extends Piwik_DataTable_Renderer
}
$i++;
}
+
+ /*
+ // to keep the same columns order as the columns labelled with strings
+ ksort($allColumns);
+ //replace the label first
+ if(isset($allColumns['label']))
+ {
+ $allColumns = array_merge(array('label'=>true),$allColumns);
+ }
+ */
$allColumns['_details'] = true;
$allColumns['_idSubtable'] = true;
diff --git a/modules/DataTable/Renderer/PHP.php b/modules/DataTable/Renderer/PHP.php
index b4f7c1437d..228275fea0 100644
--- a/modules/DataTable/Renderer/PHP.php
+++ b/modules/DataTable/Renderer/PHP.php
@@ -1,6 +1,4 @@
<?php
-
-
class Piwik_DataTable_Renderer_PHP extends Piwik_DataTable_Renderer
{
protected $serialize;
@@ -12,7 +10,21 @@ class Piwik_DataTable_Renderer_PHP extends Piwik_DataTable_Renderer
function render()
{
- return $this->renderTable($this->table);
+ if($this->table instanceof Piwik_DataTable_Simple)
+ {
+ $array = $this->renderSimpleTable($this->table);
+ }
+ else
+ {
+ $array = $this->renderTable($this->table);
+ }
+
+ if($this->serialize)
+ {
+ $array = serialize($array);
+ }
+
+ return $array;
}
function renderTable($table)
@@ -28,14 +40,17 @@ class Piwik_DataTable_Renderer_PHP extends Piwik_DataTable_Renderer
);
$array[] = $newRow;
}
-
- if($this->serialize)
- {
- serialize($array);
- }
- else
+ return $array;
+ }
+
+ function renderSimpleTable($table)
+ {
+ $array = array();
+
+ foreach($table->getRows() as $row)
{
- return $array;
+ $array[$row->getColumn('label')] = $row->getColumn('value');
}
+ return $array;
}
} \ No newline at end of file
diff --git a/modules/DataTable/Renderer/XML.php b/modules/DataTable/Renderer/XML.php
index 51ea618536..0d6ebef305 100644
--- a/modules/DataTable/Renderer/XML.php
+++ b/modules/DataTable/Renderer/XML.php
@@ -17,32 +17,33 @@ class Piwik_DataTable_Renderer_XML extends Piwik_DataTable_Renderer
{
$renderer = new Piwik_DataTable_Renderer_PHP($table, $serialize = false);
$array = $renderer->render();
- $xmlStr = $this->array_to_simplexml($array)->asXML();
- $xmlStr = str_replace("<","\n<",$xmlStr);
+ require_once 'XML/Serializer.php';
+
+ $options = array(
+ XML_SERIALIZER_OPTION_INDENT => ' ',
+ XML_SERIALIZER_OPTION_LINEBREAKS => "\n",
+ XML_SERIALIZER_OPTION_ROOT_NAME => 'row',
+ XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_SIMPLEXML
+ );
+
+ $serializer = new XML_Serializer($options);
+
+ if($table instanceof Piwik_DataTable_Simple)
+ {
+ $serializer->setOption(XML_SERIALIZER_OPTION_ROOT_NAME, 'result');
+ }
+
+ $result = $serializer->serialize($array);
+
+ $xmlStr = $serializer->getSerializedData();
+
+ if(get_class($table) == 'Piwik_DataTable')
+ {
+ $xmlStr = "<result>\n".$xmlStr."\n</result>";
+ $xmlStr = str_replace(">\n", ">\n\t",$xmlStr);
+ $xmlStr = str_replace("\t</result>", "</result>",$xmlStr);
+ }
return $xmlStr;
- }
-
- // from http://uk3.php.net/simplexml
- function array_to_simplexml($array, $name="result" ,&$xml=null )
- {
- if(is_null($xml))
- {
- $xml = new SimpleXMLElement("<{$name}/>");
- }
-
- foreach($array as $key => $value)
- {
- if(is_array($value))
- {
- $xml->addChild($key);
- $this->array_to_simplexml($value, $name, $xml->$key);
- }
- else
- {
- $xml->addChild($key, $value);
- }
- }
- return $xml;
}
} \ No newline at end of file
diff --git a/modules/DataTable/Row.php b/modules/DataTable/Row.php
index f4143904e2..adc3c20665 100644
--- a/modules/DataTable/Row.php
+++ b/modules/DataTable/Row.php
@@ -80,6 +80,15 @@ class Piwik_DataTable_Row
return $this->c[self::DETAILS];
}
+ public function getDetail( $name )
+ {
+ if(!isset($this->c[self::DETAILS][$name]))
+ {
+ return false;
+ }
+ return $this->c[self::DETAILS][$name];
+ }
+
/**
* @return int|null
*/
@@ -128,6 +137,11 @@ class Piwik_DataTable_Row
$this->c[self::DATATABLE_ASSOCIATED] = $subTable->getId();
}
+ public function setColumns( $columns )
+ {
+ $this->c[self::COLUMNS] = $columns;
+ }
+
public function setColumn($name, $value)
{
$this->c[self::COLUMNS][$name] = $value;
@@ -161,12 +175,6 @@ class Piwik_DataTable_Row
*/
public function sumRow( $rowToSum )
{
-// if( $rowToSum->getIdSubDataTable() != null xor $this->getIdSubDataTable() != null )
-// {
-// throw new Exception("Only one of either \$this or \$rowToSum
-// has a subTable associated. Not expected.");
-// }
-//
foreach($rowToSum->getColumns() as $name => $value)
{
if($name != 'label'
diff --git a/modules/DataTable/Simple.php b/modules/DataTable/Simple.php
new file mode 100644
index 0000000000..4c86186024
--- /dev/null
+++ b/modules/DataTable/Simple.php
@@ -0,0 +1,20 @@
+<?php
+class Piwik_DataTable_Simple extends Piwik_DataTable
+{
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ function loadFromArray($array)
+ {
+ foreach($array as $label => $value)
+ {
+ $row = new Piwik_DataTable_Row;
+ $row->addColumn('label', $label);
+ $row->addColumn('value', $value);
+ $this->addRow($row);
+ }
+ }
+}
+?>
diff --git a/modules/ErrorHandler.php b/modules/ErrorHandler.php
index cb746380e6..7ae1fc9d9d 100755
--- a/modules/ErrorHandler.php
+++ b/modules/ErrorHandler.php
@@ -6,5 +6,29 @@ function Piwik_ErrorHandler($errno, $errstr, $errfile, $errline)
$backtrace = ob_get_contents();
ob_end_clean();
Zend_Registry::get('logger_error')->log($errno, $errstr, $errfile, $errline, $backtrace);
+
+ switch($errno)
+ {
+ case E_ERROR:
+ case E_PARSE:
+ case E_CORE_ERROR:
+ case E_CORE_WARNING:
+ case E_COMPILE_ERROR:
+ case E_COMPILE_WARNING:
+ case E_USER_ERROR:
+ case E_EXCEPTION:
+ exit;
+ break;
+
+ case E_WARNING:
+ case E_NOTICE:
+ case E_USER_WARNING:
+ case E_USER_NOTICE:
+ case E_STRICT:
+ case E_RECOVERABLE_ERROR:
+ default:
+ // do not exit
+ break;
+ }
}
?>
diff --git a/modules/Period.php b/modules/Period.php
index b2d0d899fd..53b7a570b7 100644
--- a/modules/Period.php
+++ b/modules/Period.php
@@ -75,6 +75,10 @@ abstract class Piwik_Period
//TODO test getDateEnd
public function getDateEnd()
{
+ if(!$this->subperiodsProcessed)
+ {
+ $this->generate();
+ }
if(count($this->subperiods) == 0)
{
return $this->getDate();
diff --git a/modules/Piwik.php b/modules/Piwik.php
index 45c240ec8e..96a33bde85 100755
--- a/modules/Piwik.php
+++ b/modules/Piwik.php
@@ -17,12 +17,18 @@ class Piwik
'year' =>4,
);
- static public function log($message = '', $priority = Zend_Log::NOTICE)
+ static public function log($message = '')
{
Zend_Registry::get('logger_message')->log($message);
Zend_Registry::get('logger_message')->log( "<br>" . PHP_EOL);
}
+
+ static public function error($message = '')
+ {
+ trigger_error($message, E_USER_ERROR);
+ }
+
//TODO TEST secureDiv
static public function secureDiv( $i1, $i2 )
{
@@ -68,6 +74,7 @@ class Piwik
static public function loadPlugins()
{
+ Piwik_PluginsManager::getInstance()->setLanguageToLoad( Piwik_Translate::getInstance()->getLanguageToLoad() );
Piwik_PluginsManager::getInstance()->setPluginsToLoad( Zend_Registry::get('config')->Plugins->enabled );
}
diff --git a/modules/Plugin.php b/modules/Plugin.php
index caf7527e33..d9b72adbd0 100644
--- a/modules/Plugin.php
+++ b/modules/Plugin.php
@@ -10,6 +10,43 @@ abstract class Piwik_Plugin
{
}
+ public function registerTranslation( $langCode )
+ {
+ $infos = $this->getInformation();
+ if(!isset($infos['translationAvailable']))
+ {
+ $infos['translationAvailable'] = false;
+ }
+ $translationAvailable = $infos['translationAvailable'];
+
+ if(!$translationAvailable)
+ {
+ return;
+ }
+
+ $name = $infos['name'];
+ $path = PIWIK_INCLUDE_PATH . "/plugins/" . $name ."/lang/%s.php";
+
+ $defaultLangPath = sprintf($path, $langCode);
+ $defaultEnglishLangPath = sprintf($path, 'en');
+
+ $translations = array();
+ if(is_readable($defaultLangPath))
+ {
+ require $defaultLangPath;
+ }
+ elseif(is_readable($defaultEnglishLangPath))
+ {
+ require $defaultEnglishLangPath;
+ }
+ else
+ {
+ throw new Exception("The language file couldn't be find for this plugin '$name'.");
+ }
+
+ Piwik_Translate::getInstance()->addTranslationArray($translations);
+ }
+
/**
* Returns the plugin details
*/
diff --git a/modules/PluginsManager.php b/modules/PluginsManager.php
index 295597e1cc..bf7caeca8f 100644
--- a/modules/PluginsManager.php
+++ b/modules/PluginsManager.php
@@ -33,6 +33,7 @@ class Piwik_PluginsManager
protected $pluginsToLoad = array();
protected $installPlugins = false;
protected $doLoadPlugins = true;
+ protected $languageToLoad = null;
static private $instance = null;
@@ -54,6 +55,11 @@ class Piwik_PluginsManager
$this->dispatcher = Event_Dispatcher::getInstance();
}
+ public function isPluginEnabled($name)
+ {
+ return in_array( $name, $this->pluginsToLoad->toArray());
+ }
+
public function setPluginsToLoad( $array )
{
$this->pluginsToLoad = $array;
@@ -124,11 +130,19 @@ class Piwik_PluginsManager
if($this->doLoadPlugins)
{
+
+ $newPlugin->registerTranslation( $this->languageToLoad );
$this->addPluginObservers( $newPlugin );
+
}
}
}
+ public function setLanguageToLoad( $code )
+ {
+ $this->languageToLoad = $code;
+ }
+
/**
* For the given plugin, add all the observers of this plugin.
*/
diff --git a/modules/Translate.php b/modules/Translate.php
new file mode 100644
index 0000000000..3c8e7706dd
--- /dev/null
+++ b/modules/Translate.php
@@ -0,0 +1,52 @@
+<?php
+class Piwik_Translate
+{
+ static private $instance = null;
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ private function __construct()
+ {
+ $GLOBALS['Piwik_translations'] = array();
+
+ $language = $this->getLanguageToLoad();
+
+ $translations = array();
+ require_once PIWIK_INCLUDE_PATH . "/lang/" . $language .".php";
+
+ $this->addTranslationArray($translations);
+ }
+
+ public function addTranslationArray($translation)
+ {
+ //TODO check that no string overlap?
+ $GLOBALS['Piwik_translations'] = array_merge($GLOBALS['Piwik_translations'], $translation);
+ }
+
+ public function getLanguageToLoad()
+ {
+ $language = Zend_Registry::get('config')->Language->current;
+
+ //TODO checker that it is safe
+
+ return $language;
+ }
+}
+
+function Piwik_Translate($index)
+{
+ if(isset($GLOBALS['Piwik_translations'][$index]))
+ {
+ return $GLOBALS['Piwik_translations'][$index];
+ }
+ throw new Exception("Translation string '$index' not available.");
+}
+?>
diff --git a/plugins/Actions.php b/plugins/Actions.php
index 74bd8ceeea..48c55d7d9d 100644
--- a/plugins/Actions.php
+++ b/plugins/Actions.php
@@ -275,23 +275,28 @@ class Piwik_Actions extends Piwik_Plugin
return $rowsProcessed;
}
+ static protected $nameToIdMapping = array(
+ 'nb_visits' => 1,
+ 'nb_hits' => 2,
+ 'entry_nb_unique_visitor' => 3,
+ 'entry_nb_visits' => 4,
+ 'entry_nb_actions' => 5,
+ 'entry_sum_visit_length' => 6,
+ 'entry_bounce_count' => 7,
+ 'exit_nb_unique_visitor' => 8,
+ 'exit_nb_visits' => 9,
+ 'exit_bounce_count' => 10,
+ 'sum_time_spent' => 11,
+
+ );
+ static public function getColumnsMap()
+ {
+ return array_flip(self::$nameToIdMapping);
+ }
+
protected function getIdColumn( $name )
{
- $map = array(
- 'nb_visits' => 0,
- 'nb_hits' => 1,
- 'entry_nb_unique_visitor' => 2,
- 'entry_nb_visits' => 3,
- 'entry_nb_actions' => 4,
- 'entry_sum_visit_length' => 5,
- 'entry_bounce_count' => 6,
- 'exit_nb_unique_visitor' => 7,
- 'exit_nb_visits' => 8,
- 'exit_bounce_count' => 9,
- 'sum_time_spent' => 10,
- );
-
- return $map[$name];
+ return self::$nameToIdMapping[$name];
}
}
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 1eac19b8f7..6ef2bd7c95 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -2,6 +2,7 @@
require_once "DataFiles/Browsers.php";
require_once "DataFiles/OS.php";
+require_once "Actions.php";
class Piwik_Actions_API extends Piwik_Apiable
{
@@ -21,11 +22,44 @@ class Piwik_Actions_API extends Piwik_Apiable
return self::$instance;
}
- public function getActions( $idSite, $period, $date )
+ protected function getDataTable($name, $idSite, $period, $date, $expanded, $idSubtable )
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
- $dataTable = $archive->getDataTableExpanded('Actions_actions');
+
+ if($idSubtable === false)
+ {
+ $idSubtable = null;
+ }
+
+ if($expanded)
+ {
+ $dataTable = $archive->getDataTableExpanded($name, $idSubtable);
+ }
+ else
+ {
+ $dataTable = $archive->getDataTable($name, $idSubtable);
+ }
+
+ $dataTable->queueFilter( 'Piwik_DataTable_Filter_ReplaceColumnNames',
+ array(Piwik_Actions::getColumnsMap())
+ );
return $dataTable;
}
-} \ No newline at end of file
+
+ public function getActions( $idSite, $period, $date, $expanded = false, $idSubtable = false )
+ {
+ return $this->getDataTable('Actions_actions', $idSite, $period, $date, $expanded, $idSubtable );
+ }
+
+ public function getDownloads( $idSite, $period, $date, $expanded = false, $idSubtable = false )
+ {
+ return $this->getDataTable('Actions_downloads', $idSite, $period, $date, $expanded, $idSubtable );
+ }
+
+ public function getOutlinks( $idSite, $period, $date, $expanded = false, $idSubtable = false )
+ {
+ return $this->getDataTable('Actions_outlink', $idSite, $period, $date, $expanded, $idSubtable );
+ }
+}
+
diff --git a/plugins/ExamplePlugin.php b/plugins/ExamplePlugin.php
index 4358f674b9..101a174e06 100644
--- a/plugins/ExamplePlugin.php
+++ b/plugins/ExamplePlugin.php
@@ -1,6 +1,6 @@
<?php
-class Piwik_Example extends Piwik_Plugin
+class Piwik_ExamplePlugin extends Piwik_Plugin
{
public function __construct()
{
@@ -10,11 +10,13 @@ class Piwik_Example extends Piwik_Plugin
public function getInformation()
{
$info = array(
- 'name' => 'Name',
+ // name must be the className prefix!
+ 'name' => 'ExamplePlugin',
'description' => 'Description',
'author' => 'Piwik',
'homepage' => 'http://piwik.org/',
'version' => '0.1',
+ 'translationAvailable' => false,
);
return $info;
@@ -40,4 +42,5 @@ class Piwik_Example extends Piwik_Plugin
{
$objectPassed = $notification->getNotificationObject();
}
-} \ No newline at end of file
+}
+
diff --git a/plugins/ExamplePlugin/API.php b/plugins/ExamplePlugin/API.php
new file mode 100644
index 0000000000..06d00e30d3
--- /dev/null
+++ b/plugins/ExamplePlugin/API.php
@@ -0,0 +1,32 @@
+<?php
+
+class Piwik_ExamplePlugin_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public function getAnswerToLife()
+ {
+ return 42;
+ }
+
+ public function getMoreInformationAnswerToLife()
+ {
+ return "Check http://en.wikipedia.org/wiki/The_Answer_to_Life,_the_Universe,_and_Everything";
+ }
+
+}
+
diff --git a/plugins/Provider/API.php b/plugins/Provider/API.php
new file mode 100644
index 0000000000..811805dc60
--- /dev/null
+++ b/plugins/Provider/API.php
@@ -0,0 +1,67 @@
+<?php
+
+require_once "DataFiles/Browsers.php";
+require_once "DataFiles/OS.php";
+require_once "Actions.php";
+
+class Piwik_Provider_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public function getProvider( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable('Provider_hostnameExt');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'url', 'Piwik_getHostnameUrl'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getHostnameName'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ return $dataTable;
+ }
+}
+
+
+function Piwik_getHostnameName($in)
+{
+ if(empty($in))
+ {
+ return "Unknown";
+ }
+ elseif(strtolower($in) === 'ip')
+ {
+ return "IP";
+ }
+ else
+ {
+ return ucfirst(substr($in, 0, strpos($in, '.')));
+ }
+
+}
+
+
+function Piwik_getHostnameUrl($in)
+{
+ if(empty($in)
+ || strtolower($in) === 'ip')
+ {
+ return "http://piwik.org/";
+ }
+ else
+ {
+ return "http://www.".$in."/";
+ }
+}
diff --git a/plugins/Referers.php b/plugins/Referers.php
index ad918f923a..9bc242b92f 100644
--- a/plugins/Referers.php
+++ b/plugins/Referers.php
@@ -21,6 +21,7 @@ class Piwik_Referers extends Piwik_Plugin
'author' => 'Piwik',
'homepage' => 'http://piwik.org/',
'version' => '0.1',
+ 'translationAvailable' => true,
);
return $info;
diff --git a/plugins/Referers/API.php b/plugins/Referers/API.php
new file mode 100644
index 0000000000..3047972707
--- /dev/null
+++ b/plugins/Referers/API.php
@@ -0,0 +1,160 @@
+<?php
+
+require_once "DataFiles/Browsers.php";
+require_once "DataFiles/OS.php";
+require_once "Actions.php";
+
+class Piwik_Referers_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ function getDataTable($name, $idSite, $period, $date, $idSubtable = null)
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+
+ $dataTable = $archive->getDataTable($name, $idSubtable);
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ return $dataTable;
+ }
+ function getRefererType($idSite, $period, $date)
+ {
+ $dataTable = $this->getDataTable('Referers_type',$idSite, $period, $date);
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel'));
+ return $dataTable;
+ }
+
+ function getKeywords($idSite, $period, $date)
+ {
+ $dataTable = $this->getDataTable('Referers_searchEngineByKeyword',$idSite, $period, $date);
+ return $dataTable;
+ }
+
+ function getSearchEnginesFromKeywordId($idSite, $period, $date, $idSubtable)
+ {
+ require PIWIK_DATAFILES_INCLUDE_PATH . "/SearchEngines.php";
+ $dataTable = $this->getDataTable('Referers_searchEngineByKeyword',$idSite, $period, $date, $idSubtable);
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
+ $dataTable->queueFilter('Piwik_DataTable_Filter_DetailCallbackAddDetail', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromName') );
+ return $dataTable;
+ }
+
+ function getSearchEngines($idSite, $period, $date)
+ {
+ $dataTable = $this->getDataTable('Referers_keywordBySearchEngine',$idSite, $period, $date);
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
+ $dataTable->queueFilter('Piwik_DataTable_Filter_DetailCallbackAddDetail', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromName') );
+ return $dataTable;
+ }
+
+ function getKeywordsFromSearchEngineId($idSite, $period, $date, $idSubtable)
+ {}
+
+ function getCampaigns($idSite, $period, $date)
+ {}
+ function getKeywordsFromCampaignId($idSite, $period, $date, $idSubtable)
+ {}
+
+ function getWebsites($idSite, $period, $date)
+ {}
+ function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable)
+ {}
+
+ function getPartners($idSite, $period, $date)
+ {}
+ function getUrlsFromPartnerId($idSite, $period, $date, $idSubtable)
+ {}
+
+
+ function getNumberOfDistinctSearchEngines($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctKeywords($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctCampaigns($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctWebsites($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctWebsitesUrls($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctPartners($idSite, $period, $date)
+ {}
+ function getNumberOfDistinctPartnersUrls($idSite, $period, $date)
+ {}
+}
+
+function Piwik_getSearchEngineUrlFromName($name)
+{
+ if(isset($GLOBALS['Piwik_SearchEngines_NameToUrl'][$name]))
+ {
+ $url = 'http://'.$GLOBALS['Piwik_SearchEngines_NameToUrl'][$name];
+ }
+ else
+ {
+ $url = 'URL unknown!';
+ }
+ return $url;
+}
+
+function Piwik_getSearchEngineLogoFromName($url)
+{
+ $path = PIWIK_PLUGINS_PATH . '/Referers/images/searchEngines/%s.png';
+
+ $beginningUrl = strpos($url,'//')+2;
+ $normalPath = sprintf($path, substr($url,$beginningUrl));
+
+ // flags not in the package !
+ if(!file_exists($normalPath))
+ {
+ return sprintf($path, 'xx');
+ }
+ return $normalPath;
+}
+
+function Piwik_getRefererTypeLabel($label)
+{
+ $indexTranslation = '';
+ switch($label)
+ {
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ $indexTranslation = 'Referers_DirectEntry';
+ break;
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ $indexTranslation = 'Referers_SearchEngines';
+ break;
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $indexTranslation = 'Referers_Websites';
+ break;
+ case Piwik_Common::REFERER_TYPE_PARTNER:
+ $indexTranslation = 'Referers_Partners';
+ break;
+ case Piwik_Common::REFERER_TYPE_NEWSLETTER:
+ $indexTranslation = 'Referers_Newsletters';
+ break;
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ $indexTranslation = 'Referers_Campaigns';
+ break;
+ }
+ return Piwik_Translate($indexTranslation);
+}
+//'Referers_keywordBySearchEngine',
+//'Referers_searchEngineByKeyword',
+//'Referers_type',
+
+//'Referers_keywordByCampaign',
+//'Referers_urlByWebsite',
+//'Referers_urlByPartner',
+
diff --git a/plugins/Referers/lang/en.php b/plugins/Referers/lang/en.php
new file mode 100644
index 0000000000..9d6248eaa6
--- /dev/null
+++ b/plugins/Referers/lang/en.php
@@ -0,0 +1,10 @@
+<?php
+$translations = array(
+ 'Referers_DirectEntry' => 'Direct Entry',
+ 'Referers_SearchEngines' => 'Search Engines',
+ 'Referers_Websites' => 'Websites',
+ 'Referers_Partners' => 'Partners',
+ 'Referers_Newsletters' => 'Newsletters',
+ 'Referers_Campaigns' => 'Campaigns',
+);
+?>
diff --git a/plugins/UserCountry.php b/plugins/UserCountry.php
index dfab3a1b97..2076a43932 100644
--- a/plugins/UserCountry.php
+++ b/plugins/UserCountry.php
@@ -15,6 +15,7 @@ class Piwik_UserCountry extends Piwik_Plugin
'author' => 'Piwik',
'homepage' => 'http://piwik.org/',
'version' => '0.1',
+ 'translationAvailable' => true,
);
return $info;
diff --git a/plugins/UserCountry/API.php b/plugins/UserCountry/API.php
new file mode 100644
index 0000000000..b797f17220
--- /dev/null
+++ b/plugins/UserCountry/API.php
@@ -0,0 +1,78 @@
+<?php
+
+require_once "DataFiles/Countries.php";
+
+class Piwik_UserCountry_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public function getCountry( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable('UserCountry_country');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'code', create_function('$label', 'return $label;')));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'flag', 'Piwik_getFlagFromCode'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_CountryTranslate'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ return $dataTable;
+ }
+ public function getContinent( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable('UserCountry_continent');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'code', create_function('$label', 'return $label;')));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_ContinentTranslate'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ return $dataTable;
+ }
+
+}
+
+function Piwik_getFlagFromCode($code)
+{
+ $path = PIWIK_PLUGINS_PATH . '/UserCountry/flags/%s.png';
+
+ $normalPath = sprintf($path,$code);
+
+ // flags not in the package !
+ if(!file_exists($normalPath))
+ {
+ return sprintf($path, 'xx');
+ }
+ return $normalPath;
+}
+
+function Piwik_ContinentTranslate($label)
+{
+ if($label == 'unk')
+ {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ return Piwik_Translate('continent_'. $label);
+}
+function Piwik_CountryTranslate($label)
+{
+ if($label == 'xx')
+ {
+ return Piwik_Translate('General_Unknown');
+ }
+
+ return Piwik_Translate('country_'. $label);
+} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/en.php b/plugins/UserCountry/lang/en.php
new file mode 100644
index 0000000000..1a5d880d4e
--- /dev/null
+++ b/plugins/UserCountry/lang/en.php
@@ -0,0 +1,263 @@
+<?php
+$translations = array (
+
+ // Countries
+ 'country_ac' => 'Ascension Islands',
+ 'country_ad' => 'Andorra',
+ 'country_ae' => 'United Arab Emirates',
+ 'country_af' => 'Afghanistan',
+ 'country_ag' => 'Antigua and Barbuda',
+ 'country_ai' => 'Anguilla',
+ 'country_al' => 'Albania',
+ 'country_am' => 'Armenia',
+ 'country_an' => 'Netherlands Antilles',
+ 'country_ao' => 'Angola',
+ 'country_aq' => 'Antarctica',
+ 'country_ar' => 'Argentina',
+ 'country_as' => 'American Samoa',
+ 'country_at' => 'Austria',
+ 'country_au' => 'Australia',
+ 'country_aw' => 'Aruba',
+ 'country_az' => 'Azerbaijan',
+ 'country_ba' => 'Bosnia and Herzegovina',
+ 'country_bb' => 'Barbados',
+ 'country_bd' => 'Bangladesh',
+ 'country_be' => 'Belgium',
+ 'country_bf' => 'Burkina Faso',
+ 'country_bg' => 'Bulgaria',
+ 'country_bh' => 'Bahrain',
+ 'country_bi' => 'Burundi',
+ 'country_bj' => 'Benin',
+ 'country_bm' => 'Bermuda',
+ 'country_bn' => 'Bruneo',
+ 'country_bo' => 'Bolivia',
+ 'country_br' => 'Brazil',
+ 'country_bs' => 'Bahamas',
+ 'country_bt' => 'Bhutan',
+ 'country_bv' => 'Bouvet Island',
+ 'country_bw' => 'Botswana',
+ 'country_by' => 'Belarus',
+ 'country_bz' => 'Belize',
+ 'country_ca' => 'Canada',
+ 'country_cc' => 'Cocos (Keeling) Islands',
+ 'country_cd' => 'Congo, The Democratic Republic of the',
+ 'country_cf' => 'Central African Republic',
+ 'country_cg' => 'Congo',
+ 'country_ch' => 'Switzerland',
+ 'country_ci' => "Cote D'Ivoire",
+ 'country_ck' => 'Cook Islands',
+ 'country_cl' => 'Chile',
+ 'country_cm' => 'Cameroon',
+ 'country_cn' => 'China',
+ 'country_co' => 'Colombia',
+ 'country_cr' => 'Costa Rica',
+ 'country_cs' => 'Serbia Montenegro',
+ 'country_cu' => 'Cuba',
+ 'country_cv' => 'Cape Verde',
+ 'country_cx' => 'Christmas Island',
+ 'country_cy' => 'Cyprus',
+ 'country_cz' => 'Czech Republic',
+ 'country_de' => 'Germany',
+ 'country_dj' => 'Djibouti',
+ 'country_dk' => 'Denmark',
+ 'country_dm' => 'Dominica',
+ 'country_do' => 'Dominican Republic',
+ 'country_dz' => 'Algeria',
+ 'country_ec' => 'Ecuador',
+ 'country_ee' => 'Estonia',
+ 'country_eg' => 'Egypt',
+ 'country_eh' => 'Western Sahara',
+ 'country_er' => 'Eritrea',
+ 'country_es' => 'Spain',
+ 'country_et' => 'Ethiopia',
+ 'country_fi' => 'Finland',
+ 'country_fj' => 'Fiji',
+ 'country_fk' => 'Falkland Islands (Malvinas)',
+ 'country_fm' => 'Micronesia, Federated States of',
+ 'country_fo' => 'Faroe Islands',
+ 'country_fr' => 'France',
+ 'country_ga' => 'Gabon',
+ 'country_gd' => 'Grenada',
+ 'country_ge' => 'Georgia',
+ 'country_gf' => 'French Guyana',
+ 'country_gg' => 'Guernsey',
+ 'country_gh' => 'Ghana',
+ 'country_gi' => 'Gibraltar',
+ 'country_gl' => 'Greenland',
+ 'country_gm' => 'Gambia',
+ 'country_gn' => 'Guinea',
+ 'country_gp' => 'Guadeloupe',
+ 'country_gq' => 'Equatorial Guinea',
+ 'country_gr' => 'Greece',
+ 'country_gs' => 'South Georgia and the South Sandwich Islands',
+ 'country_gt' => 'Guatemala',
+ 'country_gu' => 'Guam',
+ 'country_gw' => 'Guinea-Bissau',
+ 'country_gy' => 'Guyana',
+ 'country_hk' => 'Hong Kong',
+ 'country_hm' => 'Heard Island and McDonald Islands',
+ 'country_hn' => 'Honduras',
+ 'country_hr' => 'Croatia',
+ 'country_ht' => 'Haiti',
+ 'country_hu' => 'Hungary',
+ 'country_id' => 'Indonesia',
+ 'country_ie' => 'Ireland',
+ 'country_il' => 'Israel',
+ 'country_im' => 'Man Island',
+ 'country_in' => 'India',
+ 'country_io' => 'British Indian Ocean Territory',
+ 'country_iq' => 'Iraq',
+ 'country_ir' => 'Iran, Islamic Republic of',
+ 'country_is' => 'Iceland',
+ 'country_it' => 'Italy',
+ 'country_je' => 'Jersey',
+ 'country_jm' => 'Jamaica',
+ 'country_jo' => 'Jordan',
+ 'country_jp' => 'Japan',
+ 'country_ke' => 'Kenya',
+ 'country_kg' => 'Kyrgyzstan',
+ 'country_kh' => 'Cambodia',
+ 'country_ki' => 'Kiribati',
+ 'country_km' => 'Comoros',
+ 'country_kn' => 'Saint Kitts and Nevis',
+ 'country_kp' => "Korea, Democratic People's Republic of",
+ 'country_kr' => 'Korea, Republic of',
+ 'country_kw' => 'Kuwait',
+ 'country_ky' => 'Cayman Islands',
+ 'country_kz' => 'Kazakhstan',
+ 'country_la' => 'Laos',
+ 'country_lb' => 'Lebanon',
+ 'country_lc' => 'Saint Lucia',
+ 'country_li' => 'Liechtenstein',
+ 'country_lk' => 'Sri Lanka',
+ 'country_lr' => 'Liberia',
+ 'country_ls' => 'Lesotho',
+ 'country_lt' => 'Lithuania',
+ 'country_lu' => 'Luxembourg',
+ 'country_lv' => 'Latvia',
+ 'country_ly' => 'Libya',
+ 'country_ma' => 'Morocco',
+ 'country_mc' => 'Monaco',
+ 'country_md' => 'Moldova, Republic of',
+ 'country_mg' => 'Madagascar',
+ 'country_mh' => 'Marshall Islands',
+ 'country_mk' => 'Macedonia',
+ 'country_ml' => 'Mali',
+ 'country_mm' => 'Myanmar',
+ 'country_mn' => 'Mongolia',
+ 'country_mo' => 'Macau',
+ 'country_mp' => 'Northern Mariana Islands',
+ 'country_mq' => 'Martinique',
+ 'country_mr' => 'Mauritania',
+ 'country_ms' => 'Montserrat',
+ 'country_mt' => 'Malta',
+ 'country_mu' => 'Mauritius',
+ 'country_mv' => 'Maldives',
+ 'country_mw' => 'Malawi',
+ 'country_mx' => 'Mexico',
+ 'country_my' => 'Malaysia',
+ 'country_mz' => 'Mozambique',
+ 'country_na' => 'Namibia',
+ 'country_nc' => 'New Caledonia',
+ 'country_ne' => 'Niger',
+ 'country_nf' => 'Norfolk Island',
+ 'country_ng' => 'Nigeria',
+ 'country_ni' => 'Nicaragua',
+ 'country_nl' => 'Netherlands',
+ 'country_no' => 'Norway',
+ 'country_np' => 'Nepal',
+ 'country_nr' => 'Nauru',
+ 'country_nu' => 'Niue',
+ 'country_nz' => 'New Zealand',
+ 'country_om' => 'Oman',
+ 'country_pa' => 'Panama',
+ 'country_pe' => 'Peru',
+ 'country_pf' => 'French Polynesia',
+ 'country_pg' => 'Papua New Guinea',
+ 'country_ph' => 'Philippines',
+ 'country_pk' => 'Pakistan',
+ 'country_pl' => 'Poland',
+ 'country_pm' => 'Saint Pierre and Miquelon',
+ 'country_pn' => 'Pitcairn',
+ 'country_pr' => 'Puerto Rico',
+ 'country_ps' => 'Palestinian Territory',
+ 'country_pt' => 'Portugal',
+ 'country_pw' => 'Palau',
+ 'country_py' => 'Paraguay',
+ 'country_qa' => 'Qatar',
+ 'country_re' => 'Reunion Island',
+ 'country_ro' => 'Romania',
+ 'country_ru' => 'Russian Federation',
+ 'country_rs' => 'Russia',
+ 'country_rw' => 'Rwanda',
+ 'country_sa' => 'Saudi Arabia',
+ 'country_sb' => 'Solomon Islands',
+ 'country_sc' => 'Seychelles',
+ 'country_sd' => 'Sudan',
+ 'country_se' => 'Sweden',
+ 'country_sg' => 'Singapore',
+ 'country_sh' => 'Saint Helena',
+ 'country_si' => 'Slovenia',
+ 'country_sj' => 'Svalbard',
+ 'country_sk' => 'Slovakia',
+ 'country_sl' => 'Sierra Leone',
+ 'country_sm' => 'San Marino',
+ 'country_sn' => 'Senegal',
+ 'country_so' => 'Somalia',
+ 'country_sr' => 'Suriname',
+ 'country_st' => 'Sao Tome and Principe',
+ 'country_su' => 'Old U.S.S.R',
+ 'country_sv' => 'El Salvador',
+ 'country_sy' => 'Syrian Arab Republic',
+ 'country_sz' => 'Swaziland',
+ 'country_tc' => 'Turks and Caicos Islands',
+ 'country_td' => 'Chad',
+ 'country_tf' => 'French Southern Territories',
+ 'country_tg' => 'Togo',
+ 'country_th' => 'Thailand',
+ 'country_tj' => 'Tajikistan',
+ 'country_tk' => 'Tokelau',
+ 'country_tm' => 'Turkmenistan',
+ 'country_tn' => 'Tunisia',
+ 'country_to' => 'Tonga',
+ 'country_tp' => 'East Timor',
+ 'country_tr' => 'Turkey',
+ 'country_tt' => 'Trinidad and Tobago',
+ 'country_tv' => 'Tuvalu',
+ 'country_tw' => 'Taiwan',
+ 'country_tz' => 'Tanzania, United Republic of',
+ 'country_ua' => 'Ukraine',
+ 'country_ug' => 'Uganda',
+ 'country_uk' => 'United Kingdom',
+ 'country_gb' => 'Great Britain',
+ 'country_um' => 'United States Minor Outlying Islands',
+ 'country_us' => 'United States',
+ 'country_uy' => 'Uruguay',
+ 'country_uz' => 'Uzbekistan',
+ 'country_va' => 'Vatican City',
+ 'country_vc' => 'Saint Vincent and the Grenadines',
+ 'country_ve' => 'Venezuela',
+ 'country_vg' => 'Virgin Islands, British',
+ 'country_vi' => 'Virgin Islands, U.S.',
+ 'country_vn' => 'Vietnam',
+ 'country_vu' => 'Vanuatu',
+ 'country_wf' => 'Wallis and Futuna',
+ 'country_ws' => 'Samoa',
+ 'country_ye' => 'Yemen',
+ 'country_yt' => 'Mayotte',
+ 'country_yu' => 'Yugoslavia',
+ 'country_za' => 'South Africa',
+ 'country_zm' => 'Zambia',
+ 'country_zr' => 'Zaire',
+ 'country_zw' => 'Zimbabwe',
+
+ // Continents
+ 'continent_eur' => 'Europe',
+ 'continent_afr' => 'Africa',
+ 'continent_asi' => 'Asia',
+ 'continent_ams' => 'South and Central America',
+ 'continent_amn' => 'North America',
+ 'continent_oce' => 'Oceania',
+
+);
+
diff --git a/plugins/UserSettings/API.php b/plugins/UserSettings/API.php
index 45f8e513ef..808cd582ff 100644
--- a/plugins/UserSettings/API.php
+++ b/plugins/UserSettings/API.php
@@ -26,43 +26,41 @@ class Piwik_UserSettings_API extends Piwik_Apiable
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_resolution');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
return $dataTable;
}
-
+
public function getConfiguration( $idSite, $period, $date )
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_configuration');
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'Piwik_getConfigurationLabel');
-
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
return $dataTable;
}
+
public function getOS( $idSite, $period, $date )
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_os');
-
-
- $filter = new Piwik_DataTable_Filter_ColumnCallbackAddDetail($dataTable, 'label', 'shortLabel', 'Piwik_getOSShortLabel');
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'Piwik_getOSLabel');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array( 'label', 'shortLabel', 'Piwik_getOSShortLabel') );
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array( 'label', 'Piwik_getOSLabel') );
return $dataTable;
}
-
-
+
public function getBrowser( $idSite, $period, $date )
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_browser');
-
-
- $filter = new Piwik_DataTable_Filter_ColumnCallbackAddDetail($dataTable, 'label', 'logo', 'Piwik_getBrowsersLogo');
- $filter = new Piwik_DataTable_Filter_ColumnCallbackAddDetail($dataTable, 'label', 'shortLabel', 'Piwik_getBrowserShortLabel');
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'Piwik_getBrowserLabel');
-
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'logo', 'Piwik_getBrowsersLogo'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
return $dataTable;
}
@@ -71,11 +69,9 @@ class Piwik_UserSettings_API extends Piwik_Apiable
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_browserType');
-
-
- $filter = new Piwik_DataTable_Filter_ColumnCallbackAddDetail($dataTable, 'label', 'shortLabel', 'ucfirst');
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'Piwik_getBrowserTypeLabel');
-
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackAddDetail', array('label', 'shortLabel', 'ucfirst'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
return $dataTable;
}
@@ -84,23 +80,20 @@ class Piwik_UserSettings_API extends Piwik_Apiable
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_wideScreen');
-
-
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'ucfirst');
-
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'ucfirst'));
return $dataTable;
}
+
public function getPlugin( $idSite, $period, $date )
{
Piwik::checkUserHasViewAccess( $idSite );
$archive = Piwik_Archive::build($idSite, $date, $period );
$dataTable = $archive->getDataTable('UserSettings_plugin');
-
- $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($dataTable, 'label', 'ucfirst');
-
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'ucfirst'));
return $dataTable;
- }
-
+ }
}
function Piwik_getOSLabel($oldLabel)
@@ -111,6 +104,8 @@ function Piwik_getOSLabel($oldLabel)
}
return 'UNK';
}
+
+
function Piwik_getOSShortLabel($oldLabel)
{
if(isset($GLOBALS['Piwik_Oslist_IdToShortLabel'][$oldLabel]))
@@ -119,6 +114,7 @@ function Piwik_getOSShortLabel($oldLabel)
}
return 'UNK';
}
+
function Piwik_getBrowserTypeLabel($oldLabel)
{
if(isset(Piwik_UserSettings::$browserType_display[$oldLabel]))
@@ -134,11 +130,18 @@ function Piwik_getConfigurationLabel($str)
$values = explode(";", $str);
$os = Piwik_getOSLabel($values[0]);
- $browser = $GLOBALS['Piwik_BrowserList_IdToLabel'][$values[1]];
+ $name = $values[1];
+ $browser = 'Unknown';
+ if(isset($GLOBALS['Piwik_BrowserList_IdToLabel'][$name]))
+ {
+ $browser = $GLOBALS['Piwik_BrowserList_IdToLabel'][$name];
+ }
+
$resolution = $values[2];
return $os . " / " . $browser . " / " . $resolution;
}
+
function Piwik_getBrowserLabel($oldLabel)
{
$name = Piwik_getBrowserId($oldLabel);
@@ -176,4 +179,4 @@ function Piwik_getBrowsersLogo($label)
$id = Piwik_getBrowserId($label);
return "/plugins/UserSettings/images/browsers/". $id . ".gif";
}
-?>
+
diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php
new file mode 100644
index 0000000000..b42cb23dfc
--- /dev/null
+++ b/plugins/VisitFrequency/API.php
@@ -0,0 +1,37 @@
+<?php
+class Piwik_VisitFrequency_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ public function getSummary( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $toFetch = array(
+ 'nb_visits_returning',
+ 'nb_actions_returning',
+ 'max_actions_returning',
+ 'sum_visit_length_returning',
+ 'bounce_count_returning',
+ );
+ $dataTable = $archive->getDataTableFromNumeric($toFetch);
+
+ return $dataTable;
+ }
+
+}
+
diff --git a/plugins/VisitTime.php b/plugins/VisitTime.php
index 6cc1fbf1c2..4a4171b832 100644
--- a/plugins/VisitTime.php
+++ b/plugins/VisitTime.php
@@ -37,17 +37,16 @@ class Piwik_VisitTime extends Piwik_Plugin
);
return $hooks;
}
-
-
+
function archiveMonth( $notification )
{
$this->archiveProcessing = $notification->getNotificationObject();
-
+
$dataTableToSum = array(
'VisitTime_localTime',
'VisitTime_serverTime',
);
-
+
$this->archiveProcessing->archiveDataTable($dataTableToSum);
}
public function archiveDay( $notification )
diff --git a/plugins/VisitTime/API.php b/plugins/VisitTime/API.php
new file mode 100644
index 0000000000..951aedf43a
--- /dev/null
+++ b/plugins/VisitTime/API.php
@@ -0,0 +1,48 @@
+<?php
+
+class Piwik_VisitTime_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+ protected function getDataTable($name, $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->queueFilter('Piwik_DataTable_Filter_Sort', array('label', 'asc'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+
+ return $dataTable;
+ }
+
+ public function getVisitInformationPerLocalTime( $idSite, $period, $date )
+ {
+ return $this->getDataTable('VisitTime_localTime', $idSite, $period, $date );
+ }
+
+ public function getVisitInformationPerServerTime( $idSite, $period, $date )
+ {
+ return $this->getDataTable('VisitTime_serverTime', $idSite, $period, $date );
+ }
+}
+
+function Piwik_getTimeLabel($label)
+{
+ return $label . "h";
+} \ No newline at end of file
diff --git a/plugins/VisitorInterest.php b/plugins/VisitorInterest.php
index aeedd8826a..90093ab12f 100644
--- a/plugins/VisitorInterest.php
+++ b/plugins/VisitorInterest.php
@@ -126,13 +126,17 @@ class Piwik_VisitorInterest extends Piwik_Plugin
{
$minGap = $gap[0] * 60;
$maxGap = $gap[1] * 60;
- $gapName = "'$minGap-$maxGap'";
+
+ if($minGap == 0 || $minGap == 0.5)
+ {
+ $gapName = "'".$minGap."-".$maxGap."'";
+ }
$select[] = "sum(case when visit_total_time between $minGap and $maxGap then 1 else 0 end) as $gapName ";
}
else
{
$minGap = $gap[0] * 60;
- $gapName = "'$minGap+'";
+ $gapName = "'$minGap'";
$select[] = "sum(case when visit_total_time > $minGap then 1 else 0 end) as $gapName ";
}
}
diff --git a/plugins/VisitorInterest/API.php b/plugins/VisitorInterest/API.php
new file mode 100644
index 0000000000..3eee441bfb
--- /dev/null
+++ b/plugins/VisitorInterest/API.php
@@ -0,0 +1,84 @@
+<?php
+
+class Piwik_VisitorInterest_API extends Piwik_Apiable
+{
+ static private $instance = null;
+ protected function __construct()
+ {
+ parent::__construct();
+ }
+
+ static public function getInstance()
+ {
+ if (self::$instance == null)
+ {
+ $c = __CLASS__;
+ self::$instance = new $c();
+ }
+ return self::$instance;
+ }
+
+
+ public function getNumberOfVisitsPerVisitDuration( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable('VisitorInterest_timeGap');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getDurationLabel'));
+
+ return $dataTable;
+ }
+
+
+ public function getNumberOfVisitsPerPage( $idSite, $period, $date )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $date, $period );
+ $dataTable = $archive->getDataTable('VisitorInterest_pageGap');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
+ $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getPageGapLabel'));
+
+ return $dataTable;
+ }
+}
+
+function Piwik_getDurationLabel($label)
+{
+ if(($pos = strpos($label,'-')) !== false)
+ {
+ $min = substr($label, 0, $pos);
+ $max = substr($label, $pos+1);
+
+ if($min == 0 || $min == 30)
+ {
+ return $min.'-'.$max.'s';
+ }
+ else
+ {
+ $min = $min / 60;
+ $max = $max / 60;
+ return $min.'-'.$max.' min';
+ }
+ }
+ else
+ {
+ $time = intval($label) / 60;
+ return '+'.$time.' min';
+ }
+}
+
+function Piwik_getPageGapLabel($label)
+{
+ if(($pos = strpos($label,'-')) !== false)
+ {
+ $min = substr($label, 0, $pos);
+ $max = substr($label, $pos+1);
+
+ if($min == $max)
+ {
+ return $min;
+ }
+ }
+ return $label;
+}
diff --git a/tests/modules/DataTable.test.php b/tests/modules/DataTable.test.php
index e99b732cc4..086205804d 100644
--- a/tests/modules/DataTable.test.php
+++ b/tests/modules/DataTable.test.php
@@ -511,6 +511,57 @@ class Test_Piwik_DataTable extends UnitTestCase
}
/**
+ * Test to sort by label queing the filter
+ */
+ function test_filter_Queue_SortString()
+ {
+
+ $idcol = Piwik_DataTable_Row::COLUMNS;
+
+ $table = new Piwik_DataTable;
+ $rows = array(
+ array( $idcol => array('label'=>'google')),//0
+ array( $idcol => array('label'=>'tsk')),//1
+ array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))')),//2
+ );
+ $table->loadFromArray( $rows );
+
+ $expectedtable = new Piwik_DataTable;
+ $rows = array(
+ array( $idcol => array('label'=>'google')),//0
+ array( $idcol => array('label'=>'Q*(%&*("$&%*(&"$*")"))')),//2
+ array( $idcol => array('label'=>'tsk')),//1
+ );
+ $expectedtable->loadFromArray( $rows );
+
+ $expectedtableReverse = new Piwik_DataTable;
+ $expectedtableReverse->loadFromArray(array_reverse($rows));
+
+ $tableCopy = clone $table;
+ $this->assertTrue(Piwik_DataTable::isEqual($tableCopy, $table));
+
+ // queue the filter and check the table didnt change
+ $table->queueFilter("Piwik_DataTable_Filter_Sort", array('label', 'asc'));
+ $this->assertTrue(Piwik_DataTable::isEqual($tableCopy, $table));
+
+ // apply filter and check the table is sorted
+ $table->applyQueuedFilters();
+ $this->assertTrue(Piwik_DataTable::isEqual($expectedtable, $table));
+
+ // apply one more filter check it hasnt changed
+ $table->queueFilter("Piwik_DataTable_Filter_Sort", array('label', 'desc'));
+ $this->assertTrue(Piwik_DataTable::isEqual($expectedtable, $table));
+
+ // now apply the second sort and check it is correctly sorted
+ $table->applyQueuedFilters();
+ $this->assertTrue(Piwik_DataTable::isEqual($expectedtableReverse, $table));
+
+ // do one more time to make sure it doesnt change
+ $table->applyQueuedFilters();
+ $this->assertTrue(Piwik_DataTable::isEqual($expectedtableReverse, $table));
+ }
+
+ /**
* Test to sort by visit
*/
function test_filter_SortNumeric()