Merge pull request #7326 from monishdeb/CRM-16901
[civicrm-core.git] / CRM / Utils / System / DrupalBase.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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-2015
32 * $Id$
33 *
34 */
35
36 /**
37 * Drupal specific stuff goes here
38 */
39 abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
40
41 /**
42 * Does this CMS / UF support a CMS specific logging mechanism?
43 * @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
44 * @var bool
45 */
46 var $supports_UF_Logging = TRUE;
47
48 /**
49 */
50 public function __construct() {
51 /**
52 * deprecated property to check if this is a drupal install. The correct method is to have functions on the UF classes for all UF specific
53 * functions and leave the codebase oblivious to the type of CMS
54 * @deprecated
55 * @var bool
56 */
57 $this->is_drupal = TRUE;
58 $this->supports_form_extensions = TRUE;
59 }
60
61 /**
62 * @inheritDoc
63 */
64 public function getDefaultSiteSettings($dir) {
65 $config = CRM_Core_Config::singleton();
66 $siteName = $siteRoot = NULL;
67 $matches = array();
68 if (preg_match(
69 '|/sites/([\w\.\-\_]+)/|',
70 $config->templateCompileDir,
71 $matches
72 )) {
73 $siteName = $matches[1];
74 if ($siteName) {
75 $siteName = "/sites/$siteName/";
76 $siteNamePos = strpos($dir, $siteName);
77 if ($siteNamePos !== FALSE) {
78 $siteRoot = substr($dir, 0, $siteNamePos);
79 }
80 }
81 }
82 $url = $config->userFrameworkBaseURL;
83 return array($url, $siteName, $siteRoot);
84 }
85
86 /**
87 * Check if a resource url is within the drupal directory and format appropriately.
88 *
89 * @param $url (reference)
90 *
91 * @return bool
92 * TRUE for internal paths, FALSE for external. The drupal_add_js fn is able to add js more
93 * efficiently if it is known to be in the drupal site
94 */
95 public function formatResourceUrl(&$url) {
96 $internal = FALSE;
97 $base = CRM_Core_Config::singleton()->resourceBase;
98 global $base_url;
99 // Handle absolute urls
100 // compares $url (which is some unknown/untrusted value from a third-party dev) to the CMS's base url (which is independent of civi's url)
101 // to see if the url is within our drupal dir, if it is we are able to treated it as an internal url
102 if (strpos($url, $base_url) === 0) {
103 $internal = TRUE;
104 $url = trim(str_replace($base_url, '', $url), '/');
105 }
106 // Handle relative urls that are within the CiviCRM module directory
107 elseif (strpos($url, $base) === 0) {
108 $internal = TRUE;
109 $url = $this->appendCoreDirectoryToResourceBase(substr(drupal_get_path('module', 'civicrm'), 0, -6)) . trim(substr($url, strlen($base)), '/');
110 }
111 // Strip query string
112 $q = strpos($url, '?');
113 if ($q && $internal) {
114 $url = substr($url, 0, $q);
115 }
116 return $internal;
117 }
118
119 /**
120 * In instance where civicrm folder has a drupal folder & a civicrm core folder @ the same level append the
121 * civicrm folder name to the url
122 * See CRM-13737 for discussion of how this allows implementers to alter the folder structure
123 * @todo - this only provides a limited amount of flexiblity - it still expects a 'civicrm' folder with a 'drupal' folder
124 * and is only flexible as to the name of the civicrm folder.
125 *
126 * @param string $url
127 * Potential resource url based on standard folder assumptions.
128 * @return string
129 * with civicrm-core directory appended if not standard civi dir
130 */
131 public function appendCoreDirectoryToResourceBase($url) {
132 global $civicrm_root;
133 $lastDirectory = basename($civicrm_root);
134 if ($lastDirectory != 'civicrm') {
135 return $url .= $lastDirectory . '/';
136 }
137 return $url;
138 }
139
140 /**
141 * Generate an internal CiviCRM URL (copied from DRUPAL/includes/common.inc#url)
142 *
143 * @inheritDoc
144 */
145 public function url(
146 $path = NULL,
147 $query = NULL,
148 $absolute = FALSE,
149 $fragment = NULL,
150 $frontend = FALSE,
151 $forceBackend = FALSE
152 ) {
153 $config = CRM_Core_Config::singleton();
154 $script = 'index.php';
155
156 $path = CRM_Utils_String::stripPathChars($path);
157
158 if (isset($fragment)) {
159 $fragment = '#' . $fragment;
160 }
161
162 $base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase;
163
164 $separator = '&';
165
166 if (!$config->cleanURL) {
167 if (isset($path)) {
168 if (isset($query)) {
169 return $base . $script . '?q=' . $path . $separator . $query . $fragment;
170 }
171 else {
172 return $base . $script . '?q=' . $path . $fragment;
173 }
174 }
175 else {
176 if (isset($query)) {
177 return $base . $script . '?' . $query . $fragment;
178 }
179 else {
180 return $base . $fragment;
181 }
182 }
183 }
184 else {
185 if (isset($path)) {
186 if (isset($query)) {
187 return $base . $path . '?' . $query . $fragment;
188 }
189 else {
190 return $base . $path . $fragment;
191 }
192 }
193 else {
194 if (isset($query)) {
195 return $base . $script . '?' . $query . $fragment;
196 }
197 else {
198 return $base . $fragment;
199 }
200 }
201 }
202 }
203
204 /**
205 * @inheritDoc
206 */
207 public function getUserIDFromUserObject($user) {
208 return !empty($user->uid) ? $user->uid : NULL;
209 }
210
211 /**
212 * @inheritDoc
213 */
214 public function setMessage($message) {
215 drupal_set_message($message);
216 }
217
218 /**
219 * @inheritDoc
220 */
221 public function getUniqueIdentifierFromUserObject($user) {
222 return empty($user->mail) ? NULL : $user->mail;
223 }
224
225 /**
226 * @inheritDoc
227 */
228 public function getLoggedInUniqueIdentifier() {
229 global $user;
230 return $this->getUniqueIdentifierFromUserObject($user);
231 }
232
233 /**
234 * @inheritDoc
235 */
236 public function permissionDenied() {
237 drupal_access_denied();
238 }
239
240 /**
241 * @inheritDoc
242 */
243 public function getUserRecordUrl($contactID) {
244 $uid = CRM_Core_BAO_UFMatch::getUFId($contactID);
245 if (CRM_Core_Session::singleton()
246 ->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(array(
247 'cms:administer users',
248 'cms:view user account',
249 ))
250 ) {
251 return CRM_Utils_System::url('user/' . $uid);
252 };
253 }
254
255 /**
256 * @inheritDoc
257 */
258 public function checkPermissionAddUser() {
259 return CRM_Core_Permission::check('administer users');
260 }
261
262 /**
263 * @inheritDoc
264 */
265 public function logger($message) {
266 if (CRM_Core_Config::singleton()->userFrameworkLogging && function_exists('watchdog')) {
267 watchdog('civicrm', '%message', array('%message' => $message), NULL, WATCHDOG_DEBUG);
268 }
269 }
270
271 /**
272 * @inheritDoc
273 */
274 public function clearResourceCache() {
275 _drupal_flush_css_js();
276 }
277
278 /**
279 * Append Drupal js to coreResourcesList.
280 *
281 * @param array $list
282 */
283 public function appendCoreResources(&$list) {
284 $list[] = 'js/crm.drupal.js';
285 }
286
287 /**
288 * @inheritDoc
289 */
290 public function flush() {
291 drupal_flush_all_caches();
292 }
293
294 /**
295 * @inheritDoc
296 */
297 public function getModules() {
298 $result = array();
299 $q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1');
300 foreach ($q as $row) {
301 $result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE);
302 }
303 return $result;
304 }
305
306 /**
307 * Find any users/roles/security-principals with the given permission
308 * and replace it with one or more permissions.
309 *
310 * @param string $oldPerm
311 * @param array $newPerms
312 * Array, strings.
313 *
314 * @return void
315 */
316 public function replacePermission($oldPerm, $newPerms) {
317 $roles = user_roles(FALSE, $oldPerm);
318 if (!empty($roles)) {
319 foreach (array_keys($roles) as $rid) {
320 user_role_revoke_permissions($rid, array($oldPerm));
321 user_role_grant_permissions($rid, $newPerms);
322 }
323 }
324 }
325
326 /**
327 * @inheritDoc
328 */
329 public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
330 if (empty($url)) {
331 return $url;
332 }
333
334 //CRM-7803 -from d7 onward.
335 $config = CRM_Core_Config::singleton();
336 if (function_exists('variable_get') &&
337 module_exists('locale') &&
338 function_exists('language_negotiation_get')
339 ) {
340 global $language;
341
342 //does user configuration allow language
343 //support from the URL (Path prefix or domain)
344 if (language_negotiation_get('language') == 'locale-url') {
345 $urlType = variable_get('locale_language_negotiation_url_part');
346
347 //url prefix
348 if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
349 if (isset($language->prefix) && $language->prefix) {
350 if ($addLanguagePart) {
351 $url .= $language->prefix . '/';
352 }
353 if ($removeLanguagePart) {
354 $url = str_replace("/{$language->prefix}/", '/', $url);
355 }
356 }
357 }
358 //domain
359 if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN) {
360 if (isset($language->domain) && $language->domain) {
361 if ($addLanguagePart) {
362 $url = (CRM_Utils_System::isSSL() ? 'https' : 'http') . '://' . $language->domain . base_path();
363 }
364 if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
365 $url = str_replace('\\', '/', $url);
366 $parseUrl = parse_url($url);
367
368 //kinda hackish but not sure how to do it right
369 //hope http_build_url() will help at some point.
370 if (is_array($parseUrl) && !empty($parseUrl)) {
371 $urlParts = explode('/', $url);
372 $hostKey = array_search($parseUrl['host'], $urlParts);
373 $ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
374 $urlParts[$hostKey] = $ufUrlParts['host'];
375 $url = implode('/', $urlParts);
376 }
377 }
378 }
379 }
380 }
381 }
382 return $url;
383 }
384
385 /**
386 * @inheritDoc
387 */
388 public function getVersion() {
389 return defined('VERSION') ? VERSION : 'Unknown';
390 }
391
392 /**
393 * @inheritDoc
394 */
395 public function updateCategories() {
396 // copied this from profile.module. Seems a bit inefficient, but i don't know a better way
397 cache_clear_all();
398 menu_rebuild();
399 }
400
401 /**
402 * @inheritDoc
403 */
404 public function getUFLocale() {
405 // return CiviCRM’s xx_YY locale that either matches Drupal’s Chinese locale
406 // (for CRM-6281), Drupal’s xx_YY or is retrieved based on Drupal’s xx
407 // sometimes for CLI based on order called, this might not be set and/or empty
408 global $language;
409
410 if (empty($language)) {
411 return NULL;
412 }
413
414 if ($language->language == 'zh-hans') {
415 return 'zh_CN';
416 }
417
418 if ($language->language == 'zh-hant') {
419 return 'zh_TW';
420 }
421
422 if (preg_match('/^.._..$/', $language->language)) {
423 return $language->language;
424 }
425
426 return CRM_Core_I18n_PseudoConstant::longForShort(substr($language->language, 0, 2));
427 }
428
429 /**
430 * @inheritDoc
431 */
432 public function setUFLocale($civicrm_language) {
433 global $language;
434
435 $langcode = substr($civicrm_language, 0, 2);
436 $languages = language_list();
437
438 if (isset($languages[$langcode])) {
439 $language = $languages[$langcode];
440
441 // Config must be re-initialized to reset the base URL
442 // otherwise links will have the wrong language prefix/domain.
443 $config = CRM_Core_Config::singleton();
444 $config->free();
445
446 return TRUE;
447 }
448
449 return FALSE;
450 }
451
452 /**
453 * Perform any post login activities required by the UF -
454 * e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
455 * calls hook_user op 'login' and generates a new session.
456 *
457 * @param array $params
458 *
459 * FIXME: Document values accepted/required by $params
460 */
461 public function userLoginFinalize($params = array()) {
462 user_login_finalize($params);
463 }
464
465 /**
466 * @inheritDoc
467 */
468 public function getLoginDestination(&$form) {
469 $args = NULL;
470
471 $id = $form->get('id');
472 if ($id) {
473 $args .= "&id=$id";
474 }
475 else {
476 $gid = $form->get('gid');
477 if ($gid) {
478 $args .= "&gid=$gid";
479 }
480 else {
481 // Setup Personal Campaign Page link uses pageId
482 $pageId = $form->get('pageId');
483 if ($pageId) {
484 $component = $form->get('component');
485 $args .= "&pageId=$pageId&component=$component&action=add";
486 }
487 }
488 }
489
490 $destination = NULL;
491 if ($args) {
492 // append destination so user is returned to form they came from after login
493 $destination = CRM_Utils_System::currentPath() . '?reset=1' . $args;
494 }
495 return $destination;
496 }
497
498 /**
499 * Fixme: Why are we overriding the parent function? Seems inconsistent.
500 * This version supplies slightly different params to $this->url (not absolute and html encoded) but why?
501 *
502 * @param string $action
503 *
504 * @return string
505 */
506 public function postURL($action) {
507 if (!empty($action)) {
508 return $action;
509 }
510 return $this->url($_GET['q']);
511 }
512
513 /**
514 * Get an array of user details for a contact, containing at minimum the user ID & name.
515 *
516 * @param int $contactID
517 *
518 * @return array
519 * CMS user details including
520 * - id
521 * - name (ie the system user name.
522 */
523 public function getUser($contactID) {
524 $userDetails = parent::getUser($contactID);
525 $user = $this->getUserObject($userDetails['id']);
526 $userDetails['name'] = $user->name;
527 $userDetails['email'] = $user->mail;
528 return $userDetails;
529 }
530
531 /**
532 * Load the user object.
533 *
534 * Note this function still works in drupal 6, 7 & 8 but is deprecated in Drupal 8.
535 *
536 * @param $userID
537 *
538 * @return object
539 */
540 public function getUserObject($userID) {
541 return user_load($userID);
542 }
543
544 public function parseDrupalSiteName($civicrm_root) {
545 $siteName = NULL;
546 if (strpos($civicrm_root,
547 DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . 'all' . DIRECTORY_SEPARATOR . 'modules'
548 ) === FALSE
549 ) {
550 $startPos = strpos($civicrm_root,
551 DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR
552 );
553 $endPos = strpos($civicrm_root,
554 DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR
555 );
556 if ($startPos && $endPos) {
557 // if component is in sites/SITENAME/modules
558 $siteName = substr($civicrm_root,
559 $startPos + 7,
560 $endPos - $startPos - 7
561 );
562 }
563 }
564 return $siteName;
565 }
566
567 }