3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
33 class CRM_Utils_VersionCheck
{
35 PINGBACK_URL
= 'http://latest.civicrm.org/stable.php?format=json',
36 // relative to $civicrm_root
37 LOCALFILE_NAME
= '[civicrm.root]/civicrm-version.php',
38 // relative to $config->uploadDir
39 CACHEFILE_NAME
= '[civicrm.files]/persist/version-info-cache.json';
42 * The version of the current (local) installation
46 public $localVersion = NULL;
49 * The major version (branch name) of the local version
53 public $localMajorVersion;
56 * Info about available versions
60 public $versionInfo = array();
63 public $isInfoAvailable;
70 protected $stats = array();
82 public function __construct() {
83 // Populate local version
84 $localFile = Civi
::paths()->getPath(self
::LOCALFILE_NAME
);
85 if (file_exists($localFile)) {
86 require_once $localFile;
88 if (function_exists('civicrmVersion')) {
89 $info = civicrmVersion();
90 $this->localVersion
= trim($info['version']);
91 $this->localMajorVersion
= $this->getMajorVersion($this->localVersion
);
94 // Populate remote $versionInfo from cache file
95 $this->cacheFile
= Civi
::paths()->getPath(self
::CACHEFILE_NAME
);
96 $this->isInfoAvailable
= $this->readCacheFile();
100 * Finds the release info for a minor version.
101 * @param string $version
104 public function getReleaseInfo($version) {
105 $majorVersion = $this->getMajorVersion($version);
106 if (isset($this->versionInfo
[$majorVersion])) {
107 foreach ($this->versionInfo
[$majorVersion]['releases'] as $info) {
108 if ($info['version'] == $version) {
117 * @param $minorVersion
120 public function getMajorVersion($minorVersion) {
121 if (!$minorVersion) {
124 list($a, $b) = explode('.', $minorVersion);
130 * Get the latest version number if it's newer than the local one
133 * Returns version number of the latest release if it is greater than the local version,
134 * along with the type of upgrade (regular/security) needed and the status of the major
137 public function isNewerVersionAvailable() {
144 if ($this->versionInfo
&& $this->localVersion
) {
145 if (isset($this->versionInfo
[$this->localMajorVersion
])) {
146 switch (CRM_Utils_Array
::value('status', $this->versionInfo
[$this->localMajorVersion
])) {
150 // look for latest version in this major version
151 $releases = $this->checkBranchForNewVersion($this->versionInfo
[$this->localMajorVersion
]);
152 if ($releases['newest']) {
153 $return['version'] = $releases['newest'];
155 // check for intervening security releases
156 $return['upgrade'] = ($releases['security']) ?
'security' : 'regular';
162 // look for latest version ever
163 foreach ($this->versionInfo
as $majorVersionNumber => $majorVersion) {
164 if ($majorVersionNumber < $this->localMajorVersion ||
$majorVersion['status'] == 'testing') {
167 $releases = $this->checkBranchForNewVersion($this->versionInfo
[$majorVersionNumber]);
169 if ($releases['newest']) {
170 $return['version'] = $releases['newest'];
172 // check for intervening security releases
173 $return['upgrade'] = ($releases['security'] ||
$return['upgrade'] == 'security') ?
'security' : 'regular';
177 $return['status'] = $this->versionInfo
[$this->localMajorVersion
]['status'];
180 // Figure if the version is really old or really new
183 foreach ($this->versionInfo
as $majorVersionNumber => $majorVersion) {
184 $wayOld = ($this->localMajorVersion
< $majorVersionNumber);
188 $releases = $this->checkBranchForNewVersion($majorVersion);
191 'version' => $releases['newest'],
192 'upgrade' => 'security',
203 * Called by version_check cron job
205 public function fetch() {
206 $this->getSiteStats();
211 * @param $majorVersion
212 * @return null|string
214 private function checkBranchForNewVersion($majorVersion) {
215 $newerVersion = array(
219 if (!empty($majorVersion['releases'])) {
220 foreach ($majorVersion['releases'] as $release) {
221 if (version_compare($this->localVersion
, $release['version']) < 0) {
222 $newerVersion['newest'] = $release['version'];
223 if (CRM_Utils_Array
::value('security', $release)) {
224 $newerVersion['security'] = $release['version'];
229 return $newerVersion;
233 * Collect info about the site to be sent as pingback data.
235 private function getSiteStats() {
236 $config = CRM_Core_Config
::singleton();
237 $siteKey = md5(defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY
: '');
239 // Calorie-free pingback for alphas
240 $this->stats
= array('version' => $this->localVersion
);
242 // Non-alpha versions get the full treatment
243 if ($this->localVersion
&& !strpos($this->localVersion
, 'alpha')) {
244 $this->stats +
= array(
245 'hash' => md5($siteKey . $config->userFrameworkBaseURL
),
246 'uf' => $config->userFramework
,
247 'lang' => $config->lcMessages
,
248 'co' => $config->defaultContactCountry
,
249 'ufv' => $config->userSystem
->getVersion(),
250 'PHP' => phpversion(),
251 'MySQL' => CRM_CORE_DAO
::singleValueQuery('SELECT VERSION()'),
252 'communityMessagesUrl' => Civi
::settings()->get('communityMessagesUrl'),
254 $this->getPayProcStats();
255 $this->getEntityStats();
256 $this->getExtensionStats();
261 * Get active payment processor types.
263 private function getPayProcStats() {
264 $dao = new CRM_Financial_DAO_PaymentProcessor();
269 // Get title and id for all processor types
270 $ppTypeNames = CRM_Core_PseudoConstant
::paymentProcessorType();
272 while ($dao->fetch()) {
273 $ppTypes[] = $ppTypeNames[$dao->payment_processor_type_id
];
275 // add the .-separated list of the processor types
276 $this->stats
['PPTypes'] = implode(',', array_unique($ppTypes));
280 * Fetch counts from entity tables.
281 * Add info to the 'entities' array
283 private function getEntityStats() {
285 'CRM_Activity_DAO_Activity' => 'is_test = 0',
286 'CRM_Case_DAO_Case' => 'is_deleted = 0',
287 'CRM_Contact_DAO_Contact' => 'is_deleted = 0',
288 'CRM_Contact_DAO_Relationship' => NULL,
289 'CRM_Campaign_DAO_Campaign' => NULL,
290 'CRM_Contribute_DAO_Contribution' => 'is_test = 0',
291 'CRM_Contribute_DAO_ContributionPage' => 'is_active = 1',
292 'CRM_Contribute_DAO_ContributionProduct' => NULL,
293 'CRM_Contribute_DAO_Widget' => 'is_active = 1',
294 'CRM_Core_DAO_Discount' => NULL,
295 'CRM_Price_DAO_PriceSetEntity' => NULL,
296 'CRM_Core_DAO_UFGroup' => 'is_active = 1',
297 'CRM_Event_DAO_Event' => 'is_active = 1',
298 'CRM_Event_DAO_Participant' => 'is_test = 0',
299 'CRM_Friend_DAO_Friend' => 'is_active = 1',
300 'CRM_Grant_DAO_Grant' => NULL,
301 'CRM_Mailing_DAO_Mailing' => 'is_completed = 1',
302 'CRM_Member_DAO_Membership' => 'is_test = 0',
303 'CRM_Member_DAO_MembershipBlock' => 'is_active = 1',
304 'CRM_Pledge_DAO_Pledge' => 'is_test = 0',
305 'CRM_Pledge_DAO_PledgeBlock' => NULL,
307 foreach ($tables as $daoName => $where) {
308 $dao = new $daoName();
310 $dao->whereAdd($where);
312 $short_name = substr($daoName, strrpos($daoName, '_') +
1);
313 $this->stats
['entities'][] = array(
314 'name' => $short_name,
315 'size' => $dao->count(),
321 * Fetch stats about enabled components/extensions
322 * Add info to the 'extensions' array
324 private function getExtensionStats() {
326 $config = CRM_Core_Config
::singleton();
327 foreach ($config->enableComponents
as $comp) {
328 $this->stats
['extensions'][] = array(
329 'name' => 'org.civicrm.component.' . strtolower($comp),
331 'version' => $this->stats
['version'],
334 // Contrib extensions
335 $mapper = CRM_Extension_System
::singleton()->getMapper();
336 $dao = new CRM_Core_DAO_Extension();
338 while ($dao->fetch()) {
339 $info = $mapper->keyToInfo($dao->full_name
);
340 $this->stats
['extensions'][] = array(
341 'name' => $dao->full_name
,
342 'enabled' => $dao->is_active
,
343 'version' => isset($info->version
) ?
$info->version
: NULL,
349 * Send the request to civicrm.org
350 * Store results in the cache file
352 private function pingBack() {
356 'header' => 'Content-type: application/x-www-form-urlencoded',
357 'content' => http_build_query($this->stats
),
360 $ctx = stream_context_create($params);
361 $rawJson = file_get_contents(self
::PINGBACK_URL
, FALSE, $ctx);
362 $versionInfo = $rawJson ?
json_decode($rawJson, TRUE) : NULL;
363 // If we couldn't fetch or parse the data $versionInfo will be NULL
364 // Otherwise it will be an array and we'll cache it.
365 // Note the array may be empty e.g. in the case of a pre-alpha with no releases
366 if ($versionInfo !== NULL) {
367 $this->writeCacheFile($rawJson);
368 $this->versionInfo
= $versionInfo;
375 private function readCacheFile() {
376 if (file_exists($this->cacheFile
)) {
377 $this->versionInfo
= (array) json_decode(file_get_contents($this->cacheFile
), TRUE);
378 // Sort version info in ascending order for easier comparisons
379 ksort($this->versionInfo
, SORT_NUMERIC
);
386 * Save version info to file.
387 * @param string $contents
389 private function writeCacheFile($contents) {
390 $fp = fopen($this->cacheFile
, 'w');
391 fwrite($fp, $contents);