Merge pull request #3223 from lcdservices/CRM-14672
[civicrm-core.git] / CRM / Utils / System / WordPress.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 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 /**
37 * WordPress specific stuff goes here
38 */
39 class CRM_Utils_System_WordPress extends CRM_Utils_System_Base {
40 function __construct() {
41 $this->is_drupal = FALSE;
42 }
43
44 /**
45 * sets the title of the page
46 *
47 * @param string $title
48 * @param null $pageTitle
49 *
50 * @paqram string $pageTitle
51 *
52 * @return void
53 * @access public
54 */
55 function setTitle($title, $pageTitle = NULL) {
56 if (!$pageTitle) {
57 $pageTitle = $title;
58 }
59 if (civicrm_wp_in_civicrm()) {
60 global $civicrm_wp_title;
61 $civicrm_wp_title = $pageTitle;
62 $template = CRM_Core_Smarty::singleton();
63 $template->assign('pageTitle', $pageTitle);
64 }
65 }
66
67 /**
68 * Append an additional breadcrumb tag to the existing breadcrumb
69 *
70 * @param $breadCrumbs
71 *
72 * @internal param string $title
73 * @internal param string $url
74 *
75 * @return void
76 * @access public
77 * @static
78 */
79 function appendBreadCrumb($breadCrumbs) {
80 $breadCrumb = wp_get_breadcrumb();
81
82 if (is_array($breadCrumbs)) {
83 foreach ($breadCrumbs as $crumbs) {
84 if (stripos($crumbs['url'], 'id%%')) {
85 $args = array('cid', 'mid');
86 foreach ($args as $a) {
87 $val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject,
88 FALSE, NULL, $_GET
89 );
90 if ($val) {
91 $crumbs['url'] = str_ireplace("%%{$a}%%", $val, $crumbs['url']);
92 }
93 }
94 }
95 $breadCrumb[] = "<a href=\"{$crumbs['url']}\">{$crumbs['title']}</a>";
96 }
97 }
98
99 $template = CRM_Core_Smarty::singleton();
100 $template->assign_by_ref('breadcrumb', $breadCrumb);
101 wp_set_breadcrumb($breadCrumb);
102 }
103
104 /**
105 * Reset an additional breadcrumb tag to the existing breadcrumb
106 *
107 * @return void
108 * @access public
109 * @static
110 */
111 function resetBreadCrumb() {
112 $bc = array();
113 wp_set_breadcrumb($bc);
114 }
115
116 /**
117 * Append a string to the head of the html file
118 *
119 * @param string $head the new string to be appended
120 *
121 * @return void
122 * @access public
123 * @static
124 */
125 function addHTMLHead($head) {
126 static $registered = FALSE;
127 if (!$registered) {
128 // front-end view
129 add_action('wp_head', array(__CLASS__, '_showHTMLHead'));
130 // back-end views
131 add_action('admin_head', array(__CLASS__, '_showHTMLHead'));
132 }
133 CRM_Core_Region::instance('wp_head')->add(array(
134 'markup' => $head,
135 ));
136 }
137
138 static function _showHTMLHead() {
139 $region = CRM_Core_Region::instance('wp_head', FALSE);
140 if ($region) {
141 echo $region->render('');
142 }
143 }
144
145 /**
146 * Add a script file
147 *
148 * @param $url: string, absolute path to file
149 * @param $region string, location within the document: 'html-header', 'page-header', 'page-footer'
150 *
151 * Note: This function is not to be called directly
152 * @see CRM_Core_Region::render()
153 *
154 * @return bool TRUE if we support this operation in this CMS, FALSE otherwise
155 * @access public
156 */
157 public function addScriptUrl($url, $region) {
158 return FALSE;
159 }
160
161 /**
162 * Add an inline script
163 *
164 * @param $code: string, javascript code
165 * @param $region string, location within the document: 'html-header', 'page-header', 'page-footer'
166 *
167 * Note: This function is not to be called directly
168 * @see CRM_Core_Region::render()
169 *
170 * @return bool TRUE if we support this operation in this CMS, FALSE otherwise
171 * @access public
172 */
173 public function addScript($code, $region) {
174 return FALSE;
175 }
176
177 /**
178 * Add a css file
179 *
180 * @param $url: string, absolute path to file
181 * @param $region string, location within the document: 'html-header', 'page-header', 'page-footer'
182 *
183 * Note: This function is not to be called directly
184 * @see CRM_Core_Region::render()
185 *
186 * @return bool TRUE if we support this operation in this CMS, FALSE otherwise
187 * @access public
188 */
189 public function addStyleUrl($url, $region) {
190 return FALSE;
191 }
192
193 /**
194 * Add an inline style
195 *
196 * @param $code: string, css code
197 * @param $region string, location within the document: 'html-header', 'page-header', 'page-footer'
198 *
199 * Note: This function is not to be called directly
200 * @see CRM_Core_Region::render()
201 *
202 * @return bool TRUE if we support this operation in this CMS, FALSE otherwise
203 * @access public
204 */
205 public function addStyle($code, $region) {
206 return FALSE;
207 }
208
209 /**
210 * rewrite various system urls to https
211 *
212 * @param null
213 *
214 * @return void
215 * @access public
216 * @static
217 */
218 function mapConfigToSSL() {
219 global $base_url;
220 $base_url = str_replace('http://', 'https://', $base_url);
221 }
222
223 /**
224 * figure out the post url for the form
225 *
226 * @param mix $action the default action if one is pre-specified
227 *
228 * @return string the url to post the form
229 * @access public
230 * @static
231 */
232 function postURL($action) {
233 if (!empty($action)) {
234 return $action;
235 }
236
237 return $this->url($_GET['q'], NULL, TRUE, NULL, FALSE);
238 }
239
240 /**
241 * Generate an internal CiviCRM URL (copied from DRUPAL/includes/common.inc#url)
242 *
243 * @param $path string The path being linked to, such as "civicrm/add"
244 * @param $query string A query string to append to the link.
245 * @param $absolute boolean Whether to force the output to be an absolute link (beginning with http:).
246 * Useful for links that will be displayed outside the site, such as in an
247 * RSS feed.
248 * @param $fragment string A fragment identifier (named anchor) to append to the link.
249 * @param $htmlize boolean whether to convert to html eqivalant
250 * @param $frontend boolean a gross joomla hack
251 *
252 * @param bool $forceBackend
253 *
254 * @return string an HTML string containing a link to the given path.
255 * @access public
256 */
257 function url(
258 $path = NULL,
259 $query = NULL,
260 $absolute = FALSE,
261 $fragment = NULL,
262 $htmlize = TRUE,
263 $frontend = FALSE,
264 $forceBackend = FALSE
265 ) {
266 $config = CRM_Core_Config::singleton();
267 $script = '';
268 $separator = $htmlize ? '&amp;' : '&';
269 $wpPageParam = '';
270 $fragment = isset($fragment) ? ('#' . $fragment) : '';
271
272 $path = CRM_Utils_String::stripPathChars($path);
273
274 //this means wp function we are trying to use is not available,
275 //so load bootStrap
276 if (!function_exists('get_option')) {
277 $this->loadBootStrap(); // FIXME: Why bootstrap in url()? Generally want to define 1-2 strategic places to put bootstrap
278 }
279 if ($config->userFrameworkFrontend) {
280 if (get_option('permalink_structure') != '') {
281 global $post;
282 $script = get_permalink($post->ID);
283 }
284
285 // when shortcode is included in page
286 // also make sure we have valid query object
287 global $wp_query;
288 if ( method_exists( $wp_query, 'get' ) ) {
289 if (get_query_var('page_id')) {
290 $wpPageParam = "page_id=" . get_query_var('page_id');
291 }
292 elseif (get_query_var('p')) {
293 // when shortcode is inserted in post
294 $wpPageParam = "p=" . get_query_var('p');
295 }
296 }
297 }
298
299 $base = $this->getBaseUrl($absolute, $frontend, $forceBackend);
300
301 if (!isset($path) && !isset($query)) {
302 // FIXME: This short-circuited codepath is the same as the general one below, except
303 // in that it ignores "permlink_structure" / $wpPageParam / $script . I don't know
304 // why it's different (and I can only find two obvious use-cases for this codepath,
305 // of which at least one looks gratuitous). A more ambitious person would simply remove
306 // this code.
307 return $base . $fragment;
308 }
309
310 if (!$forceBackend && get_option('permalink_structure') != '' && ($wpPageParam || $script != '')) {
311 $base = $script;
312 }
313
314 $queryParts = array();
315 if (isset($path)) {
316 $queryParts[] = 'page=CiviCRM';
317 $queryParts[] = "q={$path}";
318 }
319 if ($wpPageParam) {
320 $queryParts[] = $wpPageParam;
321 }
322 if (isset($query)) {
323 $queryParts[] = $query;
324 }
325
326 return $base . '?' . implode($separator, $queryParts) . $fragment;
327 }
328
329 private function getBaseUrl($absolute, $frontend, $forceBackend) {
330 $config = CRM_Core_Config::singleton();
331
332 if (!isset($config->useFrameworkRelativeBase)) {
333 $base = parse_url($config->userFrameworkBaseURL);
334 $config->useFrameworkRelativeBase = $base['path'];
335 }
336
337 $base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase;
338
339 if ((is_admin() && !$frontend) || $forceBackend) {
340 $base .= 'wp-admin/admin.php';
341 return $base;
342 }
343 elseif (defined('CIVICRM_UF_WP_BASEPAGE')) {
344 $base .= CIVICRM_UF_WP_BASEPAGE;
345 return $base;
346 }
347 elseif (isset($config->wpBasePage)) {
348 $base .= $config->wpBasePage;
349 return $base;
350 }
351 return $base;
352 }
353
354 /**
355 * Authenticate the user against the wordpress db
356 *
357 * @param string $name the user name
358 * @param string $password the password for the above user name
359 *
360 * @param bool $loadCMSBootstrap
361 * @param null $realPath
362 *
363 * @return mixed false if no auth
364 * array(
365 * contactID, ufID, unique string ) if success
366 * @access public
367 * @static
368 */
369 function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
370 $config = CRM_Core_Config::singleton();
371
372 if ($loadCMSBootstrap) {
373 $config->userSystem->loadBootStrap($name, $password);
374 }
375
376 $user = wp_authenticate($name, $password);
377 if (is_a($user, 'WP_Error')) {
378 return FALSE;
379 }
380
381 // need to change this to make sure we matched only one row
382
383 CRM_Core_BAO_UFMatch::synchronizeUFMatch($user->data, $user->data->ID, $user->data->user_email, 'WordPress');
384 $contactID = CRM_Core_BAO_UFMatch::getContactId($user->data->ID);
385 if (!$contactID) {
386 return FALSE;
387 }
388 return array($contactID, $user->data->ID, mt_rand());
389 }
390
391 /**
392 * Set a message in the UF to display to a user
393 *
394 * @param string $message the message to set
395 *
396 * @access public
397 * @static
398 */
399 function setMessage($message) {
400 }
401
402 function loadUser( $user ) {
403 return true;
404 }
405
406 function permissionDenied() {
407 CRM_Core_Error::fatal(ts('You do not have permission to access this page'));
408 }
409
410 function logout() {
411 // destroy session
412 if (session_id()) {
413 session_destroy();
414 }
415 wp_logout();
416 wp_redirect(wp_login_url());
417 }
418
419 function updateCategories() {}
420
421 /**
422 * Get the locale set in the hosting CMS
423 *
424 * @return string with the locale or null for none
425 */
426 function getUFLocale() {
427 // WPML plugin
428 if (defined('ICL_LANGUAGE_CODE')) {
429 $language = ICL_LANGUAGE_CODE;
430 }
431
432 // TODO: set language variable for others WordPress plugin
433
434 if (isset($language)) {
435 return CRM_Core_I18n_PseudoConstant::longForShort(substr($language, 0, 2));
436 } else {
437 return NULL;
438 }
439 }
440
441 /**
442 * load wordpress bootstrap
443 *
444 * @param $name string optional username for login
445 * @param $pass string optional password for login
446 *
447 * @return bool
448 */
449 function loadBootStrap($name = NULL, $pass = NULL) {
450 global $wp, $wp_rewrite, $wp_the_query, $wp_query, $wpdb;
451
452 $cmsRootPath = $this->cmsRootPath();
453 if (!$cmsRootPath) {
454 CRM_Core_Error::fatal("Could not find the install directory for WordPress");
455 }
456
457 require_once ($cmsRootPath . DIRECTORY_SEPARATOR . 'wp-load.php');
458 $wpUserTimezone = get_option('timezone_string');
459 if ($wpUserTimezone) {
460 date_default_timezone_set($wpUserTimezone);
461 CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
462 }
463 return true;
464 }
465
466 function validInstallDir($dir) {
467 $includePath = "$dir/wp-includes";
468 if (
469 @opendir($includePath) &&
470 file_exists("$includePath/version.php")
471 ) {
472 return TRUE;
473 }
474 return FALSE;
475 }
476
477 function cmsRootPath() {
478 $cmsRoot = $valid = NULL;
479 if (defined('CIVICRM_CMSDIR')) {
480 if ($this->validInstallDir(CIVICRM_CMSDIR)) {
481 $cmsRoot = CIVICRM_CMSDIR;
482 $valid = TRUE;
483 }
484 }
485 else {
486 $pathVars = explode('/', str_replace('\\', '/', $_SERVER['SCRIPT_FILENAME']));
487
488 //might be windows installation.
489 $firstVar = array_shift($pathVars);
490 if ($firstVar) {
491 $cmsRoot = $firstVar;
492 }
493
494 //start w/ csm dir search.
495 foreach ($pathVars as $var) {
496 $cmsRoot .= "/$var";
497 if ($this->validInstallDir($cmsRoot)) {
498 //stop as we found bootstrap.
499 $valid = TRUE;
500 break;
501 }
502 }
503 }
504
505 return ($valid) ? $cmsRoot : NULL;
506 }
507
508 function createUser(&$params, $mail) {
509 $user_data = array(
510 'ID' => '',
511 'user_pass' => $params['cms_pass'],
512 'user_login' => $params['cms_name'],
513 'user_email' => $params[$mail],
514 'nickname' => $params['cms_name'],
515 'role' => get_option('default_role'),
516 );
517 if (isset($params['contactID'])) {
518 $contactType = CRM_Contact_BAO_Contact::getContactType($params['contactID']);
519 if ($contactType == 'Individual') {
520 $user_data['first_name'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
521 $params['contactID'], 'first_name'
522 );
523 $user_data['last_name'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
524 $params['contactID'], 'last_name'
525 );
526 }
527 }
528
529 $uid = wp_insert_user($user_data);
530
531 $creds = array();
532 $creds['user_login'] = $params['cms_name'];
533 $creds['user_password'] = $params['cms_pass'];
534 $creds['remember'] = TRUE;
535 $user = wp_signon($creds, FALSE);
536
537 wp_new_user_notification($uid, $user_data['user_pass']);
538 return $uid;
539 }
540
541 /**
542 * Change user name in host CMS
543 *
544 * @param integer $ufID User ID in CMS
545 * @param string $ufName User name
546 */
547 function updateCMSName($ufID, $ufName) {
548 // CRM-10620
549 if (function_exists('wp_update_user')) {
550 $ufID = CRM_Utils_Type::escape($ufID, 'Integer');
551 $ufName = CRM_Utils_Type::escape($ufName, 'String');
552
553 $values = array ('ID' => $ufID, 'user_email' => $ufName);
554 if( $ufID ) {
555 wp_update_user( $values ) ;
556 }
557 }
558 }
559
560 function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
561 $config = CRM_Core_Config::singleton();
562
563 $dao = new CRM_Core_DAO();
564 $name = $dao->escape(CRM_Utils_Array::value('name', $params));
565 $email = $dao->escape(CRM_Utils_Array::value('mail', $params));
566
567 if (!empty($params['name'])) {
568 if (!validate_username($params['name'])) {
569 $errors['cms_name'] = ts("Your username contains invalid characters");
570 }
571 elseif (username_exists(sanitize_user($params['name']))) {
572 $errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', array(1 => $params['name']));
573 }
574 }
575
576 if (!empty($params['mail'])) {
577 if (!is_email($params['mail'])) {
578 $errors[$emailName] = "Your email is invaid";
579 }
580 elseif (email_exists($params['mail'])) {
581 $resetUrl = $config->userFrameworkBaseURL . 'wp-login.php?action=lostpassword';
582 $errors[$emailName] = ts('The email address %1 is already registered. <a href="%2">Have you forgotten your password?</a>',
583 array(1 => $params['mail'], 2 => $resetUrl)
584 );
585 }
586 }
587 }
588
589 /**
590 * check is user logged in.
591 *
592 * @return boolean true/false.
593 */
594 public function isUserLoggedIn() {
595 $isloggedIn = FALSE;
596 if (function_exists('is_user_logged_in')) {
597 $isloggedIn = is_user_logged_in();
598 }
599
600 return $isloggedIn;
601 }
602
603 function getLoggedInUserObject() {
604 if (function_exists('is_user_logged_in') &&
605 is_user_logged_in()) {
606 global $current_user;
607 }
608 return $current_user;
609 }
610 /**
611 * Get currently logged in user uf id.
612 *
613 * @return int $userID logged in user uf id.
614 */
615 public function getLoggedInUfID() {
616 $ufID = NULL;
617 $current_user = $this->getLoggedInUserObject();
618 return isset($current_user->ID) ? $current_user->ID : NULL;
619 }
620
621 /**
622 * Get currently logged in user unique identifier - this tends to be the email address or user name.
623 *
624 * @return string $userID logged in user unique identifier
625 */
626 function getLoggedInUniqueIdentifier() {
627 $user = $this->getLoggedInUserObject();
628 return $this->getUniqueIdentifierFromUserObject($user);
629 }
630
631 /**
632 * Get User ID from UserFramework system (Joomla)
633 * @param object $user object as described by the CMS
634 * @return mixed <NULL, number>
635 */
636 function getUserIDFromUserObject($user) {
637 return !empty($user->ID) ? $user->ID : NULL;
638 }
639
640 /**
641 * Get Unique Identifier from UserFramework system (CMS)
642 * @param object $user object as described by the User Framework
643 * @return mixed $uniqueIdentifer Unique identifier from the user Framework system
644 *
645 */
646 function getUniqueIdentifierFromUserObject($user) {
647 return empty($user->user_email) ? NULL : $user->user_email;
648 }
649
650 /**
651 * Get user login URL for hosting CMS (method declared in each CMS system class)
652 *
653 * @param string $destination - if present, add destination to querystring (works for Drupal only)
654 *
655 * @return string - loginURL for the current CMS
656 *
657 */
658 public function getLoginURL($destination = '') {
659 $config = CRM_Core_Config::singleton();
660 $loginURL = $config->userFrameworkBaseURL;
661 $loginURL .= 'wp-login.php';
662 return $loginURL;
663 }
664
665 public function getLoginDestination(&$form) {
666 return;
667 }
668
669 /**
670 * Return the current WordPress version if relevant function exists
671 *
672 * @return string - version number
673 *
674 */
675 function getVersion() {
676 if (function_exists('get_bloginfo')) {
677 return get_bloginfo('version', 'display');
678 }
679 else {
680 return 'Unknown';
681 }
682 }
683
684 /**
685 * get timezone as a string
686 * @return string Timezone e.g. 'America/Los_Angeles'
687 */
688 function getTimeZoneString() {
689 return get_option('timezone_string');
690 }
691 }
692