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