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 + + + +
+ + + +
+
+
+