Merge pull request #4796 from jitendrapurohit/webtest4.6
[civicrm-core.git] / CRM / Extension / Mapper.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
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 * This class proivdes various helper functions for locating extensions
30 * data. It's designed for compatibility with pre-existing functions from
31 * CRM_Core_Extensions.
32 *
33 * Most of these helper functions originate with the first major iteration
34 * of extensions -- a time when every extension had one eponymous PHP class,
35 * when there was no PHP class-loader, and when there was special-case logic
36 * sprinkled around to handle loading of "extension classes".
37 *
38 * With module-extensions (Civi 4.2+), there are no eponymous classes --
39 * instead, module-extensions follow the same class-naming and class-loading
40 * practices as core (and don't require special-case logic for class
41 * loading). Consequently, the helpers in here aren't much used with
42 * module-extensions.
43 *
44 * @package CRM
06b69b18 45 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
46 * $Id$
47 *
48 */
49class CRM_Extension_Mapper {
50
51 /**
52 * An URL for public extensions repository
53 */
54 //CONST DEFAULT_EXTENSIONS_REPOSITORY = 'http://civicrm.org/extdir/ver={ver}|cms={uf}';
55
56 /**
57 * Extension info file name
58 */
59 const EXT_TEMPLATES_DIRNAME = 'templates';
60
61 /**
62 * @var CRM_Extension_Container_Interface
63 */
64 protected $container;
65
66 /**
67 * @var array (key => CRM_Extension_Info)
68 */
69 protected $infos = array();
70
71 /**
72 * @var array
73 */
74 protected $moduleExtensions = NULL;
75
76 /**
77 * @var CRM_Utils_Cache_Interface
78 */
79 protected $cache;
80
81 protected $cacheKey;
82
83 protected $civicrmPath;
84
85 protected $civicrmUrl;
86
e0ef6999
EM
87 /**
88 * @param CRM_Extension_Container_Interface $container
89 * @param CRM_Utils_Cache_Interface $cache
90 * @param null $cacheKey
91 * @param null $civicrmPath
92 * @param null $civicrmUrl
93 */
6a488035
TO
94 public function __construct(CRM_Extension_Container_Interface $container, CRM_Utils_Cache_Interface $cache = NULL, $cacheKey = NULL, $civicrmPath = NULL, $civicrmUrl = NULL) {
95 $this->container = $container;
96 $this->cache = $cache;
97 $this->cacheKey = $cacheKey;
98 if ($civicrmUrl) {
99 $this->civicrmUrl = rtrim($civicrmUrl, '/');
100 } else {
101 $config = CRM_Core_Config::singleton();
102 $this->civicrmUrl = rtrim($config->resourceBase, '/');
103 }
104 if ($civicrmPath) {
105 $this->civicrmPath = rtrim($civicrmPath,'/');
106 } else {
107 global $civicrm_root;
108 $this->civicrmPath = rtrim($civicrm_root,'/');
109 }
110 }
111
112 /**
113 * Given the class, provides extension's key.
114 *
115 * @access public
116 *
117 * @param string $clazz extension class name
118 *
119 * @return string name of extension key
120 */
121 public function classToKey($clazz) {
122 return str_replace('_', '.', $clazz);
123 }
124
125 /**
126 * Given the class, provides extension path.
127 *
128 * @access public
129 *
6c8f6e67
EM
130 * @param $clazz
131 *
6a488035
TO
132 * @return string full path the extension .php file
133 */
134 public function classToPath($clazz) {
135 $elements = explode('_', $clazz);
136 $key = implode('.', $elements);
137 return $this->keyToPath($key);
138 }
139
140 /**
141 * Given the string, returns true or false if it's an extension key.
142 *
143 * @access public
144 *
145 * @param string $key a string which might be an extension key
146 *
147 * @return boolean true if given string is an extension name
148 */
149 public function isExtensionKey($key) {
150 // check if the string is an extension name or the class
151 return (strpos($key, '.') !== FALSE) ? TRUE : FALSE;
152 }
153
154 /**
155 * Given the string, returns true or false if it's an extension class name.
156 *
157 * @access public
158 *
159 * @param string $clazz a string which might be an extension class name
160 *
161 * @return boolean true if given string is an extension class name
162 */
163 public function isExtensionClass($clazz) {
164
165 if (substr($clazz, 0, 4) != 'CRM_') {
166 return (bool) preg_match('/^[a-z0-9]+(_[a-z0-9]+)+$/', $clazz);
167 }
168 return FALSE;
169 }
170
171 /**
172 * @param string $key extension fully-qualified-name
77b97be7
EM
173 * @param bool $fresh
174 *
175 * @throws CRM_Extension_Exception
176 * @throws Exception
c490a46a 177 * @return CRM_Extension_Info
6a488035
TO
178 */
179 public function keyToInfo($key, $fresh = FALSE) {
180 if ($fresh || !array_key_exists($key, $this->infos)) {
181 try {
182 $this->infos[$key] = CRM_Extension_Info::loadFromFile($this->container->getPath($key) . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME);
183 } catch (CRM_Extension_Exception $e) {
184 // file has more detailed info, but we'll fallback to DB if it's missing -- DB has enough info to uninstall
185 $this->infos[$key] = CRM_Extension_System::singleton()->getManager()->createInfoFromDB($key);
186 if (!$this->infos[$key]) {
187 throw $e;
188 }
189 }
190 }
191 return $this->infos[$key];
192 }
193
194 /**
195 * Given the key, provides extension's class name.
196 *
197 * @access public
198 *
199 * @param string $key extension key
200 *
201 * @return string name of extension's main class
202 */
203 public function keyToClass($key) {
204 return str_replace('.', '_', $key);
205 }
206
207 /**
208 * Given the key, provides the path to file containing
209 * extension's main class.
210 *
211 * @access public
212 *
213 * @param string $key extension key
214 *
215 * @return string path to file containing extension's main class
216 */
217 public function keyToPath($key) {
218 $info = $this->keyToInfo($key);
219 return $this->container->getPath($key) . DIRECTORY_SEPARATOR . $info->file . '.php';
220 }
221
222 /**
223 * Given the key, provides the path to file containing
224 * extension's main class.
225 *
226 * @access public
227 * @param string $key extension key
228 * @return string local path of the extension source tree
229 */
230 public function keyToBasePath($key) {
231 if ($key == 'civicrm') {
232 return $this->civicrmPath;
233 }
234 return $this->container->getPath($key);
235 }
236
237 /**
238 * Given the key, provides the path to file containing
239 * extension's main class.
240 *
241 * @access public
242 *
243 * @param string $key extension key
244 *
245 * @return string url for resources in this extension
246 */
247 public function keyToUrl($key) {
248 if ($key == 'civicrm') {
dee7a2b1
PJ
249 // CRM-12130 Workaround: If the domain's config_backend is NULL at the start of the request,
250 // then the Mapper is wrongly constructed with an empty value for $this->civicrmUrl.
3d4a4ccf
PJ
251 if (empty($this->civicrmUrl)) {
252 $config = CRM_Core_Config::singleton();
253 return rtrim($config->resourceBase, '/');
254 }
6a488035
TO
255 return $this->civicrmUrl;
256 }
257
258 return $this->container->getResUrl($key);
259 }
260
261 /**
262 * Fetch the list of active extensions of type 'module'
263 *
264 * @param $fresh bool whether to forcibly reload extensions list from canonical store
265 * @return array - array(array('prefix' => $, 'file' => $))
266 */
267 public function getActiveModuleFiles($fresh = FALSE) {
268 $config = CRM_Core_Config::singleton();
269 if ($config->isUpgradeMode() || !defined('CIVICRM_DSN')) {
270 return array(); // hmm, ok
271 }
272
273 $moduleExtensions = NULL;
274 if ($this->cache && !$fresh) {
275 $moduleExtensions = $this->cache->get($this->cacheKey . '/moduleFiles');
276 }
277
278 if (!is_array($moduleExtensions)) {
279 // Check canonical module list
280 $moduleExtensions = array();
281 $sql = '
282 SELECT full_name, file
283 FROM civicrm_extension
284 WHERE is_active = 1
285 AND type = "module"
286 ';
287 $dao = CRM_Core_DAO::executeQuery($sql);
288 while ($dao->fetch()) {
289 try {
290 $moduleExtensions[] = array(
291 'prefix' => $dao->file,
292 'filePath' => $this->keyToPath($dao->full_name),
293 );
294 } catch (CRM_Extension_Exception $e) {
295 // Putting a stub here provides more consistency
296 // in how getActiveModuleFiles when racing between
297 // dirty file-removals and cache-clears.
298 CRM_Core_Session::setStatus($e->getMessage(), '', 'error');
299 $moduleExtensions[] = array(
300 'prefix' => $dao->file,
301 'filePath' => NULL,
302 );
303 }
304 }
305
306 if ($this->cache) {
307 $this->cache->set($this->cacheKey . '/moduleFiles', $moduleExtensions);
308 }
309 }
310 return $moduleExtensions;
311 }
312
e7ff7042
TO
313 /**
314 * Get a list of base URLs for all active modules
315 *
316 * @return array (string $extKey => string $baseUrl)
317 */
318 public function getActiveModuleUrls() {
319 // TODO optimization/caching
320 $urls = array();
321 $urls['civicrm'] = $this->keyToUrl('civicrm');
322 foreach ($this->getModules() as $module) {
323 /** @var $module CRM_Core_Module */
324 if ($module->is_active) {
325 $urls[$module->name] = $this->keyToUrl($module->name);
326 }
327 }
328 return $urls;
329 }
330
e0ef6999 331 /**
100fef9d 332 * @param string $name
e0ef6999
EM
333 *
334 * @return bool
335 */
6a488035
TO
336 public function isActiveModule($name) {
337 $activeModules = $this->getActiveModuleFiles();
338 foreach ($activeModules as $activeModule) {
339 if ($activeModule['prefix'] == $name) {
340 return TRUE;
341 }
342 }
343 return FALSE;
344 }
345
346 /**
347 * Get a list of all installed modules, including enabled and disabled ones
348 *
349 * @return array CRM_Core_Module
350 */
351 public function getModules() {
352 $result = array();
353 $dao = new CRM_Core_DAO_Extension();
354 $dao->type = 'module';
355 $dao->find();
356 while ($dao->fetch()) {
357 $result[] = new CRM_Core_Module($dao->full_name, $dao->is_active);
358 }
359 return $result;
360 }
361
362 /**
363 * Given the class, provides the template path.
364 *
365 * @access public
366 *
367 * @param string $clazz extension class name
368 *
369 * @return string path to extension's templates directory
370 */
371 public function getTemplatePath($clazz) {
372 $path = $this->container->getPath($this->classToKey($clazz));
373 return $path . DIRECTORY_SEPARATOR . self::EXT_TEMPLATES_DIRNAME;
374 /*
375 $path = $this->classToPath($clazz);
376 $pathElm = explode(DIRECTORY_SEPARATOR, $path);
377 array_pop($pathElm);
378 return implode(DIRECTORY_SEPARATOR, $pathElm) . DIRECTORY_SEPARATOR . self::EXT_TEMPLATES_DIRNAME;
379 */
380 }
381
382 /**
383 * Given te class, provides the template name.
384 * @todo consider multiple templates, support for one template for now
385 *
386 * @access public
387 *
388 * @param string $clazz extension class name
389 *
390 * @return string extension's template name
391 */
392 public function getTemplateName($clazz) {
393 $info = $this->keyToInfo($this->classToKey($clazz));
394 return (string) $info->file . '.tpl';
395 }
396
397 public function refresh() {
398 $this->infos = array();
399 $this->moduleExtensions = NULL;
400 if ($this->cache) {
401 $this->cache->delete($this->cacheKey . '/moduleFiles');
402 }
403 }
404}