CRM add remaining missing comment blocks (autogenerated)
[civicrm-core.git] / CRM / Extension / Mapper.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
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 *
132 * @internal param string $key extension key
6a488035
TO
133 *
134 * @return string full path the extension .php file
135 */
136 public function classToPath($clazz) {
137 $elements = explode('_', $clazz);
138 $key = implode('.', $elements);
139 return $this->keyToPath($key);
140 }
141
142 /**
143 * Given the string, returns true or false if it's an extension key.
144 *
145 * @access public
146 *
147 * @param string $key a string which might be an extension key
148 *
149 * @return boolean true if given string is an extension name
150 */
151 public function isExtensionKey($key) {
152 // check if the string is an extension name or the class
153 return (strpos($key, '.') !== FALSE) ? TRUE : FALSE;
154 }
155
156 /**
157 * Given the string, returns true or false if it's an extension class name.
158 *
159 * @access public
160 *
161 * @param string $clazz a string which might be an extension class name
162 *
163 * @return boolean true if given string is an extension class name
164 */
165 public function isExtensionClass($clazz) {
166
167 if (substr($clazz, 0, 4) != 'CRM_') {
168 return (bool) preg_match('/^[a-z0-9]+(_[a-z0-9]+)+$/', $clazz);
169 }
170 return FALSE;
171 }
172
173 /**
174 * @param string $key extension fully-qualified-name
77b97be7
EM
175 * @param bool $fresh
176 *
177 * @throws CRM_Extension_Exception
178 * @throws Exception
6a488035
TO
179 * @return object CRM_Extension_Info
180 */
181 public function keyToInfo($key, $fresh = FALSE) {
182 if ($fresh || !array_key_exists($key, $this->infos)) {
183 try {
184 $this->infos[$key] = CRM_Extension_Info::loadFromFile($this->container->getPath($key) . DIRECTORY_SEPARATOR . CRM_Extension_Info::FILENAME);
185 } catch (CRM_Extension_Exception $e) {
186 // file has more detailed info, but we'll fallback to DB if it's missing -- DB has enough info to uninstall
187 $this->infos[$key] = CRM_Extension_System::singleton()->getManager()->createInfoFromDB($key);
188 if (!$this->infos[$key]) {
189 throw $e;
190 }
191 }
192 }
193 return $this->infos[$key];
194 }
195
196 /**
197 * Given the key, provides extension's class name.
198 *
199 * @access public
200 *
201 * @param string $key extension key
202 *
203 * @return string name of extension's main class
204 */
205 public function keyToClass($key) {
206 return str_replace('.', '_', $key);
207 }
208
209 /**
210 * Given the key, provides the path to file containing
211 * extension's main class.
212 *
213 * @access public
214 *
215 * @param string $key extension key
216 *
217 * @return string path to file containing extension's main class
218 */
219 public function keyToPath($key) {
220 $info = $this->keyToInfo($key);
221 return $this->container->getPath($key) . DIRECTORY_SEPARATOR . $info->file . '.php';
222 }
223
224 /**
225 * Given the key, provides the path to file containing
226 * extension's main class.
227 *
228 * @access public
229 * @param string $key extension key
230 * @return string local path of the extension source tree
231 */
232 public function keyToBasePath($key) {
233 if ($key == 'civicrm') {
234 return $this->civicrmPath;
235 }
236 return $this->container->getPath($key);
237 }
238
239 /**
240 * Given the key, provides the path to file containing
241 * extension's main class.
242 *
243 * @access public
244 *
245 * @param string $key extension key
246 *
247 * @return string url for resources in this extension
248 */
249 public function keyToUrl($key) {
250 if ($key == 'civicrm') {
dee7a2b1
PJ
251 // CRM-12130 Workaround: If the domain's config_backend is NULL at the start of the request,
252 // then the Mapper is wrongly constructed with an empty value for $this->civicrmUrl.
3d4a4ccf
PJ
253 if (empty($this->civicrmUrl)) {
254 $config = CRM_Core_Config::singleton();
255 return rtrim($config->resourceBase, '/');
256 }
6a488035
TO
257 return $this->civicrmUrl;
258 }
259
260 return $this->container->getResUrl($key);
261 }
262
263 /**
264 * Fetch the list of active extensions of type 'module'
265 *
266 * @param $fresh bool whether to forcibly reload extensions list from canonical store
267 * @return array - array(array('prefix' => $, 'file' => $))
268 */
269 public function getActiveModuleFiles($fresh = FALSE) {
270 $config = CRM_Core_Config::singleton();
271 if ($config->isUpgradeMode() || !defined('CIVICRM_DSN')) {
272 return array(); // hmm, ok
273 }
274
275 $moduleExtensions = NULL;
276 if ($this->cache && !$fresh) {
277 $moduleExtensions = $this->cache->get($this->cacheKey . '/moduleFiles');
278 }
279
280 if (!is_array($moduleExtensions)) {
281 // Check canonical module list
282 $moduleExtensions = array();
283 $sql = '
284 SELECT full_name, file
285 FROM civicrm_extension
286 WHERE is_active = 1
287 AND type = "module"
288 ';
289 $dao = CRM_Core_DAO::executeQuery($sql);
290 while ($dao->fetch()) {
291 try {
292 $moduleExtensions[] = array(
293 'prefix' => $dao->file,
294 'filePath' => $this->keyToPath($dao->full_name),
295 );
296 } catch (CRM_Extension_Exception $e) {
297 // Putting a stub here provides more consistency
298 // in how getActiveModuleFiles when racing between
299 // dirty file-removals and cache-clears.
300 CRM_Core_Session::setStatus($e->getMessage(), '', 'error');
301 $moduleExtensions[] = array(
302 'prefix' => $dao->file,
303 'filePath' => NULL,
304 );
305 }
306 }
307
308 if ($this->cache) {
309 $this->cache->set($this->cacheKey . '/moduleFiles', $moduleExtensions);
310 }
311 }
312 return $moduleExtensions;
313 }
314
e7ff7042
TO
315 /**
316 * Get a list of base URLs for all active modules
317 *
318 * @return array (string $extKey => string $baseUrl)
319 */
320 public function getActiveModuleUrls() {
321 // TODO optimization/caching
322 $urls = array();
323 $urls['civicrm'] = $this->keyToUrl('civicrm');
324 foreach ($this->getModules() as $module) {
325 /** @var $module CRM_Core_Module */
326 if ($module->is_active) {
327 $urls[$module->name] = $this->keyToUrl($module->name);
328 }
329 }
330 return $urls;
331 }
332
e0ef6999
EM
333 /**
334 * @param $name
335 *
336 * @return bool
337 */
6a488035
TO
338 public function isActiveModule($name) {
339 $activeModules = $this->getActiveModuleFiles();
340 foreach ($activeModules as $activeModule) {
341 if ($activeModule['prefix'] == $name) {
342 return TRUE;
343 }
344 }
345 return FALSE;
346 }
347
348 /**
349 * Get a list of all installed modules, including enabled and disabled ones
350 *
351 * @return array CRM_Core_Module
352 */
353 public function getModules() {
354 $result = array();
355 $dao = new CRM_Core_DAO_Extension();
356 $dao->type = 'module';
357 $dao->find();
358 while ($dao->fetch()) {
359 $result[] = new CRM_Core_Module($dao->full_name, $dao->is_active);
360 }
361 return $result;
362 }
363
364 /**
365 * Given the class, provides the template path.
366 *
367 * @access public
368 *
369 * @param string $clazz extension class name
370 *
371 * @return string path to extension's templates directory
372 */
373 public function getTemplatePath($clazz) {
374 $path = $this->container->getPath($this->classToKey($clazz));
375 return $path . DIRECTORY_SEPARATOR . self::EXT_TEMPLATES_DIRNAME;
376 /*
377 $path = $this->classToPath($clazz);
378 $pathElm = explode(DIRECTORY_SEPARATOR, $path);
379 array_pop($pathElm);
380 return implode(DIRECTORY_SEPARATOR, $pathElm) . DIRECTORY_SEPARATOR . self::EXT_TEMPLATES_DIRNAME;
381 */
382 }
383
384 /**
385 * Given te class, provides the template name.
386 * @todo consider multiple templates, support for one template for now
387 *
388 * @access public
389 *
390 * @param string $clazz extension class name
391 *
392 * @return string extension's template name
393 */
394 public function getTemplateName($clazz) {
395 $info = $this->keyToInfo($this->classToKey($clazz));
396 return (string) $info->file . '.tpl';
397 }
398
399 public function refresh() {
400 $this->infos = array();
401 $this->moduleExtensions = NULL;
402 if ($this->cache) {
403 $this->cache->delete($this->cacheKey . '/moduleFiles');
404 }
405 }
406}