diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php
index e226df074dfeb..1bce88ab71674 100644
--- a/administrator/components/com_admin/script.php
+++ b/administrator/components/com_admin/script.php
@@ -36,6 +36,63 @@ public function update($installer)
$this->updateDatabase();
$this->clearRadCache();
$this->updateAssets();
+ $this->clearStatsCache();
+ }
+
+ /**
+ * Method to clear our stats plugin cache to ensure we get fresh data on Joomla Update
+ *
+ * @return void
+ * @since 3.5
+ */
+ protected function clearStatsCache()
+ {
+ $db = JFactory::getDbo();
+
+ try
+ {
+ // Get the params for the stats plugin
+ $params = $db->setQuery(
+ $db->getQuery(true)
+ ->select($db->quoteName('params'))
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
+ ->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote('stats'))
+ )->loadResult();
+ }
+ catch (Exception $e)
+ {
+ echo JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '
';
+
+ return;
+ }
+
+ $params = json_decode($params, true);
+
+ // Reset the last run parameter
+ if (isset($params['lastrun']))
+ {
+ $params['lastrun'] = '';
+ }
+
+ $query = $db->getQuery(true)
+ ->update($db->quoteName('#__extensions'))
+ ->set($db->quoteName('params') . ' = ' . $db->quote($params))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
+ ->where($db->quoteName('folder') . ' = ' . $db->quote('system'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote('stats'));
+
+ try
+ {
+ $db->setQuery($query)->execute();
+ }
+ catch (Exception $e)
+ {
+ echo JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '
';
+
+ return;
+ }
}
/**
diff --git a/administrator/components/com_admin/sql/updates/mysql/3.5.0-2015-11-05.sql b/administrator/components/com_admin/sql/updates/mysql/3.5.0-2015-11-05.sql
new file mode 100644
index 0000000000000..f9e57739917e1
--- /dev/null
+++ b/administrator/components/com_admin/sql/updates/mysql/3.5.0-2015-11-05.sql
@@ -0,0 +1,2 @@
+INSERT INTO `#__extensions` (`extension_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `manifest_cache`, `params`, `custom_data`, `system_data`, `checked_out`, `checked_out_time`, `ordering`, `state`) VALUES
+(454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0);
diff --git a/administrator/components/com_admin/sql/updates/postgresql/3.5.0-2015-11-05.sql b/administrator/components/com_admin/sql/updates/postgresql/3.5.0-2015-11-05.sql
new file mode 100644
index 0000000000000..b13c35987b576
--- /dev/null
+++ b/administrator/components/com_admin/sql/updates/postgresql/3.5.0-2015-11-05.sql
@@ -0,0 +1,2 @@
+INSERT INTO "#__extensions" ("extension_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "system_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
+(454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0);
diff --git a/administrator/components/com_admin/sql/updates/sqlazure/3.5.0-2015-11-05.sql b/administrator/components/com_admin/sql/updates/sqlazure/3.5.0-2015-11-05.sql
new file mode 100644
index 0000000000000..6e44e38b360a2
--- /dev/null
+++ b/administrator/components/com_admin/sql/updates/sqlazure/3.5.0-2015-11-05.sql
@@ -0,0 +1,6 @@
+SET IDENTITY_INSERT [#__extensions] ON;
+
+INSERT [#__extensions] ([extension_id], [name], [type], [element], [folder], [client_id], [enabled], [access], [protected], [manifest_cache], [params], [custom_data], [system_data], [checked_out], [checked_out_time], [ordering], [state])
+SELECT 454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0;
+
+SET IDENTITY_INSERT [#__extensions] OFF;
diff --git a/administrator/language/en-GB/en-GB.plg_system_stats.ini b/administrator/language/en-GB/en-GB.plg_system_stats.ini
new file mode 100644
index 0000000000000..2bae1f168e753
--- /dev/null
+++ b/administrator/language/en-GB/en-GB.plg_system_stats.ini
@@ -0,0 +1,12 @@
+; Joomla! Project
+; Copyright (C) 2005 - 2015 Open Source Matters. All rights reserved.
+; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
+; Note : All ini files need to be saved as UTF-8
+
+PLG_SYSTEM_STATS="System - Joomla! Statistics"
+PLG_STATS_XML_DESCRIPTION="System Plugin that sends environment statistics to a server controlled by the Joomla! project for statistical analysis. Statistics sent include PHP version, CMS version, Database type and Database version."
+PLG_STATS_UNIQUE_ID_DESC="An identifier that allows us to count unique installs of the plugin. This is sent with the statistics back to the server."
+PLG_STATS_UNIQUE_ID_LABEL="Unique ID"
+PLG_STATS_URL_DESC="The official Joomla server url"
+PLG_STATS_URL_LABEL="Url"
+PLG_STATS_RESET_UNIQUE_ID="Reset Unique Id"
\ No newline at end of file
diff --git a/administrator/language/en-GB/en-GB.plg_system_stats.sys.ini b/administrator/language/en-GB/en-GB.plg_system_stats.sys.ini
new file mode 100644
index 0000000000000..ec3ad4ef5b78c
--- /dev/null
+++ b/administrator/language/en-GB/en-GB.plg_system_stats.sys.ini
@@ -0,0 +1,9 @@
+; Joomla! Project
+; Copyright (C) 2005 - 2015 Open Source Matters. All rights reserved.
+; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
+; Note : All ini files need to be saved as UTF-8
+
+PLG_SYSTEM_STATS="System - Joomla! Statistics"
+PLG_STATS_XML_DESCRIPTION="System Plugin that sends environment statistics to a server controlled by the Joomla! project for statistical analysis. Statistics sent include PHP version, CMS version, Database type and Database version."
+PLG_STATS_UNIQUE_ID_DESC="An identifier that allows us to count unique installs of the plugin. This is sent with the statistics back to the server."
+PLG_STATS_UNIQUE_ID_LABEL="Unique ID"
diff --git a/installation/sql/mysql/joomla.sql b/installation/sql/mysql/joomla.sql
index c4664b3393053..cf3c9f1a732f4 100644
--- a/installation/sql/mysql/joomla.sql
+++ b/installation/sql/mysql/joomla.sql
@@ -610,6 +610,7 @@ INSERT INTO `#__extensions` (`extension_id`, `name`, `type`, `element`, `folder`
(451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"search_limit":"50","show_tagged_items":"1"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(452, 'plg_system_updatenotification', 'plugin', 'updatenotification', 'system', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(453, 'plg_editors-xtd_module', 'plugin', 'module', 'editors-xtd', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
+(454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(503, 'beez3', 'template', 'beez3', '', 0, 1, 1, 0, '', '{"wrapperSmall":"53","wrapperLarge":"72","sitetitle":"","sitedescription":"","navposition":"center","templatecolor":"nature"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(504, 'hathor', 'template', 'hathor', '', 1, 1, 1, 0, '', '{"showSiteName":"0","colourChoice":"0","boldText":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
(506, 'protostar', 'template', 'protostar', '', 0, 1, 1, 0, '', '{"templateColor":"","logoFile":"","googleFont":"1","googleFontName":"Open+Sans","fluidContainer":"0"}', '', '', 0, '0000-00-00 00:00:00', 0, 0),
diff --git a/installation/sql/postgresql/joomla.sql b/installation/sql/postgresql/joomla.sql
index eb9070b023e81..33b70686c48f0 100644
--- a/installation/sql/postgresql/joomla.sql
+++ b/installation/sql/postgresql/joomla.sql
@@ -609,7 +609,8 @@ INSERT INTO "#__extensions" ("extension_id", "name", "type", "element", "folder"
(450, 'plg_twofactorauth_yubikey', 'plugin', 'yubikey', 'twofactorauth', 0, 0, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"search_limit":"50","show_tagged_items":"1"}', '', '', 0, '1970-01-01 00:00:00', 0, 0),
(452, 'plg_system_updatenotification', 'plugin', 'updatenotification', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
-(453, 'plg_editors-xtd_module', 'plugin', 'module', 'editors-xtd', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0);
+(453, 'plg_editors-xtd_module', 'plugin', 'module', 'editors-xtd', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0),
+(454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1970-01-01 00:00:00', 0, 0);
-- Templates
INSERT INTO "#__extensions" ("extension_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "manifest_cache", "params", "custom_data", "system_data", "checked_out", "checked_out_time", "ordering", "state") VALUES
diff --git a/installation/sql/sqlazure/joomla.sql b/installation/sql/sqlazure/joomla.sql
index bd0b5ca4bdf13..a8ae09f76dae8 100644
--- a/installation/sql/sqlazure/joomla.sql
+++ b/installation/sql/sqlazure/joomla.sql
@@ -999,7 +999,9 @@ SELECT 451, 'plg_search_tags', 'plugin', 'tags', 'search', 0, 1, 1, 0, '', '{"se
UNION ALL
SELECT 452, 'plg_system_updatenotification', 'plugin', 'updatenotification', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0
UNION ALL
-SELECT 453, 'plg_editors-xtd_module', 'plugin', 'module', 'editors-xtd', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0;
+SELECT 453, 'plg_editors-xtd_module', 'plugin', 'module', 'editors-xtd', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0
+UNION ALL
+SELECT 454, 'plg_system_stats', 'plugin', 'stats', 'system', 0, 1, 1, 0, '', '', '', '', 0, '1900-01-01 00:00:00', 0, 0;
INSERT [#__extensions] ([extension_id], [name], [type], [element], [folder], [client_id], [enabled], [access], [protected], [manifest_cache], [params], [custom_data], [system_data], [checked_out], [checked_out_time], [ordering], [state])
diff --git a/plugins/system/stats/fields/uniqueid.php b/plugins/system/stats/fields/uniqueid.php
new file mode 100644
index 0000000000000..7238ee281b0ba
--- /dev/null
+++ b/plugins/system/stats/fields/uniqueid.php
@@ -0,0 +1,42 @@
+id . '\').value=\'\';Joomla.submitbutton(\'plugin.apply\');"';
+
+ return ' '
+ . ' ' . JText::_('PLG_STATS_RESET_UNIQUE_ID') . '';
+ }
+}
diff --git a/plugins/system/stats/stats.php b/plugins/system/stats/stats.php
new file mode 100644
index 0000000000000..39ccb1a3334a4
--- /dev/null
+++ b/plugins/system/stats/stats.php
@@ -0,0 +1,197 @@
+app->isAdmin())
+ {
+ return;
+ }
+
+ // Do we need to run? Compare the last run timestamp stored in the plugin's options with the current
+ // timestamp. If the difference is greater than the cache timeout we shall not execute again.
+ $now = time();
+ $last = (int) $this->params->get('lastrun', 0);
+
+ // 12 hours - 60*60*12 = 43200
+ if (!defined('PLG_SYSTEM_STATS_DEBUG') && (abs($now - $last) < 43200))
+ {
+ return;
+ }
+
+ // Update last run status
+ $this->params->set('lastrun', $now);
+
+ $uniqueId = $this->params->get('unique_id', '');
+
+ /*
+ * If the unique ID is empty (because we have never submitted a piece of data before or because the refresh button
+ * has been used - generate a new ID and store it in the database for future use.
+ */
+ if (empty($uniqueId))
+ {
+ $uniqueId = hash('sha1', JUserHelper::genRandomPassword(28) . time());
+ $this->params->set('unique_id', $uniqueId);
+ }
+
+ $query = $this->db->getQuery(true)
+ ->update($this->db->quoteName('#__extensions'))
+ ->set($this->db->quoteName('params') . ' = ' . $this->db->quote($this->params->toString('JSON')))
+ ->where($this->db->quoteName('type') . ' = ' . $this->db->quote('plugin'))
+ ->where($this->db->quoteName('folder') . ' = ' . $this->db->quote('system'))
+ ->where($this->db->quoteName('element') . ' = ' . $this->db->quote('stats'));
+
+ try
+ {
+ // Lock the tables to prevent multiple plugin executions causing a race condition
+ $this->db->lockTable('#__extensions');
+ }
+ catch (Exception $e)
+ {
+ // If we can't lock the tables it's too risky to continue execution
+ return;
+ }
+
+ try
+ {
+ // Update the plugin parameters
+ $result = $this->db->setQuery($query)->execute();
+
+ $this->clearCacheGroups(array('com_plugins'), array(0, 1));
+ }
+ catch (Exception $exc)
+ {
+ // If we failed to execute
+ $this->db->unlockTables();
+ $result = false;
+ }
+
+ try
+ {
+ // Unlock the tables after writing
+ $this->db->unlockTables();
+ }
+ catch (Exception $e)
+ {
+ // If we can't lock the tables assume we have somehow failed
+ $result = false;
+ }
+
+ // Abort on failure
+ if (!$result)
+ {
+ return;
+ }
+
+ $data = array(
+ 'unique_id' => $uniqueId,
+ 'php_version' => PHP_VERSION,
+ 'db_type' => $this->db->name,
+ 'db_version' => $this->db->getVersion(),
+ 'cms_version' => JVERSION,
+ 'server_os' => php_uname('s') . ' ' . php_uname('r')
+ );
+
+ // Don't let the request take longer than 2 seconds to avoid page timeout issues
+ try
+ {
+ // Don't let the request take longer than 2 seconds to avoid page timeout issues
+ JHttpFactory::getHttp()->post($this->params->get('url', 'https://developer.joomla.org/stats/submit'), $data, null, 2);
+ }
+ catch (UnexpectedValueException $e)
+ {
+ // There was an error sending stats. Should we do anything?
+ JLog::add('Could not send site statistics to remote server: ' . $e->getMessage(), JLog::WARNING, 'stats');
+ }
+ catch (RuntimeException $e)
+ {
+ // There was an error connecting to the server or in the post request
+ JLog::add('Could not connect to statistics server: ' . $e->getMessage(), JLog::WARNING, 'stats');
+ }
+ catch (Exception $e)
+ {
+ // An unexpected error in processing; don't let this failure kill the site
+ JLog::add('Unexpected error connecting to statistics server: ' . $e->getMessage(), JLog::WARNING, 'stats');
+ }
+ }
+
+ /**
+ * Clears cache groups. We use it to clear the plugins cache after we update the last run timestamp.
+ *
+ * @param array $clearGroups The cache groups to clean
+ * @param array $cacheClients The cache clients (site, admin) to clean
+ *
+ * @return void
+ *
+ * @since 3.5
+ */
+ private function clearCacheGroups(array $clearGroups, array $cacheClients = array(0, 1))
+ {
+ $conf = JFactory::getConfig();
+
+ foreach ($clearGroups as $group)
+ {
+ foreach ($cacheClients as $client_id)
+ {
+ try
+ {
+ $options = array(
+ 'defaultgroup' => $group,
+ 'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR . '/cache' :
+ $conf->get('cache_path', JPATH_SITE . '/cache')
+ );
+
+ $cache = JCache::getInstance('callback', $options);
+ $cache->clean();
+ }
+ catch (Exception $e)
+ {
+ // Ignore it
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/system/stats/stats.xml b/plugins/system/stats/stats.xml
new file mode 100644
index 0000000000000..48aba4177329a
--- /dev/null
+++ b/plugins/system/stats/stats.xml
@@ -0,0 +1,48 @@
+
+
+ plg_system_stats
+ Joomla! Project
+ November 2013
+ Copyright (C) 2005 - 2015 Open Source Matters. All rights reserved.
+ GNU General Public License version 2 or later; see LICENSE.txt
+ admin@joomla.org
+ www.joomla.org
+ 3.5.0
+ PLG_STATS_XML_DESCRIPTION
+
+ stats.php
+ fields
+
+
+ en-GB/en-GB.plg_system_stats.ini
+ en-GB/en-GB.plg_system_stats.sys.ini
+
+
+
+
+
+
+