Merge pull request #3979 from eileenmcnaughton/CRM-15196
[civicrm-core.git] / CRM / Utils / VersionCheck.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
232624b1 4 | CiviCRM version 4.4 |
6a488035
TO
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2013
32 * $Id: $
33 *
34 */
35class CRM_Utils_VersionCheck {
36 CONST
37 LATEST_VERSION_AT = 'http://latest.civicrm.org/stable.php',
38 // timeout for when the connection or the server is slow
39 CHECK_TIMEOUT = 5,
40 // relative to $civicrm_root
41 LOCALFILE_NAME = 'civicrm-version.php',
42 // relative to $config->uploadDir
43 CACHEFILE_NAME = 'latest-version-cache.txt',
42f366d7
CW
44 // cachefile expiry time (in seconds) - one day
45 CACHEFILE_EXPIRE = 86400;
6a488035
TO
46
47 /**
48 * We only need one instance of this object, so we use the
49 * singleton pattern and cache the instance in this variable
50 *
51 * @var object
52 * @static
53 */
54 static private $_singleton = NULL;
55
56 /**
57 * The version of the current (local) installation
58 *
59 * @var string
60 */
61 public $localVersion = NULL;
62
63 /**
64 * The latest version of CiviCRM
65 *
66 * @var string
67 */
68 public $latestVersion = NULL;
69
70 /**
71 * Pingback params
72 *
73 * @var string
74 */
75 protected $stats = array();
76
77 /**
78 * Class constructor
79 *
80 * @access private
81 */
82 function __construct() {
83 global $civicrm_root;
84 $config = CRM_Core_Config::singleton();
85
86 $localfile = $civicrm_root . DIRECTORY_SEPARATOR . self::LOCALFILE_NAME;
87 $cachefile = $config->uploadDir . self::CACHEFILE_NAME;
88
89 if (file_exists($localfile)) {
90 require_once ($localfile);
91 if (function_exists('civicrmVersion')) {
92 $info = civicrmVersion();
8edbc7e6 93 $this->localVersion = trim($info['version']);
6a488035
TO
94 }
95 }
96 if ($config->versionCheck) {
97 $expiryTime = time() - self::CACHEFILE_EXPIRE;
98
99 // if there's a cachefile and it's not stale use it to
100 // read the latestVersion, else read it from the Internet
101 if (file_exists($cachefile) && (filemtime($cachefile) > $expiryTime)) {
8edbc7e6 102 $this->latestVersion = trim(file_get_contents($cachefile));
6a488035
TO
103 }
104 else {
105 $siteKey = md5(defined('CIVICRM_SITE_KEY') ? CIVICRM_SITE_KEY : '');
106
107 $this->stats = array(
108 'hash' => md5($siteKey . $config->userFrameworkBaseURL),
109 'version' => $this->localVersion,
110 'uf' => $config->userFramework,
111 'lang' => $config->lcMessages,
112 'co' => $config->defaultContactCountry,
113 'ufv' => $config->userFrameworkVersion,
114 'PHP' => phpversion(),
115 'MySQL' => CRM_CORE_DAO::singleValueQuery('SELECT VERSION()'),
187e57d9 116 'communityMessagesUrl' => CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'communityMessagesUrl', NULL, '*default*'),
6a488035
TO
117 );
118
119 // Add usage stats
120 $this->payProcStats();
121 $this->entityStats();
122 $this->extensionStats();
123
124 // Get the latest version and send site info
125 $this->pingBack();
126
127 // Update cache file
128 if ($this->latestVersion) {
129 $fp = @fopen($cachefile, 'w');
130 if (!$fp) {
131 if (CRM_Core_Permission::check('administer CiviCRM')) {
132 CRM_Core_Session::setStatus(
c7692a87 133 ts('Unable to write file') . ":$cachefile<br />" . ts('Please check your system file permissions.'),
6a488035
TO
134 ts('File Error'), 'error');
135 }
136 return;
137 }
138 fwrite($fp, $this->latestVersion);
139 fclose($fp);
140 }
141 }
142 }
143 }
144
145 /**
146 * Static instance provider
147 *
148 * Method providing static instance of CRM_Utils_VersionCheck,
149 * as in Singleton pattern
150 *
151 * @return CRM_Utils_VersionCheck
152 */
153 static function &singleton() {
154 if (!isset(self::$_singleton)) {
155 self::$_singleton = new CRM_Utils_VersionCheck();
156 }
157 return self::$_singleton;
158 }
159
160 /**
161 * Get the latest version number if it's newer than the local one
162 *
163 * @return string|null
2be1e4fc 164 * Returns the newer version's number, or null if the versions are equal
6a488035
TO
165 */
166 public function newerVersion() {
167 if ($this->latestVersion) {
09cdd1cd 168 if ((version_compare($this->localVersion, $this->latestVersion) < 0)
169 && CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'versionAlert', NULL, TRUE)) {
2be1e4fc 170 return $this->latestVersion;
6a488035
TO
171 }
172 }
173 return NULL;
174 }
175
176 /**
177 * Alert the site admin of new versions of CiviCRM
178 * Show the message once a day
179 */
180 public function versionAlert() {
b7aba061
CW
181 if (CRM_Core_Permission::check('administer CiviCRM') && $this->newerVersion()
182 && CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'versionAlert', NULL, TRUE)) {
6a488035
TO
183 $session = CRM_Core_Session::singleton();
184 if ($session->timer('version_alert', 24 * 60 * 60)) {
185 $msg = ts('A newer version of CiviCRM is available: %1', array(1 => $this->latestVersion))
186 . '<br />' . ts('<a href="%1">Download Now</a>', array(1 => 'http://civicrm.org/download'));
187 $session->setStatus($msg, ts('Update Available'));
188 }
189 }
190 }
191
192 /**
193 * Get active payment processor types
194 */
195 private function payProcStats() {
196 $dao = new CRM_Financial_DAO_PaymentProcessor;
197 $dao->is_active = 1;
198 $dao->find();
199 $ppTypes = array();
200
201 // Get title and id for all processor types
202 $ppTypeNames = CRM_Core_PseudoConstant::paymentProcessorType();
203
204 while ($dao->fetch()) {
205 $ppTypes[] = $ppTypeNames[$dao->payment_processor_type_id];
206 }
207 // add the .-separated list of the processor types
208 $this->stats['PPTypes'] = implode(',', array_unique($ppTypes));
209
210 }
211
212 /**
213 * Fetch counts from entity tables
214 * Add info to the 'entities' array
215 */
216 private function entityStats() {
217 $tables = array(
218 'CRM_Activity_DAO_Activity' => 'is_test = 0',
219 'CRM_Case_DAO_Case' => 'is_deleted = 0',
220 'CRM_Contact_DAO_Contact' => 'is_deleted = 0',
221 'CRM_Contact_DAO_Relationship' => NULL,
222 'CRM_Campaign_DAO_Campaign' => NULL,
223 'CRM_Contribute_DAO_Contribution' => 'is_test = 0',
224 'CRM_Contribute_DAO_ContributionPage' => 'is_active = 1',
225 'CRM_Contribute_DAO_ContributionProduct' => NULL,
226 'CRM_Contribute_DAO_Widget' => 'is_active = 1',
227 'CRM_Core_DAO_Discount' => NULL,
9da8dc8c 228 'CRM_Price_DAO_PriceSetEntity' => NULL,
6a488035
TO
229 'CRM_Core_DAO_UFGroup' => 'is_active = 1',
230 'CRM_Event_DAO_Event' => 'is_active = 1',
231 'CRM_Event_DAO_Participant' => 'is_test = 0',
232 'CRM_Friend_DAO_Friend' => 'is_active = 1',
233 'CRM_Grant_DAO_Grant' => NULL,
234 'CRM_Mailing_DAO_Mailing' => 'is_completed = 1',
235 'CRM_Member_DAO_Membership' => 'is_test = 0',
236 'CRM_Member_DAO_MembershipBlock' => 'is_active = 1',
237 'CRM_Pledge_DAO_Pledge' => 'is_test = 0',
238 'CRM_Pledge_DAO_PledgeBlock' => NULL,
239 );
240 foreach ($tables as $daoName => $where) {
241 $dao = new $daoName;
242 if ($where) {
243 $dao->whereAdd($where);
244 }
245 $short_name = substr($daoName, strrpos($daoName, '_') + 1);
246 $this->stats['entities'][] = array(
247 'name' => $short_name,
248 'size' => $dao->count(),
249 );
250 }
251 }
252
253 /**
254 * Fetch stats about enabled components/extensions
255 * Add info to the 'extensions' array
256 */
257 private function extensionStats() {
258 // Core components
259 $config = CRM_Core_Config::singleton();
260 foreach ($config->enableComponents as $comp) {
261 $this->stats['extensions'][] = array(
262 'name' => 'org.civicrm.component.' . strtolower($comp),
263 'enabled' => 1,
264 'version' => $this->stats['version'],
265 );
266 }
267 // Contrib extensions
268 $mapper = CRM_Extension_System::singleton()->getMapper();
269 $dao = new CRM_Core_DAO_Extension();
270 $dao->find();
271 while ($dao->fetch()) {
272 $info = $mapper->keyToInfo($dao->full_name);
273 $this->stats['extensions'][] = array(
274 'name' => $dao->full_name,
275 'enabled' => $dao->is_active,
276 'version' => isset($info->version) ? $info->version : NULL,
277 );
278 }
279 }
280
281 /**
282 * Send the request to civicrm.org
283 * Set timeout and suppress errors
284 */
285 private function pingBack() {
286 ini_set('default_socket_timeout', self::CHECK_TIMEOUT);
287 $params = array(
288 'http' => array(
289 'method' => 'POST',
290 'header' => 'Content-type: application/x-www-form-urlencoded',
291 'content' => http_build_query($this->stats),
292 ),
293 );
294 $ctx = stream_context_create($params);
295 $this->latestVersion = @file_get_contents(self::LATEST_VERSION_AT, FALSE, $ctx);
296 if (!preg_match('/^\d+\.\d+\.\d+$/', $this->latestVersion)) {
297 $this->latestVersion = NULL;
298 }
2be1e4fc
CW
299 else {
300 $this->latestVersion = trim($this->latestVersion);
301 }
6a488035
TO
302 ini_restore('default_socket_timeout');
303 }
304
305}