Merge pull request #14948 from civicrm/5.16
[civicrm-core.git] / CRM / Utils / System / Base.php
CommitLineData
6a488035
TO
1<?php
2
3/**
4 * Base class for UF system integrations
5 */
6abstract class CRM_Utils_System_Base {
2b0e7d03 7
4caaa696 8 /**
2b0e7d03
EM
9 * Deprecated property to check if this is a drupal install.
10 *
11 * The correct method is to have functions on the UF classes for all UF specific
4caaa696 12 * functions and leave the codebase oblivious to the type of CMS
7cdf0f5b 13 *
4caaa696 14 * @var bool
6714d8d2 15 * @deprecated
7cdf0f5b 16 * TRUE, if the CMS is Drupal.
4caaa696 17 */
6714d8d2 18 public $is_drupal = FALSE;
4caaa696
EM
19
20 /**
100fef9d 21 * Deprecated property to check if this is a joomla install. The correct method is to have functions on the UF classes for all UF specific
4caaa696 22 * functions and leave the codebase oblivious to the type of CMS
7cdf0f5b 23 *
4caaa696 24 * @var bool
6714d8d2 25 * @deprecated
7cdf0f5b 26 * TRUE, if the CMS is Joomla!.
4caaa696 27 */
6714d8d2 28 public $is_joomla = FALSE;
4caaa696 29
7cdf0f5b
AH
30 /**
31 * deprecated property to check if this is a wordpress install. The correct method is to have functions on the UF classes for all UF specific
32 * functions and leave the codebase oblivious to the type of CMS
33 *
7cdf0f5b 34 * @var bool
6714d8d2 35 * @deprecated
7cdf0f5b
AH
36 * TRUE, if the CMS is WordPress.
37 */
6714d8d2 38 public $is_wordpress = FALSE;
6a488035 39
e0dd98a5
EM
40 /**
41 * Does this CMS / UF support a CMS specific logging mechanism?
e0dd98a5 42 * @var bool
6714d8d2 43 * @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
e0dd98a5 44 */
6714d8d2 45 public $supports_UF_Logging = FALSE;
e0dd98a5 46
7cdf0f5b
AH
47 /**
48 * @var bool
49 * TRUE, if the CMS allows CMS forms to be extended by hooks.
6a488035 50 */
6714d8d2 51 public $supports_form_extensions = FALSE;
6a488035 52
d4330c62
TO
53 public function initialize() {
54 if (\CRM_Utils_System::isSSL()) {
55 $this->mapConfigToSSL();
56 }
57 }
58
6714d8d2 59 abstract public function loadBootStrap($params = [], $loadUser = TRUE, $throwError = TRUE, $realPath = NULL);
9ba02e3e 60
17f443df 61 /**
fe482240 62 * Append an additional breadcrumb tag to the existing breadcrumb.
17f443df
CW
63 *
64 * @param array $breadCrumbs
65 */
66 public function appendBreadCrumb($breadCrumbs) {
67 }
68
69 /**
fe482240 70 * Reset an additional breadcrumb tag to the existing breadcrumb.
17f443df
CW
71 */
72 public function resetBreadCrumb() {
73 }
74
75 /**
fe482240 76 * Append a string to the head of the html file.
17f443df
CW
77 *
78 * @param string $head
79 * The new string to be appended.
80 */
81 public function addHTMLHead($head) {
82 }
83
84 /**
fe482240 85 * Rewrite various system urls to https.
17f443df
CW
86 */
87 public function mapConfigToSSL() {
88 // dont need to do anything, let CMS handle their own switch to SSL
89 }
90
91 /**
fe482240 92 * Figure out the post url for QuickForm.
17f443df
CW
93 *
94 * @param string $action
95 * The default url if one is pre-specified.
96 *
97 * @return string
98 * The url to post the form.
99 */
100 public function postURL($action) {
101 $config = CRM_Core_Config::singleton();
102 if (!empty($action)) {
103 return $action;
104 }
105
106 return $this->url(CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET),
107 NULL, TRUE, NULL, FALSE
108 );
109 }
110
111 /**
112 * Generate the url string to a CiviCRM path.
113 *
114 * @param string $path
115 * The path being linked to, such as "civicrm/add".
116 * @param string $query
117 * A query string to append to the link.
118 * @param bool $absolute
119 * Whether to force the output to be an absolute link (beginning with http).
120 * Useful for links that will be displayed outside the site, such as in an RSS feed.
121 * @param string $fragment
122 * A fragment identifier (named anchor) to append to the link.
17f443df
CW
123 * @param bool $frontend
124 * This link should be to the CMS front end (applies to WP & Joomla).
125 * @param bool $forceBackend
126 * This link should be to the CMS back end (applies to WP & Joomla).
127 *
128 * @return string
129 */
130 public function url(
131 $path = NULL,
132 $query = NULL,
133 $absolute = FALSE,
134 $fragment = NULL,
17f443df
CW
135 $frontend = FALSE,
136 $forceBackend = FALSE
137 ) {
138 return NULL;
139 }
140
141 /**
fe482240 142 * Authenticate the user against the CMS db.
17f443df
CW
143 *
144 * @param string $name
145 * The user name.
146 * @param string $password
147 * The password for the above user.
148 * @param bool $loadCMSBootstrap
149 * Load cms bootstrap?.
150 * @param string $realPath
151 * Filename of script
152 *
153 * @return array|bool
154 * [contactID, ufID, unique string] else false if no auth
155 */
156 public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
157 return FALSE;
158 }
159
160 /**
fe482240 161 * Set a message in the CMS to display to a user.
17f443df
CW
162 *
163 * @param string $message
164 * The message to set.
165 */
166 public function setMessage($message) {
167 }
168
169 /**
fe482240 170 * Load user into session.
17f443df 171 *
2b0e7d03 172 * @param obj $user
17f443df
CW
173 *
174 * @return bool
175 */
176 public function loadUser($user) {
177 return TRUE;
178 }
179
180 /**
2b0e7d03 181 * Immediately stop script execution and display a 401 "Access Denied" page.
17f443df
CW
182 */
183 public function permissionDenied() {
184 CRM_Core_Error::fatal(ts('You do not have permission to access this page.'));
185 }
186
187 /**
2b0e7d03 188 * Immediately stop script execution, log out the user and redirect to the home page.
17f443df
CW
189 *
190 * @deprecated
191 * This function should be removed in favor of linking to the CMS's logout page
192 */
193 public function logout() {
194 }
195
196 /**
197 * Clear CMS caches related to the user registration/profile forms.
198 * Used when updating/embedding profiles on CMS user forms.
199 * @see CRM-3600
200 */
201 public function updateCategories() {
202 }
203
204 /**
fe482240 205 * Get the locale set in the CMS.
17f443df
CW
206 *
207 * @return string|null
208 * Locale or null for none
209 */
210 public function getUFLocale() {
211 return NULL;
212 }
213
6a488035 214 /**
2b0e7d03 215 * If we are using a theming system, invoke theme, else just print the content.
6a488035 216 *
77855840
TO
217 * @param string $content
218 * The content that will be themed.
219 * @param bool $print
220 * Are we displaying to the screen or bypassing theming?.
221 * @param bool $maintenance
222 * For maintenance mode.
6a488035 223 *
7cdf0f5b
AH
224 * @throws Exception
225 * @return string|null
226 * NULL, If $print is FALSE, and some other criteria match up.
227 * The themed string, otherwise.
26659f0c
AH
228 *
229 * @todo The return value is inconsistent.
230 * @todo Better to always return, and never print.
6a488035 231 */
00be9182 232 public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
6a488035
TO
233 $ret = FALSE;
234
235 // TODO: Split up; this was copied verbatim from CiviCRM 4.0's multi-UF theming function
236 // but the parts should be copied into cleaner subclass implementations
1e305b0b
DL
237 $config = CRM_Core_Config::singleton();
238 if (
239 $config->userSystem->is_drupal &&
240 function_exists('theme') &&
241 !$print
242 ) {
6a488035
TO
243 if ($maintenance) {
244 drupal_set_breadcrumb('');
245 drupal_maintenance_theme();
10221f8a
TO
246 if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
247 CRM_Utils_System::addHTMLHead($region->render(''));
248 }
be2fb01f 249 print theme('maintenance_page', ['content' => $content]);
6a488035
TO
250 exit();
251 }
2b0e7d03
EM
252 // TODO: Figure out why D7 returns but everyone else prints
253 $ret = TRUE;
6a488035
TO
254 }
255 $out = $content;
256
1e305b0b
DL
257 if (
258 !$print &&
f3a87cf4 259 CRM_Core_Config::singleton()->userFramework == 'WordPress'
6a488035 260 ) {
aecede9a
AH
261 if (!function_exists('is_admin')) {
262 throw new \Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
263 }
264 if (!defined('ABSPATH')) {
265 throw new \Exception('Constant "ABSPATH" is not defined, even though WordPress is the user framework.');
266 }
6a488035 267 if (is_admin()) {
e7292422 268 require_once ABSPATH . 'wp-admin/admin-header.php';
6a488035
TO
269 }
270 else {
7cdf0f5b 271 // FIXME: we need to figure out to replace civicrm content on the frontend pages
6a488035
TO
272 }
273 }
274
275 if ($ret) {
276 return $out;
277 }
278 else {
279 print $out;
85c5f34c 280 return NULL;
6a488035
TO
281 }
282 }
283
bb3a214a
EM
284 /**
285 * @return string
286 */
00be9182 287 public function getDefaultBlockLocation() {
6a488035
TO
288 return 'left';
289 }
290
8246bca4 291 /**
292 * Get the absolute path to the site's base url.
293 *
294 * @return bool|mixed|string
295 */
d4330c62
TO
296 public function getAbsoluteBaseURL() {
297 if (!defined('CIVICRM_UF_BASEURL')) {
298 return FALSE;
299 }
300
301 $url = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
302
303 //format url for language negotiation, CRM-7803
304 $url = $this->languageNegotiationURL($url);
305
306 if (CRM_Utils_System::isSSL()) {
307 $url = str_replace('http://', 'https://', $url);
308 }
309
310 return $url;
311 }
312
8246bca4 313 /**
314 * Get the relative path to the sites base url.
315 *
316 * @return bool
317 */
d4330c62
TO
318 public function getRelativeBaseURL() {
319 $absoluteBaseURL = $this->getAbsoluteBaseURL();
320 if ($absoluteBaseURL === FALSE) {
321 return FALSE;
322 }
323 $parts = parse_url($absoluteBaseURL);
324 return $parts['path'];
325 //$this->useFrameworkRelativeBase = empty($base['path']) ? '/' : $base['path'];
326 }
327
bb3a214a 328 /**
fe482240 329 * Get CMS Version.
17f443df 330 *
bb3a214a
EM
331 * @return string
332 */
00be9182 333 public function getVersion() {
6a488035
TO
334 return 'Unknown';
335 }
336
337 /**
338 * Format the url as per language Negotiation.
339 *
340 * @param string $url
77b97be7
EM
341 * @param bool $addLanguagePart
342 * @param bool $removeLanguagePart
343 *
7cdf0f5b
AH
344 * @return string
345 * Formatted url.
6a488035 346 */
d5cc0fc2 347 public function languageNegotiationURL(
6a488035
TO
348 $url,
349 $addLanguagePart = TRUE,
350 $removeLanguagePart = FALSE
351 ) {
352 return $url;
353 }
354
e29aefb4
TO
355 /**
356 * Determine the location of the CMS root.
357 *
7cdf0f5b
AH
358 * @return string|null
359 * Local file system path to CMS root, or NULL if it cannot be determined
6a488035 360 */
00be9182 361 public function cmsRootPath() {
e29aefb4 362 return NULL;
6a488035
TO
363 }
364
17f443df
CW
365 /**
366 * Create a user in the CMS.
367 *
368 * @param array $params
369 * @param string $mail
370 * Email id for cms user.
371 *
372 * @return int|bool
373 * uid if user exists, false otherwise
374 */
375 public function createUser(&$params, $mail) {
376 return FALSE;
377 }
378
379 /**
380 * Update a user's email address in the CMS.
381 *
382 * @param int $ufID
383 * User ID in CMS.
384 * @param string $email
385 * Primary contact email address.
386 */
387 public function updateCMSName($ufID, $email) {
388 }
389
390 /**
391 * Check if user is logged in to the CMS.
392 *
393 * @return bool
394 */
395 public function isUserLoggedIn() {
396 return FALSE;
397 }
398
8caad0ce 399 /**
400 * Check if user registration is permitted.
401 *
402 * @return bool
403 */
404 public function isUserRegistrationPermitted() {
405 return FALSE;
406 }
407
63df6889
HD
408 /**
409 * Check if user can create passwords or is initially assigned a system-generated one.
410 *
411 * @return bool
412 */
1a6630be 413 public function isPasswordUserGenerated() {
63df6889
HD
414 return FALSE;
415 }
416
68e3bc34 417 /**
418 * Is a front end page being accessed.
419 *
420 * Generally this would be a contribution form or other public page as opposed to a backoffice page (like contact edit).
421 *
422 * @todo Drupal uses the is_public setting - clarify & rationalise. See https://github.com/civicrm/civicrm-drupal/pull/546/files
423 *
424 * @return bool
425 */
426 public function isFrontEndPage() {
427 return CRM_Core_Config::singleton()->userFrameworkFrontend;
428 }
429
6a488035
TO
430 /**
431 * Get user login URL for hosting CMS (method declared in each CMS system class)
432 *
77855840
TO
433 * @param string $destination
434 * If present, add destination to querystring (works for Drupal only).
6a488035 435 *
a6c01b45
CW
436 * @return string
437 * loginURL for the current CMS
6a488035 438 */
6714d8d2 439 abstract public function getLoginURL($destination = '');
6a488035 440
17f443df 441 /**
2b0e7d03
EM
442 * Get the login destination string.
443 *
444 * When this is passed in the URL the user will be directed to it after filling in the CMS form.
17f443df
CW
445 *
446 * @param CRM_Core_Form $form
447 * Form object representing the 'current' form - to which the user will be returned.
2b0e7d03 448 *
17f443df
CW
449 * @return string|NULL
450 * destination value for URL
451 */
452 public function getLoginDestination(&$form) {
453 return NULL;
454 }
455
46b6363c 456 /**
fe482240 457 * Determine the native ID of the CMS user.
46b6363c 458 *
100fef9d 459 * @param string $username
f4aaa82a
EM
460 *
461 * @throws CRM_Core_Exception
46b6363c 462 */
00be9182 463 public function getUfId($username) {
46b6363c
TO
464 $className = get_class($this);
465 throw new CRM_Core_Exception("Not implemented: {$className}->getUfId");
466 }
467
bc854509 468 /**
469 * Set the localisation from the user framework.
470 *
471 * @param string $civicrm_language
472 *
473 * @return bool
474 */
a7c57397
TO
475 public function setUFLocale($civicrm_language) {
476 return TRUE;
477 }
478
5d0eb86b 479 /**
fe482240 480 * Set a init session with user object.
5d0eb86b 481 *
7cdf0f5b
AH
482 * @param array $data
483 * Array with user specific data
5d0eb86b 484 */
00be9182 485 public function setUserSession($data) {
5d0eb86b
BS
486 list($userID, $ufID) = $data;
487 $session = CRM_Core_Session::singleton();
488 $session->set('ufID', $ufID);
489 $session->set('userID', $userID);
490 }
d8a4acc0
C
491
492 /**
2b0e7d03 493 * Reset any system caches that may be required for proper CiviCRM integration.
d8a4acc0 494 */
00be9182 495 public function flush() {
d8a4acc0
C
496 // nullop by default
497 }
82d9c21e 498
f091327b 499 /**
2b0e7d03 500 * Flush css/js caches.
f091327b 501 */
00be9182 502 public function clearResourceCache() {
f091327b
CW
503 // nullop by default
504 }
505
17f443df 506 /**
fe482240 507 * Add a script file.
17f443df
CW
508 *
509 * Note: This function is not to be called directly
510 * @see CRM_Core_Region::render()
511 *
2b0e7d03 512 * @param string $url absolute path to file
17f443df
CW
513 * @param string $region
514 * location within the document: 'html-header', 'page-header', 'page-footer'.
515 *
516 * @return bool
517 * TRUE if we support this operation in this CMS, FALSE otherwise
518 */
519 public function addScriptUrl($url, $region) {
520 return FALSE;
521 }
522
523 /**
fe482240 524 * Add an inline script.
17f443df
CW
525 *
526 * Note: This function is not to be called directly
527 * @see CRM_Core_Region::render()
528 *
2b0e7d03 529 * @param string $code javascript code
17f443df
CW
530 * @param string $region
531 * location within the document: 'html-header', 'page-header', 'page-footer'.
532 *
533 * @return bool
534 * TRUE if we support this operation in this CMS, FALSE otherwise
535 */
536 public function addScript($code, $region) {
537 return FALSE;
538 }
539
540 /**
fe482240 541 * Add a css file.
17f443df
CW
542 *
543 * Note: This function is not to be called directly
544 * @see CRM_Core_Region::render()
545 *
2b0e7d03 546 * @param string $url absolute path to file
17f443df
CW
547 * @param string $region
548 * location within the document: 'html-header', 'page-header', 'page-footer'.
549 *
550 * @return bool
551 * TRUE if we support this operation in this CMS, FALSE otherwise
552 */
553 public function addStyleUrl($url, $region) {
554 return FALSE;
555 }
556
557 /**
fe482240 558 * Add an inline style.
17f443df
CW
559 *
560 * Note: This function is not to be called directly
561 * @see CRM_Core_Region::render()
562 *
2b0e7d03 563 * @param string $code css code
17f443df
CW
564 * @param string $region
565 * location within the document: 'html-header', 'page-header', 'page-footer'.
566 *
567 * @return bool
568 * TRUE if we support this operation in this CMS, FALSE otherwise
569 */
570 public function addStyle($code, $region) {
571 return FALSE;
572 }
573
574 /**
fe482240 575 * Sets the title of the page.
17f443df
CW
576 *
577 * @param string $title
578 * Title to set in html header
579 * @param string|null $pageTitle
580 * Title to set in html body (if different)
581 */
582 public function setTitle($title, $pageTitle = NULL) {
583 }
584
c8950569 585 /**
fe482240 586 * Return default Site Settings.
f4aaa82a 587 *
7cdf0f5b 588 * @param string $dir
f4aaa82a 589 *
7cdf0f5b 590 * @return array
d5cc0fc2 591 * - $url, (Joomla - non admin url)
592 * - $siteName,
593 * - $siteRoot
9977c6f5 594 */
00be9182 595 public function getDefaultSiteSettings($dir) {
9977c6f5 596 $config = CRM_Core_Config::singleton();
597 $url = $config->userFrameworkBaseURL;
be2fb01f 598 return [$url, NULL, NULL];
9977c6f5 599 }
c8950569 600
f6f958e4
TO
601 /**
602 * Determine the default location for file storage.
603 *
604 * FIXME:
605 * 1. This was pulled out from a bigger function. It should be split
606 * into even smaller pieces and marked abstract.
607 * 2. This would be easier to compute by a calling a CMS API, but
608 * for whatever reason Civi gets it from config data.
609 *
610 * @return array
611 * - url: string. ex: "http://example.com/sites/foo.com/files/civicrm"
612 * - path: string. ex: "/var/www/sites/foo.com/files/civicrm"
613 */
614 public function getDefaultFileStorage() {
615 global $civicrm_root;
616 $config = CRM_Core_Config::singleton();
617 $baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE);
618
619 $filesURL = NULL;
620 $filesPath = NULL;
621
622 if ($config->userFramework == 'Joomla') {
623 // gross hack
624 // we need to remove the administrator/ from the end
625 $tempURL = str_replace("/administrator/", "/", $baseURL);
626 $filesURL = $tempURL . "media/civicrm/";
627 }
f6f958e4
TO
628 elseif ($config->userFramework == 'UnitTests') {
629 $filesURL = $baseURL . "sites/default/files/civicrm/";
630 }
631 else {
632 throw new CRM_Core_Exception("Failed to locate default file storage ($config->userFramework)");
633 }
634
be2fb01f 635 return [
f6f958e4
TO
636 'url' => $filesURL,
637 'path' => CRM_Utils_File::baseFilePath(),
be2fb01f 638 ];
f6f958e4
TO
639 }
640
641 /**
642 * Determine the location of the CiviCRM source tree.
643 *
644 * FIXME:
645 * 1. This was pulled out from a bigger function. It should be split
646 * into even smaller pieces and marked abstract.
647 * 2. This would be easier to compute by a calling a CMS API, but
648 * for whatever reason we take the hard way.
649 *
650 * @return array
651 * - url: string. ex: "http://example.com/sites/all/modules/civicrm"
652 * - path: string. ex: "/var/www/sites/all/modules/civicrm"
653 */
654 public function getCiviSourceStorage() {
655 global $civicrm_root;
656 $config = CRM_Core_Config::singleton();
657
658 // Don't use $config->userFrameworkBaseURL; it has garbage on it.
659 // More generally, w shouldn't be using $config here.
660 if (!defined('CIVICRM_UF_BASEURL')) {
661 throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
662 }
663 $baseURL = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
664 if (CRM_Utils_System::isSSL()) {
665 $baseURL = str_replace('http://', 'https://', $baseURL);
666 }
667
668 if ($config->userFramework == 'Joomla') {
669 $userFrameworkResourceURL = $baseURL . "components/com_civicrm/civicrm/";
670 }
671 elseif ($config->userFramework == 'WordPress') {
750752f2 672 $userFrameworkResourceURL = CIVICRM_PLUGIN_URL . "civicrm/";
f6f958e4 673 }
3403e54b
TO
674 elseif ($this->is_drupal) {
675 // Drupal setting
676 // check and see if we are installed in sites/all (for D5 and above)
677 // we dont use checkURL since drupal generates an error page and throws
678 // the system for a loop on lobo's macosx box
679 // or in modules
680 $cmsPath = $config->userSystem->cmsRootPath();
681 $userFrameworkResourceURL = $baseURL . str_replace("$cmsPath/", '',
682 str_replace('\\', '/', $civicrm_root)
683 );
684
685 $siteName = $config->userSystem->parseDrupalSiteNameFromRoot($civicrm_root);
686 if ($siteName) {
687 $civicrmDirName = trim(basename($civicrm_root));
688 $userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/";
689 }
690 }
f6f958e4
TO
691 else {
692 $userFrameworkResourceURL = NULL;
693 }
694
be2fb01f 695 return [
6ee26d90 696 'url' => CRM_Utils_File::addTrailingSlash($userFrameworkResourceURL, '/'),
f6f958e4 697 'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
be2fb01f 698 ];
f6f958e4
TO
699 }
700
82d9c21e 701 /**
2b0e7d03
EM
702 * Perform any post login activities required by the CMS.
703 *
53980972 704 * e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
705 * calls hook_user op 'login' and generates a new session.
e43cc689 706 *
7cdf0f5b 707 * @param array $params
95d68223
TO
708 *
709 * FIXME: Document values accepted/required by $params
c8950569 710 */
be2fb01f 711 public function userLoginFinalize($params = []) {
82d9c21e 712 }
5a604d61
E
713
714 /**
fe482240 715 * Set timezone in mysql so that timestamp fields show the correct time.
5a604d61 716 */
9b873358 717 public function setMySQLTimeZone() {
5a604d61 718 $timeZoneOffset = $this->getTimeZoneOffset();
9b873358 719 if ($timeZoneOffset) {
5a604d61
E
720 $sql = "SET time_zone = '$timeZoneOffset'";
721 CRM_Core_DAO::executequery($sql);
722 }
723 }
724
725 /**
fe482240 726 * Get timezone from CMS.
7cdf0f5b
AH
727 *
728 * @return string|false|null
6491539b 729 */
9b873358 730 public function getTimeZoneOffset() {
6491539b 731 $timezone = $this->getTimeZoneString();
8b647c3a 732 if ($timezone) {
2451c06a 733 if ($timezone == 'UTC' || $timezone == 'Etc/UTC') {
030113f0 734 // CRM-17072 Let's short-circuit all the zero handling & return it here!
735 return '+00:00';
736 }
6491539b
DL
737 $tzObj = new DateTimeZone($timezone);
738 $dateTime = new DateTime("now", $tzObj);
739 $tz = $tzObj->getOffset($dateTime);
740
f2173d64 741 if ($tz === 0) {
278460dd
AS
742 // CRM-21422
743 return '+00:00';
744 }
745
8b647c3a
AH
746 if (empty($tz)) {
747 return FALSE;
6491539b
DL
748 }
749
e7292422 750 $timeZoneOffset = sprintf("%02d:%02d", $tz / 3600, abs(($tz / 60) % 60));
6491539b 751
8b647c3a 752 if ($timeZoneOffset > 0) {
6491539b
DL
753 $timeZoneOffset = '+' . $timeZoneOffset;
754 }
755 return $timeZoneOffset;
756 }
85c5f34c 757 return NULL;
6491539b
DL
758 }
759
760 /**
fe482240 761 * Get timezone as a string.
7cdf0f5b 762 * @return string
17f443df 763 * Timezone string e.g. 'America/Los_Angeles'
6491539b 764 */
00be9182 765 public function getTimeZoneString() {
48ec57ab 766 return date_default_timezone_get();
5a604d61 767 }
2b617cb0
EM
768
769 /**
2b0e7d03
EM
770 * Get Unique Identifier from UserFramework system (CMS).
771 *
77855840
TO
772 * @param object $user
773 * Object as described by the User Framework.
2b0e7d03 774 *
72b3a70c 775 * @return mixed
2b0e7d03 776 * Unique identifier from the user Framework system
2b617cb0 777 */
e7292422 778 public function getUniqueIdentifierFromUserObject($user) {
17f443df 779 return NULL;
e7292422 780 }
2b617cb0 781
32998c82 782 /**
2b0e7d03
EM
783 * Get User ID from UserFramework system (CMS).
784 *
77855840 785 * @param object $user
2b0e7d03 786 *
77855840 787 * Object as described by the User Framework.
17f443df 788 * @return null|int
32998c82 789 */
e7292422 790 public function getUserIDFromUserObject($user) {
17f443df 791 return NULL;
e7292422 792 }
32998c82 793
2b0e7d03
EM
794 /**
795 * Get an array of user details for a contact, containing at minimum the user ID & name.
796 *
797 * @param int $contactID
798 *
799 * @return array
800 * CMS user details including
801 * - id
802 * - name (ie the system user name.
803 */
804 public function getUser($contactID) {
be2fb01f 805 $ufMatch = civicrm_api3('UFMatch', 'getsingle', [
2b0e7d03
EM
806 'contact_id' => $contactID,
807 'domain_id' => CRM_Core_Config::domainID(),
be2fb01f
CW
808 ]);
809 return [
2b0e7d03
EM
810 'id' => $ufMatch['uf_id'],
811 'name' => $ufMatch['uf_name'],
be2fb01f 812 ];
2b0e7d03
EM
813 }
814
32998c82
EM
815 /**
816 * Get currently logged in user uf id.
817 *
17f443df
CW
818 * @return int|null
819 * logged in user uf id.
32998c82 820 */
e7292422 821 public function getLoggedInUfID() {
17f443df 822 return NULL;
e7292422 823 }
32998c82 824
2b617cb0
EM
825 /**
826 * Get currently logged in user unique identifier - this tends to be the email address or user name.
827 *
17f443df 828 * @return string|null
a6c01b45 829 * logged in user unique identifier
2b617cb0 830 */
e7292422 831 public function getLoggedInUniqueIdentifier() {
17f443df 832 return NULL;
e7292422 833 }
2b617cb0 834
32998c82 835 /**
2b0e7d03
EM
836 * Return a UFID (user account ID from the UserFramework / CMS system.
837 *
838 * ID is based on the user object passed, defaulting to the logged in user if not passed.
839 *
840 * Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would
841 * seem to be resolving the user id before calling the function.
32998c82
EM
842 *
843 * Note there is already a function getUFId which takes $username as a param - we could add $user
2b0e7d03
EM
844 * as a second param to it but it seems messy - just overloading it because the name is taken.
845 *
2b617cb0 846 * @param object $user
2b0e7d03 847 *
a6c01b45 848 * @return int
2b0e7d03 849 * User ID of UF System
32998c82 850 */
00be9182 851 public function getBestUFID($user = NULL) {
22e263ad 852 if ($user) {
32998c82
EM
853 return $this->getUserIDFromUserObject($user);
854 }
855 return $this->getLoggedInUfID();
856 }
2b617cb0
EM
857
858 /**
2b0e7d03
EM
859 * Return a unique identifier (usually an email address or username) from the UserFramework / CMS system.
860 *
861 * This is based on the user object passed, defaulting to the logged in user if not passed.
862 *
863 * Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would seem to be
864 * resolving the unique identifier before calling the function.
2b617cb0
EM
865 *
866 * @param object $user
2b0e7d03 867 *
a6c01b45
CW
868 * @return string
869 * unique identifier from the UF System
2b617cb0 870 */
00be9182 871 public function getBestUFUniqueIdentifier($user = NULL) {
22e263ad 872 if ($user) {
2b617cb0
EM
873 return $this->getUniqueIdentifierFromUserObject($user);
874 }
875 return $this->getLoggedInUniqueIdentifier();
876 }
59f97da6 877
66e42142
CW
878 /**
879 * List modules installed in the CMS, including enabled and disabled ones.
880 *
881 * @return array
882 * [CRM_Core_Module]
883 */
884 public function getModules() {
be2fb01f 885 return [];
66e42142
CW
886 }
887
59f97da6 888 /**
fe482240 889 * Get Url to view user record.
2b0e7d03 890 *
77855840
TO
891 * @param int $contactID
892 * Contact ID.
59f97da6 893 *
17f443df 894 * @return string|null
59f97da6 895 */
00be9182 896 public function getUserRecordUrl($contactID) {
59f97da6
EM
897 return NULL;
898 }
353ffa53 899
59f97da6 900 /**
fe482240 901 * Is the current user permitted to add a user.
2b0e7d03 902 *
59f97da6
EM
903 * @return bool
904 */
00be9182 905 public function checkPermissionAddUser() {
59f97da6
EM
906 return FALSE;
907 }
f85b1d20
EM
908
909 /**
fe482240 910 * Output code from error function.
2b0e7d03 911 *
f85b1d20
EM
912 * @param string $content
913 */
00be9182 914 public function outputError($content) {
f85b1d20
EM
915 echo CRM_Utils_System::theme($content);
916 }
e0dd98a5
EM
917
918 /**
fe482240 919 * Log error to CMS.
17f443df 920 *
ad37ac8e 921 * @param string $message
e0dd98a5 922 */
00be9182 923 public function logger($message) {
e0dd98a5 924 }
f9f361d0
CW
925
926 /**
fe482240 927 * Append to coreResourcesList.
17f443df 928 *
303017a1 929 * @param \Civi\Core\Event\GenericHookEvent $e
f9f361d0 930 */
303017a1 931 public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
e7292422 932 }
96025800 933
62c20d1e
CW
934 /**
935 * Modify dynamic assets.
936 *
937 * @param \Civi\Core\Event\GenericHookEvent $e
938 */
939 public function alterAssetUrl(\Civi\Core\Event\GenericHookEvent $e) {
940 }
941
d42a224c
CW
942 /**
943 * @param string $name
944 * @param string $value
945 */
946 public function setHttpHeader($name, $value) {
947 header("$name: $value");
948 }
949
03d5592a
CW
950 /**
951 * Create CRM contacts for all existing CMS users
952 *
953 * @return array
954 * @throws \Exception
955 */
956 public function synchronizeUsers() {
957 throw new Exception('CMS user creation not supported for this framework');
be2fb01f 958 return [];
03d5592a
CW
959 }
960
79dd7fe9 961 /**
46dddc5c
SL
962 * Send an HTTP Response base on PSR HTTP RespnseInterface response.
963 *
964 * @param \Psr\Http\Message\ResponseInterface $response
79dd7fe9 965 */
46dddc5c 966 public function sendResponse(\Psr\Http\Message\ResponseInterface $response) {
fcfcd0f7 967 http_response_code($response->getStatusCode());
90f6c8df
SL
968 foreach ($response->getHeaders() as $name => $values) {
969 CRM_Utils_System::setHttpHeader($name, implode(', ', (array) $values));
79dd7fe9 970 }
46dddc5c
SL
971 echo $response->getBody();
972 CRM_Utils_System::civiExit();
79dd7fe9
SL
973 }
974
6a488035 975}