First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,153 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Base class for writing API_Wrappers which generically manipulate the content
* of all fields (except for some black-listed skip-fields).
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'api/Wrapper.php';
/**
* Class CRM_Utils_API_AbstractFieldCoder.
*/
abstract class CRM_Utils_API_AbstractFieldCoder implements API_Wrapper {
/**
* Get skipped fields.
*
* @return array<string>
* List of field names
*/
public function getSkipFields() {
return NULL;
}
/**
* Is field skipped.
*
* @param string $fldName
*
* @return bool
* TRUE if encoding should be skipped for this field
*/
public function isSkippedField($fldName) {
$skipFields = $this->getSkipFields();
if ($skipFields === NULL) {
return FALSE;
}
// Field should be skipped
if (in_array($fldName, $skipFields)) {
return TRUE;
}
// Field is multilingual and after cutting off _xx_YY should be skipped (CRM-7230)…
if ((preg_match('/_[a-z][a-z]_[A-Z][A-Z]$/', $fldName) && in_array(substr($fldName, 0, -6), $skipFields))) {
return TRUE;
}
// Field can take multiple entries, eg. fieldName[1], fieldName[2], etc.
// We remove the index and check again if the fieldName in the list of skipped fields.
$matches = array();
if (preg_match('/^(.*)\[\d+\]/', $fldName, $matches) && in_array($matches[1], $skipFields)) {
return TRUE;
}
return FALSE;
}
/**
* Going to filter the submitted values.
*
* @param array|string $values the field value from the API
*/
public abstract function encodeInput(&$values);
/**
* Decode output.
*
* @param string $values
*
* @return mixed
*/
public abstract function decodeOutput(&$values);
/**
* @inheritDoc
*/
public function fromApiInput($apiRequest) {
$lowerAction = strtolower($apiRequest['action']);
if ($apiRequest['version'] == 3 && in_array($lowerAction, array('get', 'create'))) {
// note: 'getsingle', 'replace', 'update', and chaining all build on top of 'get'/'create'
foreach ($apiRequest['params'] as $key => $value) {
// Don't apply escaping to API control parameters (e.g. 'api.foo' or 'options.foo')
// and don't apply to other skippable fields
if (!$this->isApiControlField($key) && !$this->isSkippedField($key)) {
$this->encodeInput($apiRequest['params'][$key]);
}
}
}
elseif ($apiRequest['version'] == 3 && $lowerAction == 'setvalue') {
if (isset($apiRequest['params']['field']) && isset($apiRequest['params']['value'])) {
if (!$this->isSkippedField($apiRequest['params']['field'])) {
$this->encodeInput($apiRequest['params']['value']);
}
}
}
return $apiRequest;
}
/**
* @inheritDoc
*/
public function toApiOutput($apiRequest, $result) {
$lowerAction = strtolower($apiRequest['action']);
if ($apiRequest['version'] == 3 && in_array($lowerAction, array('get', 'create', 'setvalue', 'getquick'))) {
foreach ($result as $key => $value) {
// Don't apply escaping to API control parameters (e.g. 'api.foo' or 'options.foo')
// and don't apply to other skippable fields
if (!$this->isApiControlField($key) && !$this->isSkippedField($key)) {
$this->decodeOutput($result[$key]);
}
}
}
// setvalue?
return $result;
}
/**
* @param $key
*
* @return bool
*/
protected function isApiControlField($key) {
return (FALSE !== strpos($key, '.'));
}
}

View file

@ -0,0 +1,153 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* This class captures the encoding practices of CRM-5667 in a reusable
* fashion. In this design, all submitted values are partially HTML-encoded
* before saving to the database. If a DB reader needs to output in
* non-HTML medium, then it should undo the partial HTML encoding.
*
* This class should be short-lived -- 4.3 should introduce an alternative
* escaping scheme and consequently remove HTMLInputCoder.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_API_HTMLInputCoder extends CRM_Utils_API_AbstractFieldCoder {
private $skipFields = NULL;
/**
* @var CRM_Utils_API_HTMLInputCoder
*/
private static $_singleton = NULL;
/**
* @return CRM_Utils_API_HTMLInputCoder
*/
public static function singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_API_HTMLInputCoder();
}
return self::$_singleton;
}
/**
* Get skipped fields.
*
* @return array<string>
* list of field names
*/
public function getSkipFields() {
if ($this->skipFields === NULL) {
$this->skipFields = array(
'widget_code',
'html_message',
'body_html',
'msg_html',
'description',
'intro',
'thankyou_text',
'tf_thankyou_text',
'intro_text',
'page_text',
'body_text',
'footer_text',
'thankyou_footer',
'thankyou_footer_text',
'new_text',
'renewal_text',
'help_pre',
'help_post',
'confirm_title',
'confirm_text',
'confirm_footer_text',
'confirm_email_text',
'event_full_text',
'waitlist_text',
'approval_req_text',
'report_header',
'report_footer',
'cc_id',
'bcc_id',
'premiums_intro_text',
'honor_block_text',
'pay_later_text',
'pay_later_receipt',
'label', // This is needed for FROM Email Address configuration. dgg
'url', // This is needed for navigation items urls
'details',
'msg_text', // message templates text versions
'text_message', // (send an) email to contacts and CiviMails text version
'data', // data i/p of persistent table
'sqlQuery', // CRM-6673
'pcp_title',
'pcp_intro_text',
'new', // The 'new' text in word replacements
'replyto_email', // e.g. '"Full Name" <user@example.org>'
'operator',
'content', // CRM-20468
);
}
return $this->skipFields;
}
/**
* going to filter the
* submitted values across XSS vulnerability.
*
* @param array|string $values
* @param bool $castToString
* If TRUE, all scalars will be filtered (and therefore cast to strings).
* If FALSE, then non-string values will be preserved
*/
public function encodeInput(&$values, $castToString = FALSE) {
if (is_array($values)) {
foreach ($values as &$value) {
$this->encodeInput($value, TRUE);
}
}
elseif ($castToString || is_string($values)) {
$values = str_replace(array('<', '>'), array('&lt;', '&gt;'), $values);
}
}
/**
* @param array $values
* @param bool $castToString
*/
public function decodeOutput(&$values, $castToString = FALSE) {
if (is_array($values)) {
foreach ($values as &$value) {
$this->decodeOutput($value, TRUE);
}
}
elseif ($castToString || is_string($values)) {
$values = str_replace(array('&lt;', '&gt;'), array('<', '>'), $values);
}
}
}

View file

@ -0,0 +1,194 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Implement the "match" and "match-mandatory" options. If the submitted record doesn't have an ID
* but a "match" key is specified, then we will automatically search for pre-existing record and
* fill-in the missing ID. The "match" or "match-mandatory" can specified as a string (the name of the key
* to match on) or array (the names of several keys to match on).
*
* Note that "match" and "match-mandatory" behave the same in the case where one matching record
* exists (ie they update the record). They also behave the same if there are multiple matching
* records (ie they throw an error). However, if there is no matching record, they differ:
* - "match-mandatory" will generate an error
* - "match" will allow action to proceed -- thus inserting a new record
*
* @code
* $result = civicrm_api('contact', 'create', array(
* 'options' => array(
* 'match' => array('last_name', 'first_name')
* ),
* 'first_name' => 'Jeffrey',
* 'last_name' => 'Lebowski',
* 'nick_name' => 'The Dude',
* ));
* @endcode
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'api/Wrapper.php';
/**
* Class CRM_Utils_API_MatchOption
*/
class CRM_Utils_API_MatchOption implements API_Wrapper {
/**
* @var CRM_Utils_API_MatchOption
*/
private static $_singleton = NULL;
/**
* Singleton function.
*
* @return CRM_Utils_API_MatchOption
*/
public static function singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_API_MatchOption();
}
return self::$_singleton;
}
/**
* @inheritDoc
*/
public function fromApiInput($apiRequest) {
// Parse options.match or options.match-mandatory
$keys = NULL;
if (isset($apiRequest['params'], $apiRequest['params']['options']) && is_array($apiRequest['params']['options'])) {
if (isset($apiRequest['params']['options']['match-mandatory'])) {
$isMandatory = TRUE;
$keys = $apiRequest['params']['options']['match-mandatory'];
}
elseif (isset($apiRequest['params']['options']['match'])) {
$isMandatory = FALSE;
$keys = $apiRequest['params']['options']['match'];
}
if (is_string($keys)) {
$keys = array($keys);
}
}
// If one of the options was specified, then try to match records.
// Matching logic differs for 'create' and 'replace' actions.
if ($keys !== NULL) {
switch ($apiRequest['action']) {
case 'create':
if (empty($apiRequest['params']['id'])) {
$apiRequest['params'] = $this->match($apiRequest['entity'], $apiRequest['params'], $keys, $isMandatory);
}
break;
case 'replace':
// In addition to matching on the listed keys, also match on the set-definition keys.
// For example, if the $apiRequest is to "replace the set of civicrm_emails for contact_id=123 while
// matching emails on location_type_id", then we would need to search for pre-existing emails using
// both 'contact_id' and 'location_type_id'
$baseParams = _civicrm_api3_generic_replace_base_params($apiRequest['params']);
$keys = array_unique(array_merge(
array_keys($baseParams),
$keys
));
// attempt to match each replacement item
foreach ($apiRequest['params']['values'] as $offset => $createParams) {
$createParams = array_merge($baseParams, $createParams);
$createParams = $this->match($apiRequest['entity'], $createParams, $keys, $isMandatory);
$apiRequest['params']['values'][$offset] = $createParams;
}
break;
default:
// be forgiving of sloppy api calls
}
}
return $apiRequest;
}
/**
* Attempt to match a contact. This filters/updates the $createParams if there is a match.
*
* @param string $entity
* @param array $createParams
* @param array $keys
* @param bool $isMandatory
*
* @return array
* revised $createParams, including 'id' if known
* @throws API_Exception
*/
public function match($entity, $createParams, $keys, $isMandatory) {
$getParams = $this->createGetParams($createParams, $keys);
$getResult = civicrm_api3($entity, 'get', $getParams);
if ($getResult['count'] == 0) {
if ($isMandatory) {
throw new API_Exception("Failed to match existing record");
}
return $createParams; // OK, don't care
}
elseif ($getResult['count'] == 1) {
$item = array_shift($getResult['values']);
$createParams['id'] = $item['id'];
return $createParams;
}
else {
throw new API_Exception("Ambiguous match criteria");
}
}
/**
* @inheritDoc
*/
public function toApiOutput($apiRequest, $result) {
return $result;
}
/**
* Create APIv3 "get" parameters to lookup an existing record using $keys
*
* @param array $origParams
* Api request.
* @param array $keys
* List of keys to match against.
*
* @return array
* APIv3 $params
*/
public function createGetParams($origParams, $keys) {
$params = array('version' => 3);
foreach ($keys as $key) {
$params[$key] = CRM_Utils_Array::value($key, $origParams, '');
}
return $params;
}
}

View file

@ -0,0 +1,103 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Work-around for CRM-13120 - The "create" action incorrectly returns string literal "null"
* when the actual value is NULL or "". Rewrite the output.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'api/Wrapper.php';
/**
* Class CRM_Utils_API_NullOutputCoder
*/
class CRM_Utils_API_NullOutputCoder extends CRM_Utils_API_AbstractFieldCoder {
/**
* @var CRM_Utils_API_NullOutputCoder
*/
private static $_singleton = NULL;
/**
* @return CRM_Utils_API_NullOutputCoder
*/
public static function singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_API_NullOutputCoder();
}
return self::$_singleton;
}
/**
* Going to filter the submitted values across XSS vulnerability.
*
* @param array|string $values
*/
public function encodeInput(&$values) {
}
/**
* Decode output.
*
* @param array $values
* @param bool $castToString
*/
public function decodeOutput(&$values, $castToString = FALSE) {
if (is_array($values)) {
foreach ($values as &$value) {
$this->decodeOutput($value, TRUE);
}
}
elseif ($castToString || is_string($values)) {
if ($values === 'null') {
$values = '';
}
}
}
/**
* To api output.
*
* @param array $apiRequest
* @param array $result
*
* @return array
*/
public function toApiOutput($apiRequest, $result) {
$lowerAction = strtolower($apiRequest['action']);
if ($lowerAction === 'create') {
return parent::toApiOutput($apiRequest, $result);
}
else {
return $result;
}
}
}

View file

@ -0,0 +1,133 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Implement the "reload" option. This option can be used with "create" to force
* the API to reload a clean copy of the entity before returning the result.
*
* @code
* $clean = civicrm_api('myentity', 'create', array(
* 'options' => array(
* 'reload' => 1
* ),
* ));
* @endcode
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'api/Wrapper.php';
/**
* Class CRM_Utils_API_ReloadOption
*/
class CRM_Utils_API_ReloadOption implements API_Wrapper {
/**
* @var CRM_Utils_API_ReloadOption
*/
private static $_singleton = NULL;
/**
* @return CRM_Utils_API_ReloadOption
*/
public static function singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_API_ReloadOption();
}
return self::$_singleton;
}
/**
* @inheritDoc
*/
public function fromApiInput($apiRequest) {
return $apiRequest;
}
/**
* @inheritDoc
*/
public function toApiOutput($apiRequest, $result) {
$reloadMode = NULL;
if ($apiRequest['action'] === 'create' && isset($apiRequest['params'], $apiRequest['params']['options']) && is_array($apiRequest['params']['options']) && isset($apiRequest['params']['options']['reload'])) {
if (!CRM_Utils_Array::value('is_error', $result, FALSE)) {
$reloadMode = $apiRequest['params']['options']['reload'];
}
$id = (!empty($apiRequest['params']['sequential'])) ? 0 : $result['id'];
}
switch ($reloadMode) {
case NULL:
case '0':
case 'null':
case '':
return $result;
case '1':
case 'default':
$params = array(
'id' => $result['id'],
);
$reloadResult = civicrm_api3($apiRequest['entity'], 'get', $params);
if ($reloadResult['is_error']) {
throw new API_Exception($reloadResult['error_message']);
}
$result['values'][$id] = array_merge($result['values'][$id], $reloadResult['values'][$result['id']]);
return $result;
case 'selected':
$params = array(
'id' => $id,
'return' => $this->pickReturnFields($apiRequest),
);
$reloadResult = civicrm_api3($apiRequest['entity'], 'get', $params);
$result['values'][$id] = array_merge($result['values'][$id], $reloadResult['values'][$id]);
return $result;
default:
throw new API_Exception("Unknown reload mode " . $reloadMode);
}
}
/**
* Identify the fields which should be returned.
*
* @param $apiRequest
* @return array
*/
public function pickReturnFields($apiRequest) {
$fields = civicrm_api3($apiRequest['entity'], 'getfields', array());
$returnKeys = array_intersect(
array_keys($apiRequest['params']),
array_keys($fields['values'])
);
return $returnKeys;
}
}

View file

@ -0,0 +1,381 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Address Utilities
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Address {
/**
* Format an address string from address fields and a format string.
*
* Format an address basing on the address fields provided.
* Use Setting's address_format if there's no format specified.
*
* @param array $fields
* The address fields.
* @param string $format
* The desired address format.
* @param bool $microformat
* If true indicates, the address to be built in hcard-microformat standard.
* @param bool $mailing
* If true indicates, the call has been made from mailing label.
* @param bool $individualFormat
* If true indicates, the call has been made for the contact of type 'individual'.
*
* @param null $tokenFields
*
* @return string
* formatted address string
*
*/
public static function format(
$fields,
$format = NULL,
$microformat = FALSE,
$mailing = FALSE,
$individualFormat = FALSE,
$tokenFields = NULL
) {
static $config = NULL;
if (!$format) {
$format = Civi::settings()->get('address_format');
}
if ($mailing) {
$format = Civi::settings()->get('mailing_format');
}
$formatted = $format;
$fullPostalCode = CRM_Utils_Array::value('postal_code', $fields);
if (!empty($fields['postal_code_suffix'])) {
$fullPostalCode .= "-$fields[postal_code_suffix]";
}
// make sure that some of the fields do have values
$emptyFields = array(
'supplemental_address_1',
'supplemental_address_2',
'supplemental_address_3',
'state_province_name',
'county',
);
foreach ($emptyFields as $f) {
if (!isset($fields[$f])) {
$fields[$f] = NULL;
}
}
//CRM-16876 Display countries in all caps when in mailing mode.
if ($mailing && !empty($fields['country'])) {
if (Civi::settings()->get('hideCountryMailingLabels')) {
$domain = CRM_Core_BAO_Domain::getDomain();
$domainLocation = CRM_Core_BAO_Location::getValues(array('contact_id' => $domain->contact_id));
$domainAddress = $domainLocation['address'][1];
$domainCountryId = $domainAddress['country_id'];
if ($fields['country'] == CRM_Core_PseudoConstant::country($domainCountryId)) {
$fields['country'] = NULL;
}
else {
//Capitalization display on uppercase to contries with special characters
$fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, "UTF-8");
}
}
else {
$fields['country'] = mb_convert_case($fields['country'], MB_CASE_UPPER, "UTF-8");
}
}
$contactName = CRM_Utils_Array::value('display_name', $fields);
if (!$individualFormat) {
if (isset($fields['id'])) {
$type = CRM_Contact_BAO_Contact::getContactType($fields['id']);
}
else {
$type = 'Individual';
}
if ($type == 'Individual') {
$contactName = CRM_Utils_Array::value('addressee_display', $fields);
}
}
if (!$microformat) {
// replacements in case of Individual Name Format
$replacements = array(
'contact.display_name' => CRM_Utils_Array::value('display_name', $fields),
'contact.individual_prefix' => CRM_Utils_Array::value('individual_prefix', $fields),
'contact.formal_title' => CRM_Utils_Array::value('formal_title', $fields),
'contact.first_name' => CRM_Utils_Array::value('first_name', $fields),
'contact.middle_name' => CRM_Utils_Array::value('middle_name', $fields),
'contact.last_name' => CRM_Utils_Array::value('last_name', $fields),
'contact.individual_suffix' => CRM_Utils_Array::value('individual_suffix', $fields),
'contact.address_name' => CRM_Utils_Array::value('address_name', $fields),
'contact.street_address' => CRM_Utils_Array::value('street_address', $fields),
'contact.supplemental_address_1' => CRM_Utils_Array::value('supplemental_address_1', $fields),
'contact.supplemental_address_2' => CRM_Utils_Array::value('supplemental_address_2', $fields),
'contact.supplemental_address_3' => CRM_Utils_Array::value('supplemental_address_3', $fields),
'contact.city' => CRM_Utils_Array::value('city', $fields),
'contact.state_province_name' => CRM_Utils_Array::value('state_province_name', $fields),
'contact.county' => CRM_Utils_Array::value('county', $fields),
'contact.state_province' => CRM_Utils_Array::value('state_province', $fields),
'contact.postal_code' => $fullPostalCode,
'contact.country' => CRM_Utils_Array::value('country', $fields),
'contact.world_region' => CRM_Utils_Array::value('world_region', $fields),
'contact.geo_code_1' => CRM_Utils_Array::value('geo_code_1', $fields),
'contact.geo_code_2' => CRM_Utils_Array::value('geo_code_2', $fields),
'contact.current_employer' => CRM_Utils_Array::value('current_employer', $fields),
'contact.nick_name' => CRM_Utils_Array::value('nick_name', $fields),
'contact.email' => CRM_Utils_Array::value('email', $fields),
'contact.im' => CRM_Utils_Array::value('im', $fields),
'contact.do_not_email' => CRM_Utils_Array::value('do_not_email', $fields),
'contact.do_not_phone' => CRM_Utils_Array::value('do_not_phone', $fields),
'contact.do_not_mail' => CRM_Utils_Array::value('do_not_mail', $fields),
'contact.do_not_sms' => CRM_Utils_Array::value('do_not_sms', $fields),
'contact.do_not_trade' => CRM_Utils_Array::value('do_not_trade', $fields),
'contact.job_title' => CRM_Utils_Array::value('job_title', $fields),
'contact.birth_date' => CRM_Utils_Array::value('birth_date', $fields),
'contact.gender' => CRM_Utils_Array::value('gender', $fields),
'contact.is_opt_out' => CRM_Utils_Array::value('is_opt_out', $fields),
'contact.preferred_mail_format' => CRM_Utils_Array::value('preferred_mail_format', $fields),
'contact.phone' => CRM_Utils_Array::value('phone', $fields),
'contact.home_URL' => CRM_Utils_Array::value('home_URL', $fields),
'contact.contact_source' => CRM_Utils_Array::value('contact_source', $fields),
'contact.external_identifier' => CRM_Utils_Array::value('external_identifier', $fields),
'contact.contact_id' => CRM_Utils_Array::value('id', $fields),
'contact.household_name' => CRM_Utils_Array::value('display_name', $fields),
'contact.organization_name' => CRM_Utils_Array::value('display_name', $fields),
'contact.legal_name' => CRM_Utils_Array::value('legal_name', $fields),
'contact.preferred_communication_method' => CRM_Utils_Array::value('preferred_communication_method', $fields),
'contact.communication_style' => CRM_Utils_Array::value('communication_style', $fields),
'contact.addressee' => CRM_Utils_Array::value('addressee_display', $fields),
'contact.email_greeting' => CRM_Utils_Array::value('email_greeting_display', $fields),
'contact.postal_greeting' => CRM_Utils_Array::value('postal_greeting_display', $fields),
);
}
else {
$replacements = array(
'contact.address_name' => "<span class=\"address-name\">" . $fields['address_name'] . "</span>",
'contact.street_address' => "<span class=\"street-address\">" . $fields['street_address'] . "</span>",
'contact.supplemental_address_1' => "<span class=\"extended-address\">" . $fields['supplemental_address_1'] . "</span>",
'contact.supplemental_address_2' => $fields['supplemental_address_2'],
'contact.supplemental_address_3' => $fields['supplemental_address_3'],
'contact.city' => "<span class=\"locality\">" . $fields['city'] . "</span>",
'contact.state_province_name' => "<span class=\"region\">" . $fields['state_province_name'] . "</span>",
'contact.county' => "<span class=\"region\">" . $fields['county'],
'contact.state_province' => "<span class=\"region\">" . $fields['state_province'] . "</span>",
'contact.postal_code' => "<span class=\"postal-code\">" . $fullPostalCode . "</span>",
'contact.country' => "<span class=\"country-name\">" . $fields['country'] . "</span>",
'contact.world_region' => "<span class=\"region\">" . $fields['world_region'] . "</span>",
);
// erase all empty ones, so we dont get blank lines
foreach (array_keys($replacements) as $key) {
$exactKey = substr($key, 0, 8) == 'contact.' ? substr($key, 8) : $key;
if ($key != 'contact.postal_code' &&
CRM_Utils_Array::value($exactKey, $fields) == NULL
) {
$replacements[$key] = '';
}
}
if (empty($fullPostalCode)) {
$replacements['contact.postal_code'] = '';
}
}
// replacements in case of Custom Token
if (stristr($formatted, 'custom_')) {
$customToken = array_keys($fields);
foreach ($customToken as $value) {
if (substr($value, 0, 7) == 'custom_') {
$replacements["contact.{$value}"] = $fields["{$value}"];
}
}
}
// also sub all token fields
if ($tokenFields) {
foreach ($tokenFields as $token) {
$replacements["{$token}"] = CRM_Utils_Array::value("{$token}", $fields);
}
}
// for every token, replace {fooTOKENbar} with fooVALUEbar if
// the value is not empty, otherwise drop the whole {fooTOKENbar}
foreach ($replacements as $token => $value) {
if ($value && is_string($value) || is_numeric($value)) {
$formatted = preg_replace("/{([^{}]*)\b{$token}\b([^{}]*)}/u", "\${1}{$value}\${2}", $formatted);
}
else {
$formatted = preg_replace("/{[^{}]*\b{$token}\b[^{}]*}/u", '', $formatted);
}
}
// drop any {...} constructs from lines' ends
if (!$microformat) {
$formatted = "\n$formatted\n";
}
else {
if ($microformat == 1) {
$formatted = "\n<div class=\"location vcard\"><span class=\"adr\">\n$formatted</span></div>\n";
}
else {
$formatted = "\n<div class=\"vcard\"><span class=\"adr\">$formatted</span></div>\n";
}
}
$formatted = preg_replace('/\n{[^{}]*}/u', "\n", $formatted);
$formatted = preg_replace('/{[^{}]*}\n/u', "\n", $formatted);
// if there are any 'sibling' {...} constructs, replace them with the
// contents of the first one; for example, when there's no state_province:
// 1. {city}{, }{state_province}{ }{postal_code}
// 2. San Francisco{, }{ }12345
// 3. San Francisco, 12345
$formatted = preg_replace('/{([^{}]*)}({[^{}]*})+/u', '\1', $formatted);
// drop any remaining curly braces leaving their contents
$formatted = str_replace(array('{', '}'), '', $formatted);
// drop any empty lines left after the replacements
$formatted = preg_replace('/^[ \t]*[\r\n]+/m', '', $formatted);
if (!$microformat) {
$finalFormatted = $formatted;
}
else {
// remove \n from each line and only add at the end
// this hack solves formatting issue, when we convert nl2br
$lines = array();
$count = 1;
$finalFormatted = NULL;
$formattedArray = explode("\n", $formatted);
$formattedArray = array_filter($formattedArray);
foreach ($formattedArray as $line) {
$line = trim($line);
if ($line) {
if ($count > 1 && $count < count($formattedArray)) {
$line = "$line\n";
}
$finalFormatted .= $line;
$count++;
}
}
}
return $finalFormatted;
}
/**
* @param $format
*
* @return array
*/
public static function sequence($format) {
// also compute and store the address sequence
$addressSequence = array(
'address_name',
'street_address',
'supplemental_address_1',
'supplemental_address_2',
'supplemental_address_3',
'city',
'county',
'state_province',
'postal_code',
'country',
);
// get the field sequence from the format
$newSequence = array();
foreach ($addressSequence as $field) {
if (substr_count($format, $field)) {
$newSequence[strpos($format, $field)] = $field;
}
}
ksort($newSequence);
// add the addressSequence fields that are missing in the addressFormat
// to the end of the list, so that (for example) if state_province is not
// specified in the addressFormat it's still in the address-editing form
$newSequence = array_merge($newSequence, $addressSequence);
$newSequence = array_unique($newSequence);
return $newSequence;
}
/**
* Extract the billing fields from the form submission and format them for display.
*
* @param array $params
* @param int $billingLocationTypeID
*
* @return string
*/
public static function getFormattedBillingAddressFieldsFromParameters($params, $billingLocationTypeID) {
$addressParts = array(
"street_address" => "billing_street_address-{$billingLocationTypeID}",
"city" => "billing_city-{$billingLocationTypeID}",
"postal_code" => "billing_postal_code-{$billingLocationTypeID}",
"state_province" => "state_province-{$billingLocationTypeID}",
"country" => "country-{$billingLocationTypeID}",
);
$addressFields = array();
foreach ($addressParts as $name => $field) {
$value = CRM_Utils_Array::value($field, $params);
$alternateName = 'billing_' . $name . '_id-' . $billingLocationTypeID;
$alternate2 = 'billing_' . $name . '-' . $billingLocationTypeID;
if (isset($params[$alternate2]) && !isset($params[$alternateName])) {
$alternateName = $alternate2;
}
//Include values which prepend 'billing_' to country and state_province.
if (CRM_Utils_Array::value($alternateName, $params)) {
if (empty($value) || !is_numeric($value)) {
$value = $params[$alternateName];
}
}
if (is_numeric($value) && ($name == 'state_province' || $name == 'country')) {
if ($name == 'state_province') {
$addressFields[$name] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($value);
}
if ($name == 'country') {
$addressFields[$name] = CRM_Core_PseudoConstant::countryIsoCode($value);
}
}
else {
$addressFields[$name] = $value;
}
}
return CRM_Utils_Address::format($addressFields);
}
}

View file

@ -0,0 +1,301 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* A PHP cron script to format all the addresses in the database. Currently
* it only does geocoding if the geocode values are not set. At a later
* stage we will also handle USPS address cleanup and other formatting
* issues
*/
class CRM_Utils_Address_BatchUpdate {
var $start = NULL;
var $end = NULL;
var $geocoding = 1;
var $parse = 1;
var $throttle = 0;
var $returnMessages = array();
var $returnError = 0;
/**
* Class constructor.
*
* @param array $params
*/
public function __construct($params) {
foreach ($params as $name => $value) {
$this->$name = $value;
}
// fixme: more params verification
}
/**
* Run batch update.
*
* @return array
*/
public function run() {
$config = &CRM_Core_Config::singleton();
// do check for geocoding.
$processGeocode = FALSE;
if (empty($config->geocodeMethod)) {
if (CRM_Utils_String::strtobool($this->geocoding) === TRUE) {
$this->returnMessages[] = ts('Error: You need to set a mapping provider under Administer > System Settings > Mapping and Geocoding');
$this->returnError = 1;
$this->returnResult();
}
}
else {
$processGeocode = TRUE;
// user might want to over-ride.
if (CRM_Utils_String::strtobool($this->geocoding) === FALSE) {
$processGeocode = FALSE;
}
}
// do check for parse street address.
$parseAddress = FALSE;
$parseAddress = CRM_Utils_Array::value('street_address_parsing',
CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'address_options'
),
FALSE
);
$parseStreetAddress = FALSE;
if (!$parseAddress) {
if (CRM_Utils_String::strtobool($this->parse) === TRUE) {
$this->returnMessages[] = ts('Error: You need to enable Street Address Parsing under Administer > Localization > Address Settings.');
$this->returnError = 1;
return $this->returnResult();
}
}
else {
$parseStreetAddress = TRUE;
// user might want to over-ride.
if (CRM_Utils_String::strtobool($this->parse) === FALSE) {
$parseStreetAddress = FALSE;
}
}
// don't process.
if (!$parseStreetAddress && !$processGeocode) {
$this->returnMessages[] = ts('Error: Both Geocode mapping as well as Street Address Parsing are disabled. You must configure one or both options to use this script.');
$this->returnError = 1;
return $this->returnResult();
}
// do check for parse street address.
return $this->processContacts($config, $processGeocode, $parseStreetAddress);
}
/**
* Process contacts.
*
* @param CRM_Core_Config $config
* @param bool $processGeocode
* @param bool $parseStreetAddress
*
* @return array
* @throws Exception
*/
public function processContacts(&$config, $processGeocode, $parseStreetAddress) {
// build where clause.
$clause = array('( c.id = a.contact_id )');
$params = array();
if ($this->start) {
$clause[] = "( c.id >= %1 )";
$params[1] = array($this->start, 'Integer');
}
if ($this->end) {
$clause[] = "( c.id <= %2 )";
$params[2] = array($this->end, 'Integer');
}
if ($processGeocode) {
$clause[] = '( a.geo_code_1 is null OR a.geo_code_1 = 0 )';
$clause[] = '( a.geo_code_2 is null OR a.geo_code_2 = 0 )';
$clause[] = '( a.country_id is not null )';
}
$whereClause = implode(' AND ', $clause);
$query = "
SELECT c.id,
a.id as address_id,
a.street_address,
a.city,
a.postal_code,
s.name as state,
o.name as country
FROM civicrm_contact c
INNER JOIN civicrm_address a ON a.contact_id = c.id
LEFT JOIN civicrm_country o ON a.country_id = o.id
LEFT JOIN civicrm_state_province s ON a.state_province_id = s.id
WHERE {$whereClause}
ORDER BY a.id
";
$totalGeocoded = $totalAddresses = $totalAddressParsed = 0;
$dao = CRM_Core_DAO::executeQuery($query, $params);
if ($processGeocode) {
require_once str_replace('_', DIRECTORY_SEPARATOR, $config->geocodeMethod) . '.php';
}
$unparseableContactAddress = array();
while ($dao->fetch()) {
$totalAddresses++;
$params = array(
'street_address' => $dao->street_address,
'postal_code' => $dao->postal_code,
'city' => $dao->city,
'state_province' => $dao->state,
'country' => $dao->country,
);
$addressParams = array();
// process geocode.
if ($processGeocode) {
// loop through the address removing more information
// so we can get some geocode for a partial address
// i.e. city -> state -> country
$maxTries = 5;
do {
if ($this->throttle) {
usleep(5000000);
}
$className = $config->geocodeMethod;
$className::format($params, TRUE);
// see if we got a geocode error, in this case we'll trigger a fatal
// CRM-13760
if (
isset($params['geo_code_error']) &&
$params['geo_code_error'] == 'OVER_QUERY_LIMIT'
) {
CRM_Core_Error::fatal('Aborting batch geocoding. Hit the over query limit on geocoder.');
}
array_shift($params);
$maxTries--;
} while (
(!isset($params['geo_code_1']) || $params['geo_code_1'] == 'null') &&
($maxTries > 1)
);
if (isset($params['geo_code_1']) && $params['geo_code_1'] != 'null') {
$totalGeocoded++;
$addressParams = $params;
}
}
// parse street address
if ($parseStreetAddress) {
$parsedFields = CRM_Core_BAO_Address::parseStreetAddress($dao->street_address);
$success = TRUE;
// consider address is automatically parseable,
// when we should found street_number and street_name
if (empty($parsedFields['street_name']) || empty($parsedFields['street_number'])) {
$success = FALSE;
}
// do check for all elements.
if ($success) {
$totalAddressParsed++;
}
elseif ($dao->street_address) {
//build contact edit url,
//so that user can manually fill the street address fields if the street address is not parsed, CRM-5886
$url = CRM_Utils_System::url('civicrm/contact/add', "reset=1&action=update&cid={$dao->id}");
$unparseableContactAddress[] = " Contact ID: " . $dao->id . " <a href =\"$url\"> " . $dao->street_address . " </a> ";
// reset element values.
$parsedFields = array_fill_keys(array_keys($parsedFields), '');
}
$addressParams = array_merge($addressParams, $parsedFields);
}
// finally update address object.
if (!empty($addressParams)) {
$address = new CRM_Core_DAO_Address();
$address->id = $dao->address_id;
$address->copyValues($addressParams);
$address->save();
$address->free();
}
}
$this->returnMessages[] = ts("Addresses Evaluated: %1", array(
1 => $totalAddresses,
)) . "\n";
if ($processGeocode) {
$this->returnMessages[] = ts("Addresses Geocoded: %1", array(
1 => $totalGeocoded,
)) . "\n";
}
if ($parseStreetAddress) {
$this->returnMessages[] = ts("Street Addresses Parsed: %1", array(
1 => $totalAddressParsed,
)) . "\n";
if ($unparseableContactAddress) {
$this->returnMessages[] = "<br />\n" . ts("Following is the list of contacts whose address is not parsed:") . "<br />\n";
foreach ($unparseableContactAddress as $contactLink) {
$this->returnMessages[] = $contactLink . "<br />\n";
}
}
}
return $this->returnResult();
}
/**
* Return result.
*
* @return array
*/
public function returnResult() {
$result = array();
$result['is_error'] = $this->returnError;
$result['messages'] = implode("", $this->returnMessages);
return $result;
}
}

View file

@ -0,0 +1,139 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Address utilities.
*/
class CRM_Utils_Address_USPS {
/**
* Whether USPS validation should be disabled during import.
*
* @var bool
*/
protected static $_disabled = FALSE;
/**
* Disable the USPS validation.
*
* @param bool $disable
*/
public static function disable($disable = TRUE) {
self::$_disabled = $disable;
}
/**
* Check address against USPS.
*
* @param array $values
*
* @return bool
*/
public static function checkAddress(&$values) {
if (self::$_disabled) {
return FALSE;
}
if (!isset($values['street_address']) ||
(!isset($values['city']) &&
!isset($values['state_province']) &&
!isset($values['postal_code'])
)
) {
return FALSE;
}
$userID = Civi::settings()->get('address_standardization_userid');
$url = Civi::settings()->get('address_standardization_url');
if (empty($userID) ||
empty($url)
) {
return FALSE;
}
$address2 = str_replace(',', '', $values['street_address']);
$XMLQuery = '<AddressValidateRequest USERID="' . $userID . '"><Address ID="0"><Address1>' . CRM_Utils_Array::value('supplemental_address_1', $values, '') . '</Address1><Address2>' . $address2 . '</Address2><City>' . $values['city'] . '</City><State>' . $values['state_province'] . '</State><Zip5>' . $values['postal_code'] . '</Zip5><Zip4>' . CRM_Utils_Array::value('postal_code_suffix', $values, '') . '</Zip4></Address></AddressValidateRequest>';
require_once 'HTTP/Request.php';
$request = new HTTP_Request();
$request->setURL($url);
$request->addQueryString('API', 'Verify');
$request->addQueryString('XML', $XMLQuery);
$response = $request->sendRequest();
$session = CRM_Core_Session::singleton();
$code = $request->getResponseCode();
if ($code != 200) {
$session->setStatus(ts('USPS Address Lookup Failed with HTTP status code: %1',
array(1 => $code)
));
return FALSE;
}
$responseBody = $request->getResponseBody();
$xml = simplexml_load_string($responseBody);
if (is_null($xml) || is_null($xml->Address)) {
$session->setStatus(ts('Your USPS API Lookup has Failed.'));
return FALSE;
}
if ($xml->Number == '80040b1a') {
$session->setStatus(ts('Your USPS API Authorization has Failed.'));
return FALSE;
}
if (array_key_exists('Error', $xml->Address)) {
$session->setStatus(ts('Address not found in USPS database.'));
return FALSE;
}
$values['street_address'] = (string) $xml->Address->Address2;
$values['city'] = (string) $xml->Address->City;
$values['state_province'] = (string) $xml->Address->State;
$values['postal_code'] = (string) $xml->Address->Zip5;
$values['postal_code_suffix'] = (string) $xml->Address->Zip4;
if (array_key_exists('Address1', $xml->Address)) {
$values['supplemental_address_1'] = (string) $xml->Address->Address1;
}
return TRUE;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_AutoClean
*
* Automatically cleanup state when the object handle is released.
* This is useful for unordered cleanup when a function has many
* different exit scenarios (eg multiple returns, exceptions).
*/
class CRM_Utils_AutoClean {
protected $callback;
protected $args;
/**
* Call a cleanup function when the current context shuts down.
*
* @code
* function doStuff() {
* $ac = CRM_Utils_AutoClean::with(function(){
* MyCleanup::doIt();
* });
* ...
* }
* @endcode
*
* @param mixed $callback
* @return CRM_Utils_AutoClean
*/
public static function with($callback) {
$ac = new CRM_Utils_AutoClean();
$ac->args = func_get_args();
$ac->callback = array_shift($ac->args);
return $ac;
}
/**
* Temporarily swap values using callback functions, and cleanup
* when the current context shuts down.
*
* @code
* function doStuff() {
* $ac = CRM_Utils_AutoClean::swap('My::get', 'My::set', 'tmpValue');
* ...
* }
* @endcode
*
* @param mixed $getter
* Function to lookup current value.
* @param mixed $setter
* Function to set new value.
* @param mixed $tmpValue
* The value to temporarily use.
* @return CRM_Utils_AutoClean
* @see \Civi\Core\Resolver
*/
public static function swap($getter, $setter, $tmpValue) {
$resolver = \Civi\Core\Resolver::singleton();
$origValue = $resolver->call($getter, array());
$ac = new CRM_Utils_AutoClean();
$ac->callback = $setter;
$ac->args = array($origValue);
$resolver->call($setter, array($tmpValue));
return $ac;
}
public function __destruct() {
\Civi\Core\Resolver::singleton()->call($this->callback, $this->args);
}
}

View file

@ -0,0 +1,213 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Cache is an empty base object, we'll modify the scheme when we have different caching schemes
*/
class CRM_Utils_Cache {
/**
* (Quasi-Private) Treat this as private. It is marked public to facilitate testing.
*
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
*
* @var object
*/
public static $_singleton = NULL;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache
*/
public function __construct(&$config) {
CRM_Core_Error::fatal(ts('this is just an interface and should not be called directly'));
}
/**
* Singleton function used to manage this object.
*
* @return CRM_Utils_Cache_Interface
*/
public static function &singleton() {
if (self::$_singleton === NULL) {
$className = 'ArrayCache'; // default to ArrayCache for now
// Maintain backward compatibility for now.
// Setting CIVICRM_USE_MEMCACHE or CIVICRM_USE_ARRAYCACHE will
// override the CIVICRM_DB_CACHE_CLASS setting.
// Going forward, CIVICRM_USE_xxxCACHE should be deprecated.
if (defined('CIVICRM_USE_MEMCACHE') && CIVICRM_USE_MEMCACHE) {
$className = 'Memcache';
}
elseif (defined('CIVICRM_USE_ARRAYCACHE') && CIVICRM_USE_ARRAYCACHE) {
$className = 'ArrayCache';
}
elseif (defined('CIVICRM_DB_CACHE_CLASS') && CIVICRM_DB_CACHE_CLASS) {
$className = CIVICRM_DB_CACHE_CLASS;
}
// a generic method for utilizing any of the available db caches.
$dbCacheClass = 'CRM_Utils_Cache_' . $className;
$settings = self::getCacheSettings($className);
self::$_singleton = new $dbCacheClass($settings);
}
return self::$_singleton;
}
/**
* Get cache relevant settings.
*
* @param $cachePlugin
*
* @return array
* associative array of settings for the cache
*/
public static function getCacheSettings($cachePlugin) {
switch ($cachePlugin) {
case 'ArrayCache':
case 'NoCache':
$defaults = array();
break;
case 'Redis':
case 'Memcache':
case 'Memcached':
$defaults = array(
'host' => 'localhost',
'port' => 11211,
'timeout' => 3600,
'prefix' => '',
);
// Use old constants if needed to ensure backward compatibility
if (defined('CIVICRM_MEMCACHE_HOST')) {
$defaults['host'] = CIVICRM_MEMCACHE_HOST;
}
if (defined('CIVICRM_MEMCACHE_PORT')) {
$defaults['port'] = CIVICRM_MEMCACHE_PORT;
}
if (defined('CIVICRM_MEMCACHE_TIMEOUT')) {
$defaults['timeout'] = CIVICRM_MEMCACHE_TIMEOUT;
}
if (defined('CIVICRM_MEMCACHE_PREFIX')) {
$defaults['prefix'] = CIVICRM_MEMCACHE_PREFIX;
}
// Use new constants if possible
if (defined('CIVICRM_DB_CACHE_HOST')) {
$defaults['host'] = CIVICRM_DB_CACHE_HOST;
}
if (defined('CIVICRM_DB_CACHE_PORT')) {
$defaults['port'] = CIVICRM_DB_CACHE_PORT;
}
if (defined('CIVICRM_DB_CACHE_TIMEOUT')) {
$defaults['timeout'] = CIVICRM_DB_CACHE_TIMEOUT;
}
if (defined('CIVICRM_DB_CACHE_PREFIX')) {
$defaults['prefix'] = CIVICRM_DB_CACHE_PREFIX;
}
break;
case 'APCcache':
$defaults = array();
if (defined('CIVICRM_DB_CACHE_TIMEOUT')) {
$defaults['timeout'] = CIVICRM_DB_CACHE_TIMEOUT;
}
if (defined('CIVICRM_DB_CACHE_PREFIX')) {
$defaults['prefix'] = CIVICRM_DB_CACHE_PREFIX;
}
break;
}
return $defaults;
}
/**
* Create a new, named, limited-use cache.
*
* This is a factory function. Generally, you should use Civi::cache($name)
* to locate managed cached instance.
*
* @param array $params
* Array with keys:
* - name: string, unique symbolic name.
* - type: array|string, list of acceptable cache types, in order of preference.
* - prefetch: bool, whether to prefetch all data in cache (if possible).
* @return CRM_Utils_Cache_Interface
* @throws CRM_Core_Exception
* @see Civi::cache()
*/
public static function create($params = array()) {
$types = (array) $params['type'];
foreach ($types as $type) {
switch ($type) {
case '*memory*':
if (defined('CIVICRM_DB_CACHE_CLASS') && in_array(CIVICRM_DB_CACHE_CLASS, array('Memcache', 'Memcached', 'Redis'))) {
$dbCacheClass = 'CRM_Utils_Cache_' . CIVICRM_DB_CACHE_CLASS;
$settings = self::getCacheSettings(CIVICRM_DB_CACHE_CLASS);
$settings['prefix'] = $settings['prefix'] . '_' . $params['name'];
return new $dbCacheClass($settings);
}
break;
case 'SqlGroup':
if (defined('CIVICRM_DSN') && CIVICRM_DSN) {
return new CRM_Utils_Cache_SqlGroup(array(
'group' => $params['name'],
'prefetch' => CRM_Utils_Array::value('prefetch', $params, FALSE),
));
}
break;
case 'Arraycache':
case 'ArrayCache':
return new CRM_Utils_Cache_ArrayCache(array());
}
}
throw new CRM_Core_Exception("Failed to instantiate cache. No supported cache type found. " . print_r($params, 1));
}
}

View file

@ -0,0 +1,118 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
const DEFAULT_TIMEOUT = 3600;
const DEFAULT_PREFIX = '';
/**
* The default timeout to use.
*
* @var int
*/
protected $_timeout = self::DEFAULT_TIMEOUT;
/**
* The prefix prepended to cache keys.
*
* If we are using the same memcache instance for multiple CiviCRM
* installs, we must have a unique prefix for each install to prevent
* the keys from clobbering each other.
*
* @var string
*/
protected $_prefix = self::DEFAULT_PREFIX;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_APCcache
*/
public function __construct(&$config) {
if (isset($config['timeout'])) {
$this->_timeout = intval($config['timeout']);
}
if (isset($config['prefix'])) {
$this->_prefix = $config['prefix'];
}
}
/**
* @param $key
* @param $value
*
* @return bool
*/
public function set($key, &$value) {
if (!apc_store($this->_prefix . $key, $value, $this->_timeout)) {
return FALSE;
}
return TRUE;
}
/**
* @param $key
*
* @return mixed
*/
public function get($key) {
return apc_fetch($this->_prefix . $key);
}
/**
* @param $key
*
* @return bool|string[]
*/
public function delete($key) {
return apc_delete($this->_prefix . $key);
}
public function flush() {
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$prefix = $this->_prefix . "CRM_"; // Our keys follows this pattern: ([A-Za-z0-9_]+)?CRM_[A-Za-z0-9_]+
$lp = strlen($prefix); // Get prefix length
foreach ($keys as $key) {
$name = $key['info'];
if ($prefix == substr($name, 0, $lp)) {
// Ours?
apc_delete($this->_prefix . $name);
}
}
}
}

View file

@ -0,0 +1,85 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_Cache_Arraycache
*/
class CRM_Utils_Cache_Arraycache implements CRM_Utils_Cache_Interface {
/**
* The cache storage container, an in memory array by default
*/
protected $_cache;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_Arraycache
*/
public function __construct($config) {
$this->_cache = array();
}
/**
* @param string $key
* @param mixed $value
*/
public function set($key, &$value) {
$this->_cache[$key] = $value;
}
/**
* @param string $key
*
* @return mixed
*/
public function get($key) {
return CRM_Utils_Array::value($key, $this->_cache);
}
/**
* @param string $key
*/
public function delete($key) {
unset($this->_cache[$key]);
}
public function flush() {
unset($this->_cache);
$this->_cache = array();
}
}

View file

@ -0,0 +1,89 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*
* CRM_Utils_Cache_Interface
*
* PHP-FIG has been developing a draft standard for caching,
* PSR-6. The standard has not been ratified yet. When
* making changes to this interface, please take care to
* avoid *conflicst* with PSR-6's CacheItemPoolInterface. At
* time of writing, they do not conflict. Avoiding conflicts
* will enable more transition paths where Civi
* simultaneously supports both interfaces in the same
* implementation.
*
* For example, the current interface defines:
*
* function get($key) => mixed $value
*
* and PSR-6 defines:
*
* function getItem($key) => ItemInterface $item
*
* These are different styles (e.g. "weak item" vs "strong item"),
* but the two methods do not *conflict*. They can coexist,
* and you can trivially write adapters between the two.
*
* @see https://github.com/php-fig/fig-standards/blob/master/proposed/cache.md
*/
interface CRM_Utils_Cache_Interface {
/**
* Set the value in the cache.
*
* @param string $key
* @param mixed $value
*/
public function set($key, &$value);
/**
* Get a value from the cache.
*
* @param string $key
* @return mixed
* NULL if $key has not been previously set
*/
public function get($key);
/**
* Delete a value from the cache.
*
* @param string $key
*/
public function delete($key);
/**
* Delete all values from the cache.
*/
public function flush();
}

View file

@ -0,0 +1,148 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 11211;
const DEFAULT_TIMEOUT = 3600;
const DEFAULT_PREFIX = '';
/**
* The host name of the memcached server.
*
* @var string
*/
protected $_host = self::DEFAULT_HOST;
/**
* The port on which to connect on.
*
* @var int
*/
protected $_port = self::DEFAULT_PORT;
/**
* The default timeout to use.
*
* @var int
*/
protected $_timeout = self::DEFAULT_TIMEOUT;
/**
* The prefix prepended to cache keys.
*
* If we are using the same memcache instance for multiple CiviCRM
* installs, we must have a unique prefix for each install to prevent
* the keys from clobbering each other.
*
* @var string
*/
protected $_prefix = self::DEFAULT_PREFIX;
/**
* The actual memcache object.
*
* @var Memcache
*/
protected $_cache;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_Memcache
*/
public function __construct($config) {
if (isset($config['host'])) {
$this->_host = $config['host'];
}
if (isset($config['port'])) {
$this->_port = $config['port'];
}
if (isset($config['timeout'])) {
$this->_timeout = $config['timeout'];
}
if (isset($config['prefix'])) {
$this->_prefix = $config['prefix'];
}
$this->_cache = new Memcache();
if (!$this->_cache->connect($this->_host, $this->_port)) {
// dont use fatal here since we can go in an infinite loop
echo 'Could not connect to Memcached server';
CRM_Utils_System::civiExit();
}
}
/**
* @param $key
* @param $value
*
* @return bool
*/
public function set($key, &$value) {
if (!$this->_cache->set($this->_prefix . $key, $value, FALSE, $this->_timeout)) {
return FALSE;
}
return TRUE;
}
/**
* @param $key
*
* @return mixed
*/
public function &get($key) {
$result = $this->_cache->get($this->_prefix . $key);
return $result;
}
/**
* @param $key
*
* @return mixed
*/
public function delete($key) {
return $this->_cache->delete($this->_prefix . $key);
}
/**
* @return mixed
*/
public function flush() {
return $this->_cache->flush();
}
}

View file

@ -0,0 +1,170 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 11211;
const DEFAULT_TIMEOUT = 3600;
const DEFAULT_PREFIX = '';
const MAX_KEY_LEN = 62;
/**
* The host name of the memcached server
*
* @var string
*/
protected $_host = self::DEFAULT_HOST;
/**
* The port on which to connect on
*
* @var int
*/
protected $_port = self::DEFAULT_PORT;
/**
* The default timeout to use
*
* @var int
*/
protected $_timeout = self::DEFAULT_TIMEOUT;
/**
* The prefix prepended to cache keys.
*
* If we are using the same memcache instance for multiple CiviCRM
* installs, we must have a unique prefix for each install to prevent
* the keys from clobbering each other.
*
* @var string
*/
protected $_prefix = self::DEFAULT_PREFIX;
/**
* The actual memcache object.
*
* @var Memcached
*/
protected $_cache;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_Memcached
*/
public function __construct($config) {
if (isset($config['host'])) {
$this->_host = $config['host'];
}
if (isset($config['port'])) {
$this->_port = $config['port'];
}
if (isset($config['timeout'])) {
$this->_timeout = $config['timeout'];
}
if (isset($config['prefix'])) {
$this->_prefix = $config['prefix'];
}
$this->_cache = new Memcached();
if (!$this->_cache->addServer($this->_host, $this->_port)) {
// dont use fatal here since we can go in an infinite loop
echo 'Could not connect to Memcached server';
CRM_Utils_System::civiExit();
}
}
/**
* @param $key
* @param $value
*
* @return bool
* @throws Exception
*/
public function set($key, &$value) {
$key = $this->cleanKey($key);
if (!$this->_cache->set($key, $value, $this->_timeout)) {
CRM_Core_Error::debug('Result Code: ', $this->_cache->getResultMessage());
CRM_Core_Error::fatal("memcached set failed, wondering why?, $key", $value);
return FALSE;
}
return TRUE;
}
/**
* @param $key
*
* @return mixed
*/
public function &get($key) {
$key = $this->cleanKey($key);
$result = $this->_cache->get($key);
return $result;
}
/**
* @param $key
*
* @return mixed
*/
public function delete($key) {
$key = $this->cleanKey($key);
return $this->_cache->delete($key);
}
/**
* @param $key
*
* @return mixed|string
*/
public function cleanKey($key) {
$key = preg_replace('/\s+|\W+/', '_', $this->_prefix . $key);
if (strlen($key) > self::MAX_KEY_LEN) {
$md5Key = md5($key); // this should be 32 characters in length
$subKeyLen = self::MAX_KEY_LEN - 1 - strlen($md5Key);
$key = substr($key, 0, $subKeyLen) . "_" . $md5Key;
}
return $key;
}
/**
* @return mixed
*/
public function flush() {
return $this->_cache->flush();
}
}

View file

@ -0,0 +1,89 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Cache_NoCache implements CRM_Utils_Cache_Interface {
/**
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
*
* @var object
*/
static private $_singleton = NULL;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_NoCache
*/
public function __construct($config) {
}
/**
* @param string $key
* @param mixed $value
*
* @return bool
*/
public function set($key, &$value) {
return FALSE;
}
/**
* @param string $key
*
* @return null
*/
public function get($key) {
return NULL;
}
/**
* @param string $key
*
* @return bool
*/
public function delete($key) {
return FALSE;
}
/**
* @return bool
*/
public function flush() {
return FALSE;
}
}

View file

@ -0,0 +1,152 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
* $Id$
*
*/
class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 6379;
const DEFAULT_TIMEOUT = 3600;
const DEFAULT_PREFIX = '';
/**
* The host name of the redisd server
*
* @var string
*/
protected $_host = self::DEFAULT_HOST;
/**
* The port on which to connect on
*
* @var int
*/
protected $_port = self::DEFAULT_PORT;
/**
* The default timeout to use
*
* @var int
*/
protected $_timeout = self::DEFAULT_TIMEOUT;
/**
* The prefix prepended to cache keys.
*
* If we are using the same redis instance for multiple CiviCRM
* installs, we must have a unique prefix for each install to prevent
* the keys from clobbering each other.
*
* @var string
*/
protected $_prefix = self::DEFAULT_PREFIX;
/**
* The actual redis object
*
* @var Redis
*/
protected $_cache;
/**
* Constructor
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_Redis
*/
public function __construct($config) {
if (isset($config['host'])) {
$this->_host = $config['host'];
}
if (isset($config['port'])) {
$this->_port = $config['port'];
}
if (isset($config['timeout'])) {
$this->_timeout = $config['timeout'];
}
if (isset($config['prefix'])) {
$this->_prefix = $config['prefix'];
}
$this->_cache = new Redis();
if (!$this->_cache->connect($this->_host, $this->_port)) {
// dont use fatal here since we can go in an infinite loop
echo 'Could not connect to redisd server';
CRM_Utils_System::civiExit();
}
$this->_cache->auth(CIVICRM_DB_CACHE_PASSWORD);
}
/**
* @param $key
* @param $value
*
* @return bool
* @throws Exception
*/
public function set($key, &$value) {
if (!$this->_cache->set($this->_prefix . $key, serialize($value), $this->_timeout)) {
CRM_Core_Error::fatal("Redis set failed, wondering why?, $key", $value);
return FALSE;
}
return TRUE;
}
/**
* @param $key
*
* @return mixed
*/
public function get($key) {
$result = $this->_cache->get($this->_prefix . $key);
return unserialize($result);
}
/**
* @param $key
*
* @return mixed
*/
public function delete($key) {
return $this->_cache->delete($this->_prefix . $key);
}
/**
* @return mixed
*/
public function flush() {
return $this->_cache->flushDB();
}
}

View file

@ -0,0 +1,125 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_Cache_SerializeCache
*/
class CRM_Utils_Cache_SerializeCache implements CRM_Utils_Cache_Interface {
/**
* The cache storage container, an array by default, stored in a file under templates
*/
private $_cache;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
*
* @return \CRM_Utils_Cache_SerializeCache
*/
public function __construct($config) {
$this->_cache = array();
}
/**
* @param $key
*
* @return string
*/
public function fileName($key) {
if (strlen($key) > 50) {
return CIVICRM_TEMPLATE_COMPILEDIR . "CRM_" . md5($key) . ".php";
}
return CIVICRM_TEMPLATE_COMPILEDIR . $key . ".php";
}
/**
* @param string $key
*
* @return mixed
*/
public function get($key) {
if (array_key_exists($key, $this->_cache)) {
return $this->_cache[$key];
}
if (!file_exists($this->fileName($key))) {
return;
}
$this->_cache[$key] = unserialize(substr(file_get_contents($this->fileName($key)), 8));
return $this->_cache[$key];
}
/**
* @param string $key
* @param mixed $value
*/
public function set($key, &$value) {
if (file_exists($this->fileName($key))) {
return;
}
$this->_cache[$key] = $value;
file_put_contents($this->fileName($key), "<?php //" . serialize($value));
}
/**
* @param string $key
*/
public function delete($key) {
if (file_exists($this->fileName($key))) {
unlink($this->fileName($key));
}
unset($this->_cache[$key]);
}
/**
* @param null $key
*/
public function flush($key = NULL) {
$prefix = "CRM_";
if (!$handle = opendir(CIVICRM_TEMPLATE_COMPILEDIR)) {
return; // die? Error?
}
while (FALSE !== ($entry = readdir($handle))) {
if (substr($entry, 0, 4) == $prefix) {
unlink(CIVICRM_TEMPLATE_COMPILEDIR . $entry);
}
}
closedir($handle);
unset($this->_cache);
$this->_cache = array();
}
}

View file

@ -0,0 +1,137 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* This caching provider stores all cached items as a "group" in the
* "civicrm_cache" table. The entire 'group' may be prefetched when
* instantiating the cache provider.
*/
class CRM_Utils_Cache_SqlGroup implements CRM_Utils_Cache_Interface {
/**
* The host name of the memcached server.
*
* @var string
*/
protected $group;
/**
* @var int $componentID The optional component ID (so componenets can share the same name space)
*/
protected $componentID;
/**
* @var array in-memory cache to optimize redundant get()s
*/
protected $frontCache;
/**
* Constructor.
*
* @param array $config
* An array of configuration params.
* - group: string
* - componentID: int
* - prefetch: bool, whether to preemptively read the entire cache group; default: TRUE
*
* @throws RuntimeException
* @return \CRM_Utils_Cache_SqlGroup
*/
public function __construct($config) {
if (isset($config['group'])) {
$this->group = $config['group'];
}
else {
throw new RuntimeException("Cannot construct SqlGroup cache: missing group");
}
if (isset($config['componentID'])) {
$this->componentID = $config['componentID'];
}
else {
$this->componentID = NULL;
}
$this->frontCache = array();
if (CRM_Utils_Array::value('prefetch', $config, TRUE)) {
$this->prefetch();
}
}
/**
* @param string $key
* @param mixed $value
*/
public function set($key, &$value) {
CRM_Core_BAO_Cache::setItem($value, $this->group, $key, $this->componentID);
$this->frontCache[$key] = $value;
}
/**
* @param string $key
*
* @return mixed
*/
public function get($key) {
if (!array_key_exists($key, $this->frontCache)) {
$this->frontCache[$key] = CRM_Core_BAO_Cache::getItem($this->group, $key, $this->componentID);
}
return $this->frontCache[$key];
}
/**
* @param $key
* @param null $default
*
* @return mixed
*/
public function getFromFrontCache($key, $default = NULL) {
return CRM_Utils_Array::value($key, $this->frontCache, $default);
}
/**
* @param string $key
*/
public function delete($key) {
CRM_Core_BAO_Cache::deleteGroup($this->group, $key);
unset($this->frontCache[$key]);
}
public function flush() {
CRM_Core_BAO_Cache::deleteGroup($this->group);
$this->frontCache = array();
}
public function prefetch() {
$this->frontCache = CRM_Core_BAO_Cache::getItems($this->group, $this->componentID);
}
}

View file

@ -0,0 +1,260 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check {
// How often to run checks and notify admins about issues.
const CHECK_TIMER = 86400;
/**
* @var array
* @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
*/
protected static $severityList = array(
\Psr\Log\LogLevel::DEBUG,
\Psr\Log\LogLevel::INFO,
\Psr\Log\LogLevel::NOTICE,
\Psr\Log\LogLevel::WARNING,
\Psr\Log\LogLevel::ERROR,
\Psr\Log\LogLevel::CRITICAL,
\Psr\Log\LogLevel::ALERT,
\Psr\Log\LogLevel::EMERGENCY,
);
/**
* We only need one instance of this object, so we use the
* singleton pattern and cache the instance in this variable
*
* @var object
*/
static private $_singleton = NULL;
/**
* Provide static instance of CRM_Utils_Check.
*
* @return CRM_Utils_Check
*/
public static function &singleton() {
if (!isset(self::$_singleton)) {
self::$_singleton = new CRM_Utils_Check();
}
return self::$_singleton;
}
/**
* @return array
*/
public static function getSeverityList() {
return self::$severityList;
}
/**
* Display daily system status alerts (admin only).
*/
public function showPeriodicAlerts() {
if (CRM_Core_Permission::check('administer CiviCRM')) {
$session = CRM_Core_Session::singleton();
if ($session->timer('check_' . __CLASS__, self::CHECK_TIMER)) {
// Best attempt at re-securing folders
$config = CRM_Core_Config::singleton();
$config->cleanup(0, FALSE);
$statusMessages = array();
$maxSeverity = 0;
foreach ($this->checkAll() as $message) {
if (!$message->isVisible()) {
continue;
}
if ($message->getLevel() >= 3) {
$maxSeverity = max($maxSeverity, $message->getLevel());
$statusMessage = $message->getMessage();
$statusMessages[] = $statusTitle = $message->getTitle();
}
}
if ($statusMessages) {
if (count($statusMessages) > 1) {
$statusTitle = self::toStatusLabel($maxSeverity);
$statusMessage = '<ul><li>' . implode('</li><li>', $statusMessages) . '</li></ul>';
}
$statusMessage .= '<p><a href="' . CRM_Utils_System::url('civicrm/a/#/status') . '">' . ts('View details and manage alerts') . '</a></p>';
$statusType = $maxSeverity >= 4 ? 'error' : 'alert';
CRM_Core_Session::setStatus($statusMessage, $statusTitle, $statusType);
}
}
}
}
/**
* Sort messages based upon severity
*
* @param CRM_Utils_Check_Message $a
* @param CRM_Utils_Check_Message $b
* @return int
*/
public static function severitySort($a, $b) {
$aSeverity = $a->getLevel();
$bSeverity = $b->getLevel();
if ($aSeverity == $bSeverity) {
return strcmp($a->getName(), $b->getName());
}
// The Message constructor guarantees that these will always be integers.
return ($aSeverity < $bSeverity);
}
/**
* Get the integer value (useful for thresholds) of the severity.
*
* @param int|string $severity
* the value to look up
* @param bool $reverse
* whether to find the constant from the integer
* @return string|int
* @throws \CRM_Core_Exception
*/
public static function severityMap($severity, $reverse = FALSE) {
if ($reverse) {
if (isset(self::$severityList[$severity])) {
return self::$severityList[$severity];
}
}
else {
// Lowercase string-based severities
$severity = strtolower($severity);
if (in_array($severity, self::$severityList)) {
return array_search($severity, self::$severityList);
}
}
throw new CRM_Core_Exception('Invalid PSR Severity Level');
}
/**
* Throw an exception if any of the checks fail.
*
* @param array|NULL $messages
* [CRM_Utils_Check_Message]
* @param string $threshold
*
* @throws \CRM_Core_Exception
* @throws \Exception
*/
public function assertValid($messages = NULL, $threshold = \Psr\Log\LogLevel::ERROR) {
if ($messages === NULL) {
$messages = $this->checkAll();
}
$minLevel = self::severityMap($threshold);
$errors = array();
foreach ($messages as $message) {
if ($message->getLevel() >= $minLevel) {
$errors[] = $message->toArray();
}
}
if ($errors) {
throw new Exception("System $threshold: " . print_r($errors, TRUE));
}
}
/**
* Run all system checks.
*
* This functon is wrapped by the System.check api.
*
* Calls hook_civicrm_check() for extensions to add or modify messages.
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_check
*
* @param bool $max
* Whether to return just the maximum non-hushed severity
*
* @return array
* Array of CRM_Utils_Check_Message objects
*/
public static function checkAll($max = FALSE) {
$messages = array();
foreach (glob(__DIR__ . '/Check/Component/*.php') as $filePath) {
$className = 'CRM_Utils_Check_Component_' . basename($filePath, '.php');
/* @var CRM_Utils_Check_Component $check */
$check = new $className();
if ($check->isEnabled()) {
$messages = array_merge($messages, $check->checkAll());
}
}
CRM_Utils_Hook::check($messages);
uasort($messages, array(__CLASS__, 'severitySort'));
$maxSeverity = 1;
foreach ($messages as $message) {
if (!$message->isVisible()) {
continue;
}
$maxSeverity = max(1, $message->getLevel());
break;
}
Civi::settings()->set('systemStatusCheckResult', $maxSeverity);
return ($max) ? $maxSeverity : $messages;
}
/**
* @param int $level
* @return string
*/
public static function toStatusLabel($level) {
switch ($level) {
case 7:
return ts('System Status: Emergency');
case 6:
return ts('System Status: Alert');
case 5:
return ts('System Status: Critical');
case 4:
return ts('System Status: Error');
case 3:
return ts('System Status: Warning');
case 2:
return ts('System Status: Notice');
default:
return ts('System Status: Ok');
}
}
}

View file

@ -0,0 +1,60 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
abstract class CRM_Utils_Check_Component {
/**
* Should these checks be run?
*
* @return bool
*/
public function isEnabled() {
return TRUE;
}
/**
* Run all checks in this class.
*
* @return array
* [CRM_Utils_Check_Message]
*/
public function checkAll() {
$messages = array();
foreach (get_class_methods($this) as $method) {
if ($method !== 'checkAll' && strpos($method, 'check') === 0) {
$messages = array_merge($messages, $this->$method());
}
}
return $messages;
}
}

View file

@ -0,0 +1,165 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_Case extends CRM_Utils_Check_Component {
const DOCTOR_WHEN = 'https://github.com/civicrm/org.civicrm.doctorwhen';
/**
* @var CRM_Case_XMLRepository
*/
protected $xmlRepo;
/**
* @var array<string>
*/
protected $caseTypeNames;
/**
* Class constructor.
*/
public function __construct() {
$this->caseTypeNames = CRM_Case_PseudoConstant::caseType('name');
$this->xmlRepo = CRM_Case_XMLRepository::singleton();
}
/**
* @inheritDoc
*/
public function isEnabled() {
return CRM_Case_BAO_Case::enabled();
}
/**
* Check that the case-type names don't rely on double-munging.
*
* @return array<CRM_Utils_Check_Message>
* An empty array, or a list of warnings
*/
public function checkCaseTypeNameConsistency() {
$messages = array();
foreach ($this->caseTypeNames as $caseTypeName) {
$normalFile = $this->xmlRepo->findXmlFile($caseTypeName);
$mungedFile = $this->xmlRepo->findXmlFile(CRM_Case_XMLProcessor::mungeCaseType($caseTypeName));
if ($normalFile && $mungedFile && $normalFile == $mungedFile) {
// ok
}
elseif ($normalFile && $mungedFile) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__ . $caseTypeName,
ts('Case type "%1" has duplicate XML files ("%2" and "%3")', array(
1 => $caseTypeName,
2 => $normalFile,
3 => $mungedFile,
)) .
'<br /><a href="' . CRM_Utils_System::getWikiBaseURL() . __FUNCTION__ . '">' .
ts('Read more about this warning') .
'</a>',
ts('CiviCase'),
\Psr\Log\LogLevel::WARNING,
'fa-puzzle-piece'
);
}
elseif ($normalFile && !$mungedFile) {
// ok
}
elseif (!$normalFile && $mungedFile) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__ . $caseTypeName,
ts('Case type "%1" corresponds to XML file ("%2") The XML file should be named "%3".', array(
1 => $caseTypeName,
2 => $mungedFile,
3 => "{$caseTypeName}.xml",
)) .
'<br /><a href="' . CRM_Utils_System::getWikiBaseURL() . __FUNCTION__ . '">' .
ts('Read more about this warning') .
'</a>',
ts('CiviCase'),
\Psr\Log\LogLevel::WARNING,
'fa-puzzle-piece'
);
}
elseif (!$normalFile && !$mungedFile) {
// ok -- probably a new or DB-based CaseType
}
}
return $messages;
}
/**
* Check that the timestamp columns are populated. (CRM-20958)
*
* @return array<CRM_Utils_Check_Message>
* An empty array, or a list of warnings
*/
public function checkNullTimestamps() {
$messages = array();
$nullCount = 0;
$nullCount += CRM_Utils_SQL_Select::from('civicrm_activity')
->where('created_date IS NULL OR modified_date IS NULL')
->select('COUNT(*)')
->execute()
->fetchValue();
$nullCount += CRM_Utils_SQL_Select::from('civicrm_case')
->where('created_date IS NULL OR modified_date IS NULL')
->select('COUNT(*)')
->execute()
->fetchValue();
if ($nullCount > 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
'<p>' .
ts('The tables "<em>civicrm_activity</em>" and "<em>civicrm_case</em>" were updated to support two new fields, "<em>created_date</em>" and "<em>modified_date</em>". For historical data, these fields may appear blank. (%1 records have NULL timestamps.)', array(
1 => $nullCount,
)) .
'</p><p>' .
ts('At time of writing, this is not a problem. However, future extensions and improvements could rely on these fields, so it may be useful to back-fill them.') .
'</p><p>' .
ts('For further discussion, please visit %1', array(
1 => sprintf('<a href="%s" target="_blank">%s</a>', self::DOCTOR_WHEN, self::DOCTOR_WHEN),
)) .
'</p>',
ts('Timestamps for Activities and Cases'),
\Psr\Log\LogLevel::NOTICE,
'fa-clock-o'
);
}
return $messages;
}
}

View file

@ -0,0 +1,918 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component {
/**
* @return array
*/
public function checkPhpVersion() {
$messages = array();
$phpVersion = phpversion();
if (version_compare($phpVersion, CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER) >= 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('This system uses PHP version %1 which meets or exceeds the recommendation of %2.',
array(
1 => $phpVersion,
2 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
)),
ts('PHP Up-to-Date'),
\Psr\Log\LogLevel::INFO,
'fa-server'
);
}
elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER) >= 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('This system uses PHP version %1. This meets the minimum recommendations and you do not need to upgrade immediately, but the preferred version is %2.',
array(
1 => $phpVersion,
2 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
)),
ts('PHP Out-of-Date'),
\Psr\Log\LogLevel::NOTICE,
'fa-server'
);
}
elseif (version_compare($phpVersion, CRM_Upgrade_Incremental_General::MIN_INSTALL_PHP_VER) >= 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('This system uses PHP version %1. This meets the minimum requirements for CiviCRM to function but is not recommended. At least PHP version %2 is recommended; the preferrred version is %3.',
array(
1 => $phpVersion,
2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
3 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
)),
ts('PHP Out-of-Date'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
}
else {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('This system uses PHP version %1. To ensure the continued operation of CiviCRM, upgrade your server now. At least PHP version %2 is recommended; the preferrred version is %3.',
array(
1 => $phpVersion,
2 => CRM_Upgrade_Incremental_General::MIN_RECOMMENDED_PHP_VER,
3 => CRM_Upgrade_Incremental_General::RECOMMENDED_PHP_VER,
)),
ts('PHP Out-of-Date'),
\Psr\Log\LogLevel::ERROR,
'fa-server'
);
}
return $messages;
}
/**
* @return array
*/
public function checkPhpMysqli() {
$messages = array();
if (!extension_loaded('mysqli')) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Future versions of CiviCRM may require the PHP extension "%2". To ensure that your system will be compatible, please install it in advance. For more explanation, see <a href="%1">the announcement</a>.',
array(
1 => 'https://civicrm.org/blog/totten/psa-please-verify-php-extension-mysqli',
2 => 'mysqli',
)),
ts('Forward Compatibility: Enable "mysqli"'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
}
return $messages;
}
/**
* Check that the MySQL time settings match the PHP time settings.
*
* @return array<CRM_Utils_Check_Message> an empty array, or a list of warnings
*/
public function checkMysqlTime() {
//CRM-19115 - Always set MySQL time before checking it.
CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
$messages = array();
$phpNow = date('Y-m-d H:i');
$sqlNow = CRM_Core_DAO::singleValueQuery("SELECT date_format(now(), '%Y-%m-%d %H:%i')");
if (!CRM_Utils_Time::isEqual($phpNow, $sqlNow, 2.5 * 60)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Timestamps reported by MySQL (eg "%2") and PHP (eg "%3" ) are mismatched.<br /><a href="%1">Read more about this warning</a>', array(
1 => CRM_Utils_System::getWikiBaseURL() . 'checkMysqlTime',
2 => $sqlNow,
3 => $phpNow,
)),
ts('Timestamp Mismatch'),
\Psr\Log\LogLevel::ERROR,
'fa-server'
);
}
return $messages;
}
/**
* @return array
*/
public function checkDebug() {
$config = CRM_Core_Config::singleton();
if ($config->debug) {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Warning: Debug is enabled in <a href="%1">system settings</a>. This should not be enabled on production servers.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/debug', 'reset=1'))),
ts('Debug Mode Enabled'),
\Psr\Log\LogLevel::WARNING,
'fa-bug'
);
$message->addAction(
ts('Disable Debug Mode'),
ts('Disable debug mode now?'),
'api3',
array('Setting', 'create', array('debug_enabled' => 0))
);
return array($message);
}
return array();
}
/**
* @return array
*/
public function checkOutboundMail() {
$messages = array();
$mailingInfo = Civi::settings()->get('mailing_backend');
if (($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB
|| (defined('CIVICRM_MAIL_LOG') && CIVICRM_MAIL_LOG)
|| $mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED
|| $mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MOCK)
) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Warning: Outbound email is disabled in <a href="%1">system settings</a>. Proper settings should be enabled on production servers.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))),
ts('Outbound Email Disabled'),
\Psr\Log\LogLevel::WARNING,
'fa-envelope'
);
}
return $messages;
}
/**
* Check that domain email and org name are set
* @return array
*/
public function checkDomainNameEmail() {
$messages = array();
list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail(TRUE);
$domain = CRM_Core_BAO_Domain::getDomain();
$domainName = $domain->name;
$fixEmailUrl = CRM_Utils_System::url("civicrm/admin/domain", "action=update&reset=1");
if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
if (!$domainName || $domainName == 'Default Domain Name') {
$msg = ts("Please enter your organization's <a href=\"%1\">name, primary address, and default FROM Email Address</a> (for system-generated emails).",
array(1 => $fixEmailUrl));
}
else {
$msg = ts('Please enter a <a href="%1">default FROM Email Address</a> (for system-generated emails).',
array(1 => $fixEmailUrl));
}
}
elseif (!$domainName || $domainName == 'Default Domain Name') {
$msg = ts("Please enter your organization's <a href=\"%1\">name and primary address</a>.",
array(1 => $fixEmailUrl));
}
if (!empty($msg)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
$msg,
ts('Complete Setup'),
\Psr\Log\LogLevel::WARNING,
'fa-check-square-o'
);
}
return $messages;
}
/**
* Checks if a default bounce handling mailbox is set up
* @return array
*/
public function checkDefaultMailbox() {
$messages = array();
$config = CRM_Core_Config::singleton();
if (in_array('CiviMail', $config->enableComponents) &&
CRM_Core_BAO_MailSettings::defaultDomain() == "EXAMPLE.ORG"
) {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Please configure a <a href="%1">default mailbox</a> for CiviMail.',
array(1 => CRM_Utils_System::url('civicrm/admin/mailSettings', "reset=1"))),
ts('Configure Default Mailbox'),
\Psr\Log\LogLevel::WARNING,
'fa-envelope'
);
$docUrl = 'target="_blank" href="' . CRM_Utils_System::docURL(array('page' => 'user/advanced-configuration/email-system-configuration/', 'URLonly' => TRUE)) . '""';
$message->addHelp(
ts('A default mailbox must be configured for email bounce processing.') . '<br />' .
ts("Learn more in the <a %1>online documentation</a>.", array(1 => $docUrl))
);
$messages[] = $message;
}
return $messages;
}
/**
* Checks if cron has run in a reasonable amount of time
* @return array
*/
public function checkLastCron() {
$messages = array();
$statusPreference = new CRM_Core_DAO_StatusPreference();
$statusPreference->domain_id = CRM_Core_Config::domainID();
$statusPreference->name = 'checkLastCron';
if ($statusPreference->find(TRUE) && !empty($statusPreference->check_info)) {
$lastCron = $statusPreference->check_info;
$msg = ts('Last cron run at %1.', array(1 => CRM_Utils_Date::customFormat(date('c', $lastCron))));
}
else {
$lastCron = 0;
$msg = ts('No cron runs have been recorded.');
}
if ($lastCron > gmdate('U') - 3600) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
$msg,
ts('Cron Running OK'),
\Psr\Log\LogLevel::INFO,
'fa-clock-o'
);
}
else {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
$msg,
ts('Cron Not Running'),
($lastCron > gmdate('U') - 86400) ? \Psr\Log\LogLevel::WARNING : \Psr\Log\LogLevel::ERROR,
'fa-clock-o'
);
$docUrl = 'target="_blank" href="' . CRM_Utils_System::docURL(array('resource' => 'wiki', 'page' => 'Managing Scheduled Jobs', 'URLonly' => TRUE)) . '""';
$message->addHelp(
ts('Configuring cron on your server is necessary for running scheduled jobs such as sending mail and scheduled reminders.') . '<br />' .
ts("Learn more in the <a %1>online documentation</a>.", array(1 => $docUrl))
);
$messages[] = $message;
}
return $messages;
}
/**
* Recommend that sites use path-variables for their directories and URLs.
* @return array
*/
public function checkUrlVariables() {
$messages = array();
$hasOldStyle = FALSE;
$settingNames = array(
'userFrameworkResourceURL',
'imageUploadURL',
'customCSSURL',
'extensionsURL',
);
foreach ($settingNames as $settingName) {
$settingValue = Civi::settings()->get($settingName);
if (!empty($settingValue) && $settingValue{0} != '[') {
$hasOldStyle = TRUE;
break;
}
}
if ($hasOldStyle) {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('<a href="%1">Resource URLs</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each URL (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/url', "reset=1"))),
ts('Resource URLs: Make them portable'),
\Psr\Log\LogLevel::NOTICE,
'fa-server'
);
$messages[] = $message;
}
return $messages;
}
/**
* Recommend that sites use path-variables for their directories and URLs.
* @return array
*/
public function checkDirVariables() {
$messages = array();
$hasOldStyle = FALSE;
$settingNames = array(
'uploadDir',
'imageUploadDir',
'customFileUploadDir',
'customTemplateDir',
'customPHPPathDir',
'extensionsDir',
);
foreach ($settingNames as $settingName) {
$settingValue = Civi::settings()->get($settingName);
if (!empty($settingValue) && $settingValue{0} != '[') {
$hasOldStyle = TRUE;
break;
}
}
if ($hasOldStyle) {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('<a href="%1">Directories</a> may use absolute paths, relative paths, or variables. Absolute paths are more difficult to maintain. To maximize portability, consider using a variable in each directory (eg "<tt>[cms.root]</tt>" or "<tt>[civicrm.files]</tt>").',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/path', "reset=1"))),
ts('Directory Paths: Make them portable'),
\Psr\Log\LogLevel::NOTICE,
'fa-server'
);
$messages[] = $message;
}
return $messages;
}
/**
* Check that important directories are writable.
*
* @return array
* Any CRM_Utils_Check_Message instances that need to be generated.
*/
public function checkDirsWritable() {
$notWritable = array();
$config = CRM_Core_Config::singleton();
$directories = array(
'uploadDir' => ts('Temporary Files Directory'),
'imageUploadDir' => ts('Images Directory'),
'customFileUploadDir' => ts('Custom Files Directory'),
'extensionsDir' => ts('CiviCRM Extensions Directory'),
);
foreach ($directories as $directory => $label) {
$file = CRM_Utils_File::createFakeFile($config->$directory);
if ($file === FALSE) {
$notWritable[] = "$label ({$config->$directory})";
}
else {
$dirWithSlash = CRM_Utils_File::addTrailingSlash($config->$directory);
unlink($dirWithSlash . $file);
}
}
$messages = array();
if (!empty($notWritable)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The %1 is not writable. Please check your file permissions.', array(
1 => implode(', ', $notWritable),
'count' => count($notWritable),
'plural' => 'The following directories are not writable: %1. Please check your file permissions.',
)),
ts('Directory not writable', array(
'count' => count($notWritable),
'plural' => 'Directories not writable',
)),
\Psr\Log\LogLevel::ERROR,
'fa-ban'
);
}
return $messages;
}
/**
* Checks if new versions are available
* @return array
*/
public function checkVersion() {
$messages = array();
try {
$vc = new CRM_Utils_VersionCheck();
$vc->initialize();
}
catch (Exception $e) {
$messages[] = new CRM_Utils_Check_Message(
'checkVersionError',
ts('Directory %1 is not writable. Please change your file permissions.',
array(1 => dirname($vc->cacheFile))),
ts('Directory not writable'),
\Psr\Log\LogLevel::ERROR,
'fa-times-circle-o'
);
return $messages;
}
// Show a notice if the version_check job is disabled
if (empty($vc->cronJob['is_active'])) {
$args = empty($vc->cronJob['id']) ? array('reset' => 1) : array('reset' => 1, 'action' => 'update', 'id' => $vc->cronJob['id']);
$messages[] = new CRM_Utils_Check_Message(
'checkVersionDisabled',
ts('The check for new versions of CiviCRM has been disabled. <a %1>Re-enable the scheduled job</a> to receive important security update notifications.', array(1 => 'href="' . CRM_Utils_System::url('civicrm/admin/job', $args) . '"')),
ts('Update Check Disabled'),
\Psr\Log\LogLevel::NOTICE,
'fa-times-circle-o'
);
}
if ($vc->isInfoAvailable) {
$newerVersion = $vc->isNewerVersionAvailable();
if ($newerVersion['version']) {
$vInfo = array(
1 => $newerVersion['version'],
2 => $vc->localVersion,
);
// LTS = long-term support version
if ($newerVersion['status'] == 'lts') {
$vInfo[1] .= ' ' . ts('(long-term support)');
}
if ($newerVersion['upgrade'] == 'security') {
// Security
$severity = \Psr\Log\LogLevel::CRITICAL;
$title = ts('CiviCRM Security Update Required');
$message = ts('New security release %1 is available. The site is currently running %2.', $vInfo);
}
elseif ($newerVersion['status'] == 'eol') {
// Warn about EOL
$severity = \Psr\Log\LogLevel::WARNING;
$title = ts('CiviCRM Update Needed');
$message = ts('New version %1 is available. The site is currently running %2, which has reached its end of life.', $vInfo);
}
else {
// For most new versions, just make them notice
$severity = \Psr\Log\LogLevel::NOTICE;
$title = ts('CiviCRM Update Available');
$message = ts('New version %1 is available. The site is currently running %2.', $vInfo);
}
}
elseif (!empty($vc->cronJob['is_active'])) {
$vNum = $vc->localVersion;
// LTS = long-term support version
if ($newerVersion['status'] == 'lts') {
$vNum .= ' ' . ts('(long-term support)');
}
$severity = \Psr\Log\LogLevel::INFO;
$title = ts('CiviCRM Up-to-Date');
$message = ts('CiviCRM version %1 is up-to-date.', array(1 => $vNum));
}
if (!empty($message)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
$message,
$title,
$severity,
'fa-cloud-upload'
);
}
}
return $messages;
}
/**
* Checks if extensions are set up properly
* @return array
*/
public function checkExtensions() {
$messages = array();
$extensionSystem = CRM_Extension_System::singleton();
$mapper = $extensionSystem->getMapper();
$manager = $extensionSystem->getManager();
if ($extensionSystem->getDefaultContainer()) {
$basedir = $extensionSystem->getDefaultContainer()->baseDir;
}
if (empty($basedir)) {
// no extension directory
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Your extensions directory is not set. Click <a href="%1">here</a> to set the extensions directory.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/path', 'reset=1'))),
ts('Directory not writable'),
\Psr\Log\LogLevel::NOTICE,
'fa-plug'
);
return $messages;
}
if (!is_dir($basedir)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Your extensions directory path points to %1, which is not a directory. Please check your file system.',
array(1 => $basedir)),
ts('Extensions directory incorrect'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
return $messages;
}
elseif (!is_writable($basedir)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Directory %1 is not writable. Please change your file permissions.',
array(1 => $basedir)),
ts('Directory not writable'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
return $messages;
}
if (empty($extensionSystem->getDefaultContainer()->baseUrl)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The extensions URL is not properly set. Please go to the <a href="%1">URL setting page</a> and correct it.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/url', 'reset=1'))),
ts('Extensions url missing'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
return $messages;
}
if (!$extensionSystem->getBrowser()->isEnabled()) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Not checking remote URL for extensions since ext_repo_url is set to false.'),
ts('Extensions check disabled'),
\Psr\Log\LogLevel::NOTICE,
'fa-plug'
);
return $messages;
}
try {
$remotes = $extensionSystem->getBrowser()->getExtensions();
}
catch (CRM_Extension_Exception $e) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
$e->getMessage(),
ts('Extension download error'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
return $messages;
}
if (!$remotes) {
// CRM-13141 There may not be any compatible extensions available for the requested CiviCRM version + CMS. If so, $extdir is empty so just return a notice.
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('There are currently no extensions on the CiviCRM public extension directory which are compatible with version %1. If you want to install an extension which is not marked as compatible, you may be able to <a %2>download and install extensions manually</a> (depending on access to your web server).', array(
1 => CRM_Utils_System::majorVersion(),
2 => 'href="http://wiki.civicrm.org/confluence/display/CRMDOC/Extensions"',
)),
ts('No Extensions Available for this Version'),
\Psr\Log\LogLevel::NOTICE,
'fa-plug'
);
return $messages;
}
$keys = array_keys($manager->getStatuses());
sort($keys);
$updates = $errors = $okextensions = array();
foreach ($keys as $key) {
try {
$obj = $mapper->keyToInfo($key);
}
catch (CRM_Extension_Exception $ex) {
$errors[] = ts('Failed to read extension (%1). Please refresh the extension list.', array(1 => $key));
continue;
}
$row = CRM_Admin_Page_Extensions::createExtendedInfo($obj);
switch ($row['status']) {
case CRM_Extension_Manager::STATUS_INSTALLED_MISSING:
$errors[] = ts('%1 extension (%2) is installed but missing files.', array(1 => CRM_Utils_Array::value('label', $row), 2 => $key));
break;
case CRM_Extension_Manager::STATUS_INSTALLED:
if (!empty($remotes[$key]) && version_compare($row['version'], $remotes[$key]->version, '<')) {
$updates[] = ts('%1 (%2) version %3 is installed. <a %4>Upgrade to version %5</a>.', array(
1 => CRM_Utils_Array::value('label', $row),
2 => $key,
3 => $row['version'],
4 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', "action=update&id=$key&key=$key") . '"',
5 => $remotes[$key]->version,
));
}
else {
if (empty($row['label'])) {
$okextensions[] = $key;
}
else {
$okextensions[] = ts('%1 (%2) version %3', array(
1 => $row['label'],
2 => $key,
3 => $row['version'],
));
}
}
break;
}
}
if (!$okextensions && !$updates && !$errors) {
$messages[] = new CRM_Utils_Check_Message(
'extensionsOk',
ts('No extensions installed. <a %1>Browse available extensions</a>.', array(
1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"',
)),
ts('Extensions'),
\Psr\Log\LogLevel::INFO,
'fa-plug'
);
}
if ($errors) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
'<ul><li>' . implode('</li><li>', $errors) . '</li></ul>',
ts('Extension Error'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
}
if ($updates) {
$messages[] = new CRM_Utils_Check_Message(
'extensionUpdates',
'<ul><li>' . implode('</li><li>', $updates) . '</li></ul>',
ts('Extension Update Available', array('plural' => '%count Extension Updates Available', 'count' => count($updates))),
\Psr\Log\LogLevel::WARNING,
'fa-plug'
);
}
if ($okextensions) {
if ($updates || $errors) {
$message = ts('1 extension is up-to-date:', array('plural' => '%count extensions are up-to-date:', 'count' => count($okextensions)));
}
else {
$message = ts('All extensions are up-to-date:');
}
$messages[] = new CRM_Utils_Check_Message(
'extensionsOk',
$message . '<ul><li>' . implode('</li><li>', $okextensions) . '</li></ul>',
ts('Extensions'),
\Psr\Log\LogLevel::INFO,
'fa-plug'
);
}
return $messages;
}
/**
* Checks if there are pending extension upgrades.
*
* @return array
*/
public function checkExtensionUpgrades() {
if (CRM_Extension_Upgrades::hasPending()) {
$message = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Extension upgrades should be run as soon as possible.'),
ts('Extension Upgrades Pending'),
\Psr\Log\LogLevel::ERROR,
'fa-plug'
);
$message->addAction(
ts('Run Upgrades'),
ts('Run extension upgrades now?'),
'href',
array('path' => 'civicrm/admin/extensions/upgrade', 'query' => array('reset' => 1, 'destination' => CRM_Utils_System::url('civicrm/a/#/status')))
);
return array($message);
}
return array();
}
/**
* Checks if CiviCRM database version is up-to-date
* @return array
*/
public function checkDbVersion() {
$messages = array();
$dbVersion = CRM_Core_BAO_Domain::version();
$upgradeUrl = CRM_Utils_System::url("civicrm/upgrade", "reset=1");
if (!$dbVersion) {
// if db.ver missing
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Version information found to be missing in database. You will need to determine the correct version corresponding to your current database state.'),
ts('Database Version Missing'),
\Psr\Log\LogLevel::ERROR,
'fa-database'
);
}
elseif (!CRM_Utils_System::isVersionFormatValid($dbVersion)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.'),
ts('Database Version Invalid'),
\Psr\Log\LogLevel::ERROR,
'fa-database'
);
}
elseif (stripos($dbVersion, 'upgrade')) {
// if db.ver indicates a partially upgraded db
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Database check failed - the database looks to have been partially upgraded. You must reload the database with the backup and try the <a href=\'%1\'>upgrade process</a> again.', array(1 => $upgradeUrl)),
ts('Database Partially Upgraded'),
\Psr\Log\LogLevel::ALERT,
'fa-database'
);
}
else {
$codeVersion = CRM_Utils_System::version();
// if db.ver < code.ver, time to upgrade
if (version_compare($dbVersion, $codeVersion) < 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('New codebase version detected. You must visit <a href=\'%1\'>upgrade screen</a> to upgrade the database.', array(1 => $upgradeUrl)),
ts('Database Upgrade Required'),
\Psr\Log\LogLevel::ALERT,
'fa-database'
);
}
// if db.ver > code.ver, sth really wrong
if (version_compare($dbVersion, $codeVersion) > 0) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Your database is marked with an unexpected version number: %1. The v%2 codebase may not be compatible with your database state.
You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase
you were using until you resolve this problem.<br/>OR if this is a manual install from git, you might want to fix civicrm-version.php file.',
array(1 => $dbVersion, 2 => $codeVersion)
),
ts('Database In Unexpected Version'),
\Psr\Log\LogLevel::ERROR,
'fa-database'
);
}
}
return $messages;
}
/**
* ensure that all CiviCRM tables are InnoDB
* @return array
*/
public function checkDbEngine() {
$messages = array();
if (CRM_Core_DAO::isDBMyISAM(150)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Your database is configured to use the MyISAM database engine. CiviCRM requires InnoDB. You will need to convert any MyISAM tables in your database to InnoDB. Using MyISAM tables will result in data integrity issues.'),
ts('MyISAM Database Engine'),
\Psr\Log\LogLevel::ERROR,
'fa-database'
);
}
return $messages;
}
/**
* Check for required mbstring extension
* @return array
*/
public function checkMbstring() {
$messages = array();
if (!function_exists('mb_substr')) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The PHP Multibyte String extension is needed for CiviCRM to correctly handle user input among other functionality. Ask your system administrator to install it.'),
ts('Missing mbstring Extension'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
}
return $messages;
}
/**
* Check if environment is Production.
* @return array
*/
public function checkEnvironment() {
$messages = array();
$environment = CRM_Core_Config::environment();
if ($environment != 'Production') {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The environment of this CiviCRM instance is set to \'%1\'. Certain functionality like scheduled jobs has been disabled.', array(1 => $environment)),
ts('Non-Production Environment'),
\Psr\Log\LogLevel::ALERT,
'fa-bug'
);
}
return $messages;
}
/**
* Check that the resource URL points to the correct location.
* @return array
*/
public function checkResourceUrl() {
$messages = array();
// Skip when run during unit tests, you can't check without a CMS.
if (CRM_Core_Config::singleton()->userFramework == 'UnitTests') {
return $messages;
}
// Does arrow.png exist where we expect it?
$arrowUrl = CRM_Core_Config::singleton()->userFrameworkResourceURL . 'packages/jquery/css/images/arrow.png';
$headers = get_headers($arrowUrl);
$fileExists = stripos($headers[0], "200 OK") ? 1 : 0;
if (!$fileExists) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The Resource URL is not set correctly. Please set the <a href="%1">CiviCRM Resource URL</a>.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/url', 'reset=1'))),
ts('Incorrect Resource URL'),
\Psr\Log\LogLevel::ERROR,
'fa-server'
);
}
return $messages;
}
}

View file

@ -0,0 +1,55 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_FinancialTypeAcls extends CRM_Utils_Check_Component {
public static function checkFinancialAclReport() {
$messages = array();
$ftAclSetting = Civi::settings()->get('acl_financial_type');
$financialAclExtension = civicrm_api3('extension', 'get', array('key' => 'biz.jmaconsulting.financialaclreport'));
if ($ftAclSetting && (($financialAclExtension['count'] == 1 && $financialAclExtension['status'] != 'Installed') || $financialAclExtension['count'] !== 1)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('CiviCRM will in the future require the extension %1 for CiviCRM Reports to work correctly with the Financial Type ACLs. The extension can be downloaded <a href="%2">here</a>', array(
1 => 'biz.jmaconsulting.financialaclreport',
2 => 'https://github.com/JMAConsulting/biz.jmaconsulting.financialaclreport',
)),
ts('Extension Missing'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
}
return $messages;
}
}

View file

@ -0,0 +1,85 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_OptionGroups extends CRM_Utils_Check_Component {
/**
* @return array
*/
public function checkOptionGroupValues() {
$messages = array();
$problemValues = array();
$optionGroups = civicrm_api3('OptionGroup', 'get', array(
'sequential' => 1,
'data_type' => array('IS NOT NULL' => 1),
'options' => array('limit' => 0),
));
if ($optionGroups['count'] > 0) {
foreach ($optionGroups['values'] as $optionGroup) {
$values = CRM_Core_BAO_OptionValue::getOptionValuesArray($optionGroup['id']);
if (count($values) > 0) {
foreach ($values as $value) {
$validate = CRM_Utils_Type::validate($value['value'], $optionGroup['data_type'], FALSE);
if (!$validate) {
$problemValues[] = array(
'group_name' => $optionGroup['title'],
'value_name' => $value['label'],
);
}
}
}
}
}
if (!empty($problemValues)) {
$strings = '';
foreach ($problemValues as $problemValue) {
$strings .= ts('<tr><td> "%1" </td><td> "%2" </td></tr>', array(
1 => $problemValue['group_name'],
2 => $problemValue['value_name'],
));
}
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The Following Option Values contain value fields that do not match the Data Type of the Option Group</p>
<p><table><tbody><th>Option Group</th><th>Option Value</th></tbody><tbody>') .
$strings . ts('</tbody></table></p>'),
ts('Option Values with problematic Values'),
\Psr\Log\LogLevel::NOTICE,
'fa-server'
);
}
return $messages;
}
}

View file

@ -0,0 +1,76 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2016 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2016
*/
class CRM_Utils_Check_Component_PriceFields extends CRM_Utils_Check_Component {
/**
* Display warning about invalid priceFields
*
*/
public function checkPriceFields() {
$sql = "SELECT DISTINCT ps.title as ps_title, ps.id as ps_id, psf.label as psf_label
FROM civicrm_price_set ps
INNER JOIN civicrm_price_field psf ON psf.price_set_id = ps.id
INNER JOIN civicrm_price_field_value pfv ON pfv.price_field_id = psf.id
LEFT JOIN civicrm_financial_type cft ON cft.id = pfv.financial_type_id
WHERE cft.id IS NULL OR cft.is_active = 0";
$dao = CRM_Core_DAO::executeQuery($sql);
$count = 0;
$html = '';
$messages = array();
while ($dao->fetch()) {
$count++;
$url = CRM_Utils_System::url('civicrm/admin/price/field', array(
'reset' => 1,
'action' => 'browse',
'sid' => $dao->ps_id));
$html .= "<tr><td>$dao->ps_title</td><td>$dao->psf_label</td><td><a href='$url'>View Price Set Fields</a></td></tr>";
}
if ($count > 0) {
$msg = "<p>the following Price Set Fields use disabled or invalid financial types and need to be fixed if they are to still be used.<p>
<p><table><thead><tr><th>Price Set</th><th>Price Set Field</th><th>Action Link</th>
</tr></thead><tbody>
$html
</tbody></table></p>";
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts($msg),
ts('Invalid Price Fields'),
\Psr\Log\LogLevel::WARNING,
'fa-lock'
);
}
return $messages;
}
}

View file

@ -0,0 +1,99 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2016 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2016
*/
class CRM_Utils_Check_Component_Schema extends CRM_Utils_Check_Component {
/**
* @return array
*/
public function checkIndices() {
$messages = array();
$missingIndices = CRM_Core_BAO_SchemaHandler::getMissingIndices();
if ($missingIndices) {
$html = '';
foreach ($missingIndices as $tableName => $indices) {
foreach ($indices as $index) {
$fields = implode(', ', $index['field']);
$html .= "<tr><td>{$tableName}</td><td>{$index['name']}</td><td>$fields</td>";
}
}
$message = "<p>The following tables have missing indices. Click 'Update Indices' button to create them.<p>
<p><table><thead><tr><th>Table Name</th><th>Key Name</th><th>Expected Indices</th>
</tr></thead><tbody>
$html
</tbody></table></p>";
$msg = new CRM_Utils_Check_Message(
__FUNCTION__,
ts($message),
ts('Performance warning: Missing indices'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
$msg->addAction(
ts('Update Indices'),
ts('Update all database indices now? This may take a few minutes and cause a noticeable performance lag for all users while running.'),
'api3',
array('System', 'updateindexes')
);
$messages[] = $msg;
}
return $messages;
}
/**
* @return array
*/
public function checkMissingLogTables() {
$messages = array();
$logging = new CRM_Logging_Schema();
$missingLogTables = $logging->getMissingLogTables();
if ($missingLogTables) {
$msg = new CRM_Utils_Check_Message(
__FUNCTION__,
ts("You don't have logging enabled on some tables. This may cause errors on performing insert/update operation on them."),
ts('Missing Log Tables'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
$msg->addAction(
ts('Create Missing Log Tables'),
ts('Create missing log tables now? This may take few minutes.'),
'api3',
array('System', 'createmissinglogtables')
);
$messages[] = $msg;
}
return $messages;
}
}

View file

@ -0,0 +1,407 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_Security extends CRM_Utils_Check_Component {
/**
* CMS have a different pattern to their default file path and URL.
*
* @todo Use Civi::paths instead?
*/
public function getFilePathMarker() {
$config = CRM_Core_Config::singleton();
switch ($config->userFramework) {
case 'Joomla':
return '/media/';
default:
return '/files/';
}
}
/**
* Check if our logfile is directly accessible.
*
* Per CiviCRM default the logfile sits in a folder which is
* web-accessible, and is protected by a default .htaccess
* configuration. If server config causes the .htaccess not to
* function as intended, there may be information disclosure.
*
* The debug log may be jam-packed with sensitive data, we don't
* want that.
*
* Being able to be retrieved directly doesn't mean the logfile
* is browseable or visible to search engines; it means it can be
* requested directly.
*
* @return array
* Array of messages
* @see CRM-14091
*/
public function checkLogFileIsNotAccessible() {
$messages = array();
$config = CRM_Core_Config::singleton();
$log = CRM_Core_Error::createDebugLogger();
$log_filename = str_replace('\\', '/', $log->_filename);
$filePathMarker = $this->getFilePathMarker();
// Hazard a guess at the URL of the logfile, based on common
// CiviCRM layouts.
if ($upload_url = explode($filePathMarker, $config->imageUploadURL)) {
$url[] = $upload_url[0];
if ($log_path = explode($filePathMarker, $log_filename)) {
// CRM-17149: check if debug log path includes $filePathMarker
if (count($log_path) > 1) {
$url[] = $log_path[1];
$log_url = implode($filePathMarker, $url);
$headers = @get_headers($log_url);
if (stripos($headers[0], '200')) {
$docs_url = $this->createDocUrl('checkLogFileIsNotAccessible');
$msg = 'The <a href="%1">CiviCRM debug log</a> should not be downloadable.'
. '<br />' .
'<a href="%2">Read more about this warning</a>';
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts($msg, array(1 => $log_url, 2 => $docs_url)),
ts('Security Warning'),
\Psr\Log\LogLevel::WARNING,
'fa-lock'
);
}
}
}
}
return $messages;
}
/**
* Check if our uploads directory has accessible files.
*
* We'll test a handful of files randomly. Hazard a guess at the URL
* of the uploads dir, based on common CiviCRM layouts. Try and
* request the files, and if any are successfully retrieved, warn.
*
* Being retrievable doesn't mean the files are browseable or visible
* to search engines; it only means they can be requested directly.
*
* @return array
* Array of messages
* @see CRM-14091
*
* @todo Test with WordPress, Joomla.
*/
public function checkUploadsAreNotAccessible() {
$messages = array();
$config = CRM_Core_Config::singleton();
$privateDirs = array(
$config->uploadDir,
$config->customFileUploadDir,
);
foreach ($privateDirs as $privateDir) {
$heuristicUrl = $this->guessUrl($privateDir);
if ($this->isDirAccessible($privateDir, $heuristicUrl)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Files in the data directory (<a href="%3">%2</a>) should not be downloadable.'
. '<br />'
. '<a href="%1">Read more about this warning</a>',
array(
1 => $this->createDocUrl('checkUploadsAreNotAccessible'),
2 => $privateDir,
3 => $heuristicUrl,
)),
ts('Private Files Readable'),
\Psr\Log\LogLevel::WARNING,
'fa-lock'
);
}
}
return $messages;
}
/**
* Check if our uploads or ConfigAndLog directories have browseable
* listings.
*
* Retrieve a listing of files from the local filesystem, and the
* corresponding path via HTTP. Then check and see if the local
* files are represented in the HTTP result; if so then warn. This
* MAY trigger false positives (if you have files named 'a', 'e'
* we'll probably match that).
*
* @return array
* Array of messages
* @see CRM-14091
*
* @todo Test with WordPress, Joomla.
*/
public function checkDirectoriesAreNotBrowseable() {
$messages = array();
$config = CRM_Core_Config::singleton();
$publicDirs = array(
$config->imageUploadDir => $config->imageUploadURL,
);
// Setup index.html files to prevent browsing
foreach ($publicDirs as $publicDir => $publicUrl) {
CRM_Utils_File::restrictBrowsing($publicDir);
}
// Test that $publicDir is not browsable
foreach ($publicDirs as $publicDir => $publicUrl) {
if ($this->isBrowsable($publicDir, $publicUrl)) {
$msg = 'Directory <a href="%1">%2</a> should not be browseable via the web.'
. '<br />' .
'<a href="%3">Read more about this warning</a>';
$docs_url = $this->createDocUrl('checkDirectoriesAreNotBrowseable');
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts($msg, array(1 => $publicDir, 2 => $publicDir, 3 => $docs_url)),
ts('Browseable Directories'),
\Psr\Log\LogLevel::ERROR,
'fa-lock'
);
}
}
return $messages;
}
/**
* Check that some files are not present.
*
* These files have generally been deleted but Civi source tree but could be
* left online if one does a faulty upgrade.
*
* @return array of messages
*/
public function checkFilesAreNotPresent() {
global $civicrm_root;
$messages = array();
$files = array(
array(
// CRM-16005, upgraded from Civi <= 4.5.6
"{$civicrm_root}/packages/dompdf/dompdf.php",
\Psr\Log\LogLevel::CRITICAL,
),
array(
// CRM-16005, Civi >= 4.5.7
"{$civicrm_root}/packages/vendor/dompdf/dompdf/dompdf.php",
\Psr\Log\LogLevel::CRITICAL,
),
array(
// CRM-16005, Civi >= 4.6.0
"{$civicrm_root}/vendor/dompdf/dompdf/dompdf.php",
\Psr\Log\LogLevel::CRITICAL,
),
array(
// CIVI-SA-2013-001
"{$civicrm_root}/packages/OpenFlashChart/php-ofc-library/ofc_upload_image.php",
\Psr\Log\LogLevel::CRITICAL,
),
array(
"{$civicrm_root}/packages/html2text/class.html2text.inc",
\Psr\Log\LogLevel::CRITICAL,
),
);
foreach ($files as $file) {
if (file_exists($file[0])) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('File \'%1\' presents a security risk and should be deleted.', array(1 => $file[0])),
ts('Unsafe Files'),
$file[1],
'fa-lock'
);
}
}
return $messages;
}
/**
* Discourage use of remote profile forms.
*/
public function checkRemoteProfile() {
$messages = array();
if (Civi::settings()->get('remote_profile_submissions')) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('Warning: External profile support (aka "HTML Snippet" support) is enabled in <a href="%1">system settings</a>. This setting may be prone to abuse. If you must retain it, consider HTTP throttling or other protections.',
array(1 => CRM_Utils_System::url('civicrm/admin/setting/misc', 'reset=1'))
),
ts('Remote Profiles Enabled'),
\Psr\Log\LogLevel::WARNING,
'fa-lock'
);
}
return $messages;
}
/**
* Check that the sysadmin has not modified the Cxn
* security setup.
*/
public function checkCxnOverrides() {
$list = array();
if (defined('CIVICRM_CXN_CA') && CIVICRM_CXN_CA !== 'CiviRootCA') {
$list[] = 'CIVICRM_CXN_CA';
}
if (defined('CIVICRM_CXN_APPS_URL') && CIVICRM_CXN_APPS_URL !== \Civi\Cxn\Rpc\Constants::OFFICIAL_APPMETAS_URL) {
$list[] = 'CIVICRM_CXN_APPS_URL';
}
$messages = array();
if (!empty($list)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The system administrator has disabled security settings (%1). Connections to remote applications are insecure.', array(
1 => implode(', ', $list),
)),
ts('Security Warning'),
\Psr\Log\LogLevel::WARNING,
'fa-lock'
);
}
return $messages;
}
/**
* Determine whether $url is a public, browsable listing for $dir
*
* @param string $dir
* Local dir path.
* @param string $url
* Public URL.
* @return bool
*/
public function isBrowsable($dir, $url) {
if (empty($dir) || empty($url) || !is_dir($dir)) {
return FALSE;
}
$result = FALSE;
// this could be a new system with no uploads (yet) -- so we'll make a file
$file = CRM_Utils_File::createFakeFile($dir);
if ($file === FALSE) {
// Couldn't write the file
return FALSE;
}
$content = @file_get_contents("$url");
if (stristr($content, $file)) {
$result = TRUE;
}
unlink("$dir/$file");
return $result;
}
/**
* Determine whether $url is a public version of $dir in which files
* are remotely accessible.
*
* @param string $dir
* Local dir path.
* @param string $url
* Public URL.
* @return bool
*/
public function isDirAccessible($dir, $url) {
$url = rtrim($url, '/');
if (empty($dir) || empty($url) || !is_dir($dir)) {
return FALSE;
}
$result = FALSE;
$file = CRM_Utils_File::createFakeFile($dir, 'delete me');
if ($file === FALSE) {
// Couldn't write the file
return FALSE;
}
$headers = @get_headers("$url/$file");
if (stripos($headers[0], '200')) {
$content = @file_get_contents("$url/$file");
if (preg_match('/delete me/', $content)) {
$result = TRUE;
}
}
unlink("$dir/$file");
return $result;
}
/**
* @param $topic
*
* @return string
*/
public function createDocUrl($topic) {
return CRM_Utils_System::getWikiBaseURL() . $topic;
}
/**
* Make a guess about the URL that corresponds to $targetDir.
*
* @param string $targetDir
* Local path to a directory.
* @return string
* a guessed URL for $realDir
*/
public function guessUrl($targetDir) {
$filePathMarker = $this->getFilePathMarker();
$config = CRM_Core_Config::singleton();
list($heuristicBaseUrl) = explode($filePathMarker, $config->imageUploadURL);
list(, $heuristicSuffix) = array_pad(explode($filePathMarker, str_replace('\\', '/', $targetDir)), 2, '');
return $heuristicBaseUrl . $filePathMarker . $heuristicSuffix;
}
}

View file

@ -0,0 +1,122 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_Source extends CRM_Utils_Check_Component {
public function getRemovedFiles() {
$files[] = '[civicrm.packages]/Auth/SASL';
$files[] = '[civicrm.packages]/Auth/SASL.php';
$files[] = '[civicrm.packages]/Net/SMTP.php';
$files[] = '[civicrm.packages]/Net/Socket.php';
$files[] = '[civicrm.packages]/_ORIGINAL_/Net/SMTP.php';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/Readme.md';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/license.txt';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/css/jquery.dataTables.css';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/css/jquery.dataTables.min.css';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/css/jquery.dataTables_themeroller.css';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/js/jquery.dataTables.js';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/js/jquery.dataTables.min.js';
$files[] = '[civicrm.packages]/jquery/plugins/DataTables/media/js/jquery.js';
$files[] = '[civicrm.vendor]/pear/net_smtp/examples';
$files[] = '[civicrm.vendor]/pear/net_smtp/tests';
$files[] = '[civicrm.vendor]/pear/net_smtp/phpdoc.sh';
$files[] = '[civicrm.vendor]/phpoffice/phpword/samples';
return $files;
}
/**
* @return array
* Each item is an array with keys:
* - name: string, an abstract name
* - path: string, a full file path
* Files are returned in deletable order (ie children before parents).
*/
public function findOrphanedFiles() {
$orphans = array();
foreach ($this->getRemovedFiles() as $file) {
$path = Civi::paths()->getPath($file);
if (empty($path) || strpos('[civicrm', $path) !== FALSE) {
Civi::log()->warning('Failed to resolve path of old file \"{file}\" ({path})', array(
'file' => $file,
'path' => $path,
));
}
if (file_exists($path)) {
$orphans[] = array(
'name' => $file,
'path' => $path,
);
}
}
usort($orphans, function ($a, $b) {
// Children first, then parents.
$diff = strlen($b['name']) - strlen($a['name']);
if ($diff !== 0) {
return $diff;
}
if ($a['name'] === $b['name']) {
return 0;
}
return $a['name'] < $b['name'] ? -1 : 1;
});
return $orphans;
}
/**
* @return array
*/
public function checkOrphans() {
$orphans = $this->findOrphanedFiles();
if (empty($orphans)) {
return array();
}
$messages = array();
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('The local system includes old files which should not exist: "%1"',
array(
1 => implode('", "', CRM_Utils_Array::collect('path', $orphans)),
)),
ts('Old files'),
\Psr\Log\LogLevel::WARNING,
'fa-server'
);
return $messages;
}
}

View file

@ -0,0 +1,138 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Component_Timestamps extends CRM_Utils_Check_Component {
const DOCTOR_WHEN = 'https://github.com/civicrm/org.civicrm.doctorwhen';
/**
* Check that various columns are TIMESTAMP and not DATETIME. (CRM-9683, etal)
*
* @return array
*/
public function checkSchema() {
$problems = array();
foreach (self::getConvertedTimestamps() as $target) {
if (self::isFieldType($target['table'], $target['column'], 'datetime')) {
$phrases = array();
$phrases[] = sprintf('<em>%s.%s</em>', $target['table'], $target['column']);
if ($target['changed']) {
$phrases[] = sprintf('(New sites default to TIMESTAMP in v%s+)', $target['changed']);
}
else {
$phrases[] = '(Experimental suggestion)';
}
if (isset($target['jira'])) {
$phrases[] = sprintf(' [<a href="https://issues.civicrm.org/jira/browse/%s" target="_blank">%s</a>]', $target['jira'], $target['jira']);
}
$problems[] = implode(' ', $phrases);
}
}
$messages = array();
if ($problems) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__ . md5(implode(',', $problems)),
'<p>' .
ts('This MySQL database stores certain fields with data-type "DATETIME". To improve timezone support, you <em>may</em> want to change these from "DATETIME" to "TIMESTAMP".') .
'</p>' .
'<ul><li>' .
implode('</li><li>', $problems) .
'</li></ul>' .
'<p>' .
ts('Changing should improve data-quality for organizations working in multiple timezones. However, if you do change, then you may need to re-test any customizations or processes that reference these fields. Changing is <em>suggested</em> but not <em>required</em>.') .
'</p>' .
'<p>' .
ts('For further discussion, please visit %1', array(
1 => sprintf('<a href="%s" target="_blank">%s</a>', self::DOCTOR_WHEN, self::DOCTOR_WHEN),
)) .
'</p>',
ts('Timestamps and Timezones'),
\Psr\Log\LogLevel::NOTICE,
'fa-clock-o'
);
}
return $messages;
}
/**
* @param string $table
* Ex: 'civicrm_log'.
* @param string $column
* Ex: 'modified_date'.
* @param string $expectType
* Ex: 'datetime' or 'timestamp'.
* @return bool
*/
public static function isFieldType($table, $column, $expectType) {
$result = FALSE;
$dao = CRM_Core_DAO::executeQuery('DESC ' . $table);
while ($dao->fetch()) {
if ($dao->Field === $column && strtolower($dao->Type) === $expectType) {
$result = TRUE;
}
}
$dao->free();
return $result;
}
public static function getConvertedTimestamps() {
return array(
array('table' => 'civicrm_cache', 'column' => 'created_date', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When was the cache item created'),
array('table' => 'civicrm_cache', 'column' => 'expired_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'When should the cache item expire'),
array('table' => 'civicrm_job', 'column' => 'last_run', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'When was this cron entry last run'),
array('table' => 'civicrm_mailing_event_bounce', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this bounce event occurred.'),
array('table' => 'civicrm_mailing_event_confirm', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this confirmation event occurred.'),
array('table' => 'civicrm_mailing_event_delivered', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this delivery event occurred.'),
array('table' => 'civicrm_mailing_event_forward', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this forward event occurred.'),
array('table' => 'civicrm_mailing_event_opened', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this open event occurred.'),
array('table' => 'civicrm_mailing_event_reply', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this reply event occurred.'),
array('table' => 'civicrm_mailing_event_subscribe', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this subscription event occurred.'),
array('table' => 'civicrm_mailing_event_trackable_url_open', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this trackable URL open occurred.'),
array('table' => 'civicrm_mailing_event_unsubscribe', 'column' => 'time_stamp', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When this delivery event occurred.'),
array('table' => 'civicrm_mailing', 'column' => 'created_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'Date and time this mailing was created.'),
array('table' => 'civicrm_mailing', 'column' => 'scheduled_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'Date and time this mailing was scheduled.'),
array('table' => 'civicrm_mailing', 'column' => 'approval_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'Date and time this mailing was approved.'),
array('table' => 'civicrm_mailing_abtest', 'column' => 'created_date', 'changed' => '4.7.20', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-9683', 'comment' => 'When was this item created'),
array('table' => 'civicrm_mailing_job', 'column' => 'scheduled_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'date on which this job was scheduled.'),
array('table' => 'civicrm_mailing_job', 'column' => 'start_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'date on which this job was started.'),
array('table' => 'civicrm_mailing_job', 'column' => 'end_date', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'date on which this job ended.'),
array('table' => 'civicrm_mailing_spool', 'column' => 'added_at', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'date on which this job was added.'),
array('table' => 'civicrm_mailing_spool', 'column' => 'removed_at', 'changed' => '4.7.20', 'jira' => 'CRM-9683', 'comment' => 'date on which this job was removed.'),
array('table' => 'civicrm_subscription_history', 'column' => 'date', 'changed' => '4.7.27', 'default' => 'CURRENT_TIMESTAMP', 'jira' => 'CRM-21157', 'comment' => 'Date of the (un)subscription'),
);
}
}

View file

@ -0,0 +1,297 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Check_Message {
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $message;
/**
* @var string
*/
private $title;
/**
* @var int
* @see Psr\Log\LogLevel
*/
private $level;
/**
* @var string
* help text (to be presented separately from the message)
*/
private $help;
/**
* @var array
* actions which can be performed with this message
*/
private $actions = array();
/**
* @var string
* crm-i css class
*/
private $icon;
/**
* @var bool
* Has this message been suppressed?
*/
private $isVisible;
/**
* @var bool|string
* Date this message is hidden until
*/
private $hiddenUntil;
/**
* Class constructor.
*
* @param string $name
* Symbolic name for the check.
* @param string $message
* Printable message (short or long).
* @param string $title
* Printable message (short).
* @param string $level
* The severity of the message. Use PSR-3 log levels.
* @param string $icon
*
* @see Psr\Log\LogLevel
*
*/
public function __construct($name, $message, $title, $level = \Psr\Log\LogLevel::WARNING, $icon = NULL) {
$this->name = $name;
$this->message = $message;
$this->title = $title;
$this->icon = $icon;
$this->setLevel($level);
}
/**
* Get name.
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Get message.
*
* @return string
*/
public function getMessage() {
return $this->message;
}
/**
* @return string
*/
public function getTitle() {
return $this->title;
}
/**
* Get severity level number.
*
* @return int
* @see Psr\Log\LogLevel
*/
public function getLevel() {
return $this->level;
}
/**
* Get severity string.
*
* @return string
* @see Psr\Log\LogLevel
*/
public function getSeverity() {
return CRM_Utils_Check::severityMap($this->level, TRUE);
}
/**
* Set optional additional help text.
*
* @param string $help
*/
public function addHelp($help) {
$this->help = $help;
}
/**
* Set optional additional actions text.
*
* @param string $title
* Text displayed on the status message as a link or button.
* @param string $confirmation
* Optional confirmation message before performing action
* @param string $type
* Currently supports: api3 or href
* @param array $params
* Params to be passed to CRM.api3 or CRM.url depending on type
*/
public function addAction($title, $confirmation, $type, $params) {
$this->actions[] = array(
'title' => $title,
'confirm' => $confirmation,
'type' => $type,
'params' => $params,
);
}
/**
* Set severity level
*
* @param string|int $level
* @throws \CRM_Core_Exception
*/
public function setLevel($level) {
// Convert level to integer
if (!CRM_Utils_Rule::positiveInteger($level)) {
$level = CRM_Utils_Check::severityMap($level);
}
else {
// Validate numeric input - this will throw an exception if invalid
CRM_Utils_Check::severityMap($level, TRUE);
}
$this->level = $level;
// Clear internal caches
unset($this->isVisible, $this->hiddenUntil);
}
/**
* Convert to array.
*
* @return array
*/
public function toArray() {
$array = array(
'name' => $this->name,
'message' => $this->message,
'title' => $this->title,
'severity' => $this->getSeverity(),
'severity_id' => $this->level,
'is_visible' => (int) $this->isVisible(),
'icon' => $this->icon,
);
if ($this->getHiddenUntil()) {
$array['hidden_until'] = $this->getHiddenUntil();
}
if (!empty($this->help)) {
$array['help'] = $this->help;
}
if (!empty($this->actions)) {
$array['actions'] = $this->actions;
}
return $array;
}
/**
* Get message visibility.
*
* @return bool
*/
public function isVisible() {
if (!isset($this->isVisible)) {
$this->isVisible = !$this->checkStatusPreference();
}
return $this->isVisible;
}
/**
* Get date hidden until.
*
* @return string
*/
public function getHiddenUntil() {
if (!isset($this->hiddenUntil)) {
$this->checkStatusPreference();
}
return $this->hiddenUntil;
}
/**
* Check if message has been hidden by the user.
*
* Also populates this->hiddenUntil property.
*
* @return bool
* TRUE means hidden, FALSE means visible.
* @throws \CiviCRM_API3_Exception
*/
private function checkStatusPreference() {
$this->hiddenUntil = FALSE;
// Debug & info can't be hidden
if ($this->level < 2) {
return FALSE;
}
$statusPreferenceParams = array(
'name' => $this->getName(),
'domain_id' => CRM_Core_Config::domainID(),
'sequential' => 1,
);
// Check if there's a StatusPreference matching this name/domain.
$statusPreference = civicrm_api3('StatusPreference', 'get', $statusPreferenceParams);
$prefs = CRM_Utils_Array::value('values', $statusPreference, array());
if ($prefs) {
// If so, compare severity to StatusPreference->severity.
if ($this->level <= $prefs[0]['ignore_severity']) {
if (isset($prefs[0]['hush_until'])) {
// Time-based hush.
$this->hiddenUntil = $prefs[0]['hush_until'];
$today = new DateTime();
$snoozeDate = new DateTime($prefs[0]['hush_until']);
return !($today > $snoozeDate);
}
else {
// Hidden indefinitely.
return TRUE;
}
}
}
return FALSE;
}
}

View file

@ -0,0 +1,56 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Static utility functions for working with colors
*/
class CRM_Utils_Color {
/**
* Determine the appropriate text color for a given background.
*
* Based on YIQ value.
*
* @param string $hexcolor
* @return string
*/
public static function getContrast($hexcolor) {
$hexcolor = trim($hexcolor, ' #');
$r = hexdec(substr($hexcolor, 0, 2));
$g = hexdec(substr($hexcolor, 2, 2));
$b = hexdec(substr($hexcolor, 4, 2));
$yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
return ($yiq >= 128) ? 'black' : 'white';
}
}

View file

@ -0,0 +1,118 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Capture the output from the console, copy it to a file, and pass it on.
*
* @code
* $tee = CRM_Utils_ConsoleTee::create()->start();
* echo "hello world";
* $tee->stop();
* assertEquals("hello world", file_get_contents($tee->getFileName()));
* @endCode
*
* Loosely speaking, it serves a similar purpose to Unix `tee`.
*
* @link https://en.wikipedia.org/wiki/Tee_(command)
*/
class CRM_Utils_ConsoleTee {
/**
* Capture console output and copy to a temp file.
*
* @param string $prefix
* @return CRM_Utils_ConsoleTee
*/
public static function create($prefix = 'ConsoleTee-') {
return new static(tempnam(sys_get_temp_dir(), $prefix));
}
/**
* @var string
*/
protected $fileName;
/**
* @var resource
*/
protected $fh;
/**
* CRM_Utils_ConsoleTee constructor.
*
* @param string $fileName
* The full path of the file to write to.
*/
public function __construct($fileName) {
$this->fileName = $fileName;
}
/**
* Start capturing console output and copying it to a file.
*
* @param string $mode
* The file output mode, e.g. `w` or `w+`.
* @return CRM_Utils_ConsoleTee
* @see fopen
*/
public function start($mode = 'w') {
$this->fh = fopen($this->fileName, $mode);
ob_start(array($this, 'onOutput'));
return $this;
}
/**
* Process a snippet of data from the output buffer.
*
* @param string $buf
* @return bool
* @see ob_start
*/
public function onOutput($buf) {
fwrite($this->fh, $buf);
return FALSE;
}
/**
* Stop capturing console output.
*
* @return CRM_Utils_ConsoleTee
*/
public function stop() {
ob_end_flush();
fclose($this->fh);
return $this;
}
/**
* @return string
*/
public function getFileName() {
return $this->fileName;
}
}

View file

@ -0,0 +1,58 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_Constant
*/
class CRM_Utils_Constant {
/**
* Determine the value of a constant, if any.
*
* If the specified constant is undefined, return a default value.
*
* @param string $name
* @param mixed $default
* (optional)
* @return mixed
*/
public static function value($name, $default = NULL) {
if (defined($name)) {
return constant($name);
}
else {
return $default;
}
}
}

View file

@ -0,0 +1,112 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Crypt {
/**
* Encrypts a string using AES256 in ECB mode, if encryption is enabled.
*
* After encrypting the string, it is base64 encoded.
*
* If encryption is not enabled, either due to CIVICRM_SITE_KEY being
* undefined or due to unavailability of the mcrypt module, the string is
* merely base64 encoded and is not encrypted at all.
*
* @param string $string
* Plaintext to be encrypted.
* @return string
* Base64-encoded ciphertext, or base64-encoded plaintext if encryption is
* disabled or unavailable.
*/
public static function encrypt($string) {
if (empty($string)) {
return $string;
}
if (function_exists('mcrypt_module_open') &&
defined('CIVICRM_SITE_KEY')
) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_ECB, '');
// ECB mode - iv not needed - CRM-8198
$iv = '00000000000000000000000000000000';
$ks = mcrypt_enc_get_key_size($td);
$key = substr(sha1(CIVICRM_SITE_KEY), 0, $ks);
mcrypt_generic_init($td, $key, $iv);
$string = mcrypt_generic($td, $string);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
return base64_encode($string);
}
/**
* Decrypts ciphertext encrypted with AES256 in ECB mode, if possible.
*
* If the mcrypt module is not available or if CIVICRM_SITE_KEY is not set,
* the provided ciphertext is only base64-decoded, not decrypted.
*
* @param string $string
* Ciphertext to be decrypted.
* @return string
* Plaintext, or base64-decoded ciphertext if encryption is disabled or
* unavailable.
*/
public static function decrypt($string) {
if (empty($string)) {
return $string;
}
$string = base64_decode($string);
if (empty($string)) {
return $string;
}
if (function_exists('mcrypt_module_open') &&
defined('CIVICRM_SITE_KEY')
) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_ECB, '');
// ECB mode - iv not needed - CRM-8198
$iv = '00000000000000000000000000000000';
$ks = mcrypt_enc_get_key_size($td);
$key = substr(sha1(CIVICRM_SITE_KEY), 0, $ks);
mcrypt_generic_init($td, $key, $iv);
$string = rtrim(mdecrypt_generic($td, $string));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
return $string;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* This is a quick-and-dirty way to define a vaguely-class-ish structure. It's non-performant, abnormal,
* and not a complete OOP system. Only use for testing/mocking.
*
* @code
* $object = new CRM_Utils_FakeObject(array(
* 'doIt' => function() { print "It!\n"; }
* ));
* $object->doIt();
* @endcode
*/
class CRM_Utils_FakeObject {
/**
* @param $array
*/
public function __construct($array) {
$this->array = $array;
}
/**
* @param string $name
* @param $arguments
*
* @throws Exception
*/
public function __call($name, $arguments) {
if (isset($this->array[$name]) && is_callable($this->array[$name])) {
return call_user_func_array($this->array[$name], $arguments);
}
else {
throw new Exception("Call to unimplemented method: $name");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
{
"image": "fa-file-image-o",
"audio": "fa-file-audio-o",
"video": "fa-file-video-o",
"application/pdf": "fa-file-pdf-o",
"application/msword": "fa-file-word-o",
"application/vnd.ms-word": "fa-file-word-o",
"application/vnd.oasis.opendocument.text": "fa-file-word-o",
"application/vnd.openxmlformats-officedocument.wordprocessingml": "fa-file-word-o",
"application/vnd.ms-excel": "fa-file-excel-o",
"application/vnd.openxmlformats-officedocument.spreadsheetml": "fa-file-excel-o",
"application/vnd.oasis.opendocument.spreadsheet": "fa-file-excel-o",
"application/vnd.ms-powerpoint": "fa-file-powerpoint-o",
"application/vnd.openxmlformats-officedocument.presentationml": "fa-file-powerpoint-o",
"application/vnd.oasis.opendocument.presentation": "fa-file-powerpoint-o",
"text/plain": "fa-file-text-o",
"text/html": "fa-file-code-o",
"application/json": "fa-file-code-o",
"application/gzip": "fa-file-archive-o",
"application/zip": "fa-file-archive-o",
"*": "fa-file-o"
}

View file

@ -0,0 +1,56 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_Geocode
*/
class CRM_Utils_Geocode {
/**
* @return string|''
* Class name, or empty.
*/
public static function getProviderClass() {
$settings = Civi::settings();
if ($settings->get('geoProvider')) {
return 'CRM_Utils_Geocode_' . $settings->get('geoProvider');
}
elseif ($settings->get('mapProvider')) {
return 'CRM_Utils_Geocode_' . $settings->get('mapProvider');
}
else {
return '';
}
}
}

View file

@ -0,0 +1,165 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class that uses google geocoder
*/
class CRM_Utils_Geocode_Google {
/**
* Server to retrieve the lat/long
*
* @var string
*/
static protected $_server = 'maps.googleapis.com';
/**
* Uri of service.
*
* @var string
*/
static protected $_uri = '/maps/api/geocode/xml?sensor=false&address=';
/**
* Function that takes an address object and gets the latitude / longitude for this
* address. Note that at a later stage, we could make this function also clean up
* the address into a more valid format
*
* @param array $values
* @param bool $stateName
*
* @return bool
* true if we modified the address, false otherwise
*/
public static function format(&$values, $stateName = FALSE) {
// we need a valid country, else we ignore
if (empty($values['country'])) {
return FALSE;
}
$config = CRM_Core_Config::singleton();
$add = '';
if (!empty($values['street_address'])) {
$add = urlencode(str_replace('', '+', $values['street_address']));
$add .= ',+';
}
$city = CRM_Utils_Array::value('city', $values);
if ($city) {
$add .= '+' . urlencode(str_replace('', '+', $city));
$add .= ',+';
}
if (!empty($values['state_province']) || (!empty($values['state_province_id']) && $values['state_province_id'] != 'null')) {
if (!empty($values['state_province_id'])) {
$stateProvince = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $values['state_province_id']);
}
else {
if (!$stateName) {
$stateProvince = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince',
$values['state_province'],
'name',
'abbreviation'
);
}
else {
$stateProvince = $values['state_province'];
}
}
// dont add state twice if replicated in city (happens in NZ and other countries, CRM-2632)
if ($stateProvince != $city) {
$add .= '+' . urlencode(str_replace('', '+', $stateProvince));
$add .= ',+';
}
}
if (!empty($values['postal_code'])) {
$add .= '+' . urlencode(str_replace('', '+', $values['postal_code']));
$add .= ',+';
}
if (!empty($values['country'])) {
$add .= '+' . urlencode(str_replace('', '+', $values['country']));
}
if (!empty($config->geoAPIKey)) {
$add .= '&key=' . urlencode($config->geoAPIKey);
}
$query = 'https://' . self::$_server . self::$_uri . $add;
require_once 'HTTP/Request.php';
$request = new HTTP_Request($query);
$request->sendRequest();
$string = $request->getResponseBody();
libxml_use_internal_errors(TRUE);
$xml = @simplexml_load_string($string);
CRM_Utils_Hook::geocoderFormat('Google', $values, $xml);
if ($xml === FALSE) {
// account blocked maybe?
CRM_Core_Error::debug_var('Geocoding failed. Message from Google:', $string);
return FALSE;
}
if (isset($xml->status)) {
if ($xml->status == 'OK' &&
is_a($xml->result->geometry->location,
'SimpleXMLElement'
)
) {
$ret = $xml->result->geometry->location->children();
if ($ret->lat && $ret->lng) {
$values['geo_code_1'] = (float) $ret->lat;
$values['geo_code_2'] = (float) $ret->lng;
return TRUE;
}
}
elseif ($xml->status == 'ZERO_RESULTS') {
// reset the geo code values if we did not get any good values
$values['geo_code_1'] = $values['geo_code_2'] = 'null';
return FALSE;
}
else {
CRM_Core_Error::debug_var("Geocoding failed. Message from Google: ({$xml->status})", (string ) $xml->error_message);
$values['geo_code_1'] = $values['geo_code_2'] = 'null';
$values['geo_code_error'] = $xml->status;
return FALSE;
}
}
}
}

View file

@ -0,0 +1,211 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class that uses Yahoo! PlaceFinder API to retrieve the lat/long of an address
* Documentation is at http://developer.yahoo.com/geo/placefinder/
*/
class CRM_Utils_Geocode_Yahoo {
/**
* Server to retrieve the lat/long
*
* @var string
*/
static protected $_server = 'query.yahooapis.com';
/**
* Uri of service.
*
* @var string
*/
static protected $_uri = '/v1/public/yql';
/**
* Function that takes an address array and gets the latitude / longitude
* and postal code for this address. Note that at a later stage, we could
* make this function also clean up the address into a more valid format
*
* @param array $values
* Associative array of address data: country, street_address, city, state_province, postal code.
* @param bool $stateName
* This parameter currently has no function.
*
* @return bool
* true if we modified the address, false otherwise
*/
public static function format(&$values, $stateName = FALSE) {
CRM_Utils_System::checkPHPVersion(5, TRUE);
$config = CRM_Core_Config::singleton();
$whereComponents = array();
if (!empty($values['street_address'])) {
$whereComponents['street'] = $values['street_address'];
}
if ($city = CRM_Utils_Array::value('city', $values)) {
$whereComponents['city'] = $city;
}
if (!empty($values['state_province']) || (!empty($values['state_province_id']) && $values['state_province_id'] != 'null')) {
if (!empty($values['state_province_id'])) {
$stateProvince = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $values['state_province_id']);
}
else {
if (!$stateName) {
$stateProvince = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince',
$values['state_province'],
'name',
'abbreviation'
);
}
else {
$stateProvince = $values['state_province'];
}
}
// dont add state twice if replicated in city (happens in NZ and other countries, CRM-2632)
if ($stateProvince != $city) {
$whereComponents['state'] = $stateProvince;
}
}
if (!empty($values['postal_code'])) {
$whereComponents['postal'] = $values['postal_code'];
}
if (!empty($values['country'])) {
$whereComponents['country'] = $values['country'];
}
foreach ($whereComponents as $componentName => $componentValue) {
$whereComponents[$componentName] = urlencode("$componentName=\"$componentValue\"");
}
$add = 'q=' . urlencode('select * from geo.placefinder where ');
$add .= implode(urlencode(' and '), $whereComponents);
$add .= "&appid=" . urlencode($config->mapAPIKey);
$query = 'http://' . self::$_server . self::$_uri . '?' . $add;
require_once 'HTTP/Request.php';
$request = new HTTP_Request($query);
$request->sendRequest();
$string = $request->getResponseBody();
// see CRM-11359 for why we suppress errors with @
$xml = @simplexml_load_string($string);
CRM_Utils_Hook::geocoderFormat('Yahoo', $values, $xml);
if ($xml === FALSE) {
// account blocked maybe?
CRM_Core_Error::debug_var('Geocoding failed. Message from Yahoo:', $string);
return FALSE;
}
if ($xml->getName() == 'error') {
CRM_Core_Error::debug_var('query', $query);
CRM_Core_Error::debug_log_message('Geocoding failed. Message from Yahoo: ' . (string) $xml->description);
return FALSE;
}
if (is_a($xml->results->Result, 'SimpleXMLElement')) {
$result = array();
$result = get_object_vars($xml->results->Result);
foreach ($result as $key => $val) {
if (is_scalar($val) &&
strlen($val)
) {
$ret[(string) $key] = (string) $val;
}
}
$values['geo_code_1'] = $ret['latitude'];
$values['geo_code_2'] = $ret['longitude'];
if (!empty($ret['postal'])) {
$current_pc = CRM_Utils_Array::value('postal_code', $values);
$skip_postal = FALSE;
if ($current_pc) {
$current_pc_suffix = CRM_Utils_Array::value('postal_code_suffix', $values);
$current_pc_complete = $current_pc . $current_pc_suffix;
$new_pc_complete = preg_replace("/[+-]/", '', $ret['postal']);
// if a postal code was already entered, don't change it, except to make it more precise
if (strpos($new_pc_complete, $current_pc_complete) !== 0) {
// Don't bother anonymous users with the message - they can't change a form they just submitted anyway
if (CRM_Utils_System::isUserLoggedIn()) {
$msg = ts('The Yahoo Geocoding system returned a different postal code (%1) than the one you entered (%2). If you want the Yahoo value, please delete the current postal code and save again.', array(
1 => $ret['postal'],
2 => $current_pc_suffix ? "$current_pc-$current_pc_suffix" : $current_pc,
));
CRM_Core_Session::setStatus($msg, ts('Postal Code Mismatch'), 'error');
}
$skip_postal = TRUE;
}
}
if (!$skip_postal) {
$values['postal_code'] = $ret['postal'];
/* the following logic to split the string was borrowed from
CRM/Core/BAO/Address.php -- CRM_Core_BAO_Address::fixAddress.
This is actually the function that calls the geocoding
script to begin with, but the postal code business takes
place before geocoding gets called.
*/
if (preg_match('/^(\d{4,5})[+-](\d{4})$/',
$ret['postal'],
$match
)
) {
$values['postal_code'] = $match[1];
$values['postal_code_suffix'] = $match[2];
}
}
}
return TRUE;
}
// reset the geo code values if we did not get any good values
$values['geo_code_1'] = $values['geo_code_2'] = 'null';
return FALSE;
}
}

View file

@ -0,0 +1,121 @@
<?php /*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Temporarily change a global variable.
*
* @code
* $globals = CRM_Utils_GlobalStack::singleton();
* $globals->push(array(
* '_GET' => array(
* 'q' => 'some-value
* ),
* ));
* ...do stuff...
* $globals->pop();
* @endcode
*
* Note: for purposes of this class, we'll refer to the array passed into
* push() as a frame.
*/
class CRM_Utils_GlobalStack {
/**
* We don't have a container or dependency-injection, so use singleton instead
*
* @var object
*/
private static $_singleton = NULL;
private $backups = array();
/**
* Get or set the single instance of CRM_Utils_GlobalStack.
*
* @return CRM_Utils_GlobalStack
*/
static public function singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_GlobalStack();
}
return self::$_singleton;
}
/**
* @param $newFrame
*/
public function push($newFrame) {
$this->backups[] = $this->createBackup($newFrame);
$this->applyFrame($newFrame);
}
public function pop() {
$this->applyFrame(array_pop($this->backups));
}
/**
* @param array $new
* The new, incoming frame.
* @return array
* frame
*/
public function createBackup($new) {
$frame = array();
foreach ($new as $globalKey => $values) {
if (is_array($values)) {
foreach ($values as $key => $value) {
$frame[$globalKey][$key] = CRM_Utils_Array::value($key, $GLOBALS[$globalKey]);
}
}
else {
$frame[$globalKey] = CRM_Utils_Array::value($globalKey, $GLOBALS);
}
}
return $frame;
}
/**
* @param $newFrame
*/
public function applyFrame($newFrame) {
foreach ($newFrame as $globalKey => $values) {
if (is_array($values)) {
foreach ($values as $key => $value) {
$GLOBALS[$globalKey][$key] = $value;
}
}
else {
$GLOBALS[$globalKey] = $values;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Backdrop extends CRM_Utils_Hook_DrupalBase {
}

View file

@ -0,0 +1,35 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Drupal extends CRM_Utils_Hook_DrupalBase {
}

View file

@ -0,0 +1,34 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Drupal6 extends CRM_Utils_Hook_DrupalBase {
}

View file

@ -0,0 +1,35 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Drupal8 extends CRM_Utils_Hook_DrupalBase {
}

View file

@ -0,0 +1,131 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_DrupalBase extends CRM_Utils_Hook {
/**
* @var bool
*/
private $isBuilt = FALSE;
/**
* @var array(string)
*/
private $allModules = NULL;
/**
* @var array(string)
*/
private $civiModules = NULL;
/**
* @var array(string)
*/
private $drupalModules = NULL;
/**
* @see CRM_Utils_Hook::invoke()
*
* @param int $numParams
* Number of parameters to pass to the hook.
* @param unknown $arg1
* Parameter to be passed to the hook.
* @param unknown $arg2
* Parameter to be passed to the hook.
* @param unknown $arg3
* Parameter to be passed to the hook.
* @param unknown $arg4
* Parameter to be passed to the hook.
* @param unknown $arg5
* Parameter to be passed to the hook.
* @param mixed $arg6
* @param string $fnSuffix
* Function suffix, this is effectively the hook name.
*
* @return array|bool
*/
public function invokeViaUF(
$numParams,
&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
$fnSuffix) {
$this->buildModuleList();
return $this->runHooks($this->allModules, $fnSuffix,
$numParams, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6
);
}
/**
* Build the list of modules to be processed for hooks.
*/
public function buildModuleList() {
if ($this->isBuilt === FALSE) {
if ($this->drupalModules === NULL) {
if (function_exists('module_list')) {
// copied from user_module_invoke
$this->drupalModules = module_list();
}
}
if ($this->civiModules === NULL) {
$this->civiModules = array();
$this->requireCiviModules($this->civiModules);
}
// CRM-12370
// we should add civicrm's module's just after main civicrm drupal module
// Note: Assume that drupalModules and civiModules may each be array() or NULL
if ($this->drupalModules !== NULL) {
foreach ($this->drupalModules as $moduleName) {
$this->allModules[$moduleName] = $moduleName;
if ($moduleName == 'civicrm') {
if (!empty($this->civiModules)) {
foreach ($this->civiModules as $civiModuleName) {
$this->allModules[$civiModuleName] = $civiModuleName;
}
}
}
}
}
else {
$this->allModules = (array) $this->civiModules;
}
if ($this->drupalModules !== NULL && $this->civiModules !== NULL) {
// both CRM and CMS have bootstrapped, so this is the final list
$this->isBuilt = TRUE;
}
}
}
}

View file

@ -0,0 +1,128 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Joomla extends CRM_Utils_Hook {
/**
* Invoke hooks.
*
* @param int $numParams
* Number of parameters to pass to the hook.
* @param mixed $arg1
* Parameter to be passed to the hook.
* @param mixed $arg2
* Parameter to be passed to the hook.
* @param mixed $arg3
* Parameter to be passed to the hook.
* @param mixed $arg4
* Parameter to be passed to the hook.
* @param mixed $arg5
* Parameter to be passed to the hook.
* @param mixed $arg6
* Parameter to be passed to the hook.
* @param string $fnSuffix
* Function suffix, this is effectively the hook name.
*
* @return mixed
*/
/**
* @param int $numParams
* @param mixed $arg1
* @param mixed $arg2
* @param mixed $arg3
* @param mixed $arg4
* @param mixed $arg5
* @param mixed $arg6
* @param string $fnSuffix
*
* @return mixed
*/
public function invokeViaUF(
$numParams,
&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
$fnSuffix
) {
// ensure that we are running in a joomla context
// we've not yet figured out how to bootstrap joomla, so we should
// not execute hooks if joomla is not loaded
if (defined('_JEXEC')) {
//Invoke the Joomla plugin system to observe to civicrm events.
jimport('joomla.plugin.helper');
jimport('cms.plugin.helper');
JPluginHelper::importPlugin('civicrm');
// get app based on cli or web
if (PHP_SAPI != 'cli') {
$app = JFactory::getApplication('administrator');
}
else {
// condition on Joomla version
if (version_compare(JVERSION, '3.0', 'lt')) {
$app = JCli::getInstance();
}
else {
$app = JApplicationCli::getInstance();
}
}
$result = $app->triggerEvent($fnSuffix, array(&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6));
$moduleResult = $this->commonInvoke($numParams,
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6,
$fnSuffix, 'joomla');
if (!empty($moduleResult) && is_array($moduleResult)) {
if (empty($result)) {
$result = $moduleResult;
}
else {
if (is_array($moduleResult)) {
$result = array_merge($result, $moduleResult);
}
}
}
if (!empty($result)) {
// collapse result returned from hooks
// CRM-9XXX
$finalResult = array();
foreach ($result as $res) {
if (!is_array($res)) {
$res = array($res);
}
$finalResult = array_merge($finalResult, $res);
}
$result = $finalResult;
}
return $result;
}
}
}

View file

@ -0,0 +1,77 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_Soap extends CRM_Utils_Hook {
/**
* Invoke hooks.
*
* @param int $numParams
* Number of parameters to pass to the hook.
* @param mixed $arg1
* Parameter to be passed to the hook.
* @param mixed $arg2
* Parameter to be passed to the hook.
* @param mixed $arg3
* Parameter to be passed to the hook.
* @param mixed $arg4
* Parameter to be passed to the hook.
* @param mixed $arg5
* Parameter to be passed to the hook.
* @param mixed $arg6
* Parameter to be passed to the hook.
* @param string $fnSuffix
* Function suffix, this is effectively the hook name.
*
* @return mixed
*/
/**
* @param int $numParams
* @param mixed $arg1
* @param mixed $arg2
* @param mixed $arg3
* @param mixed $arg4
* @param mixed $arg5
* @param mixed $arg6
* @param string $fnSuffix
*
* @return mixed
*/
public function invokeViaUF(
$numParams,
&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
$fnSuffix
) {
// suppress all hok calls during soap
return NULL;
}
}

View file

@ -0,0 +1,132 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_UnitTests extends CRM_Utils_Hook {
protected $mockObject;
/**
* @var array $adhocHooks to call
*/
protected $adhocHooks;
protected $civiModules = NULL;
/**
* Call this in CiviUnitTestCase::setUp()
*/
public function reset() {
$this->mockObject = NULL;
$this->adhocHooks = array();
}
/**
* Use a unit-testing mock object to handle hook invocations.
*
* e.g. hook_civicrm_foo === $mockObject->foo()
* Mocks with a magic `__call()` method are called for every hook invokation.
*
* @param object $mockObject
*/
public function setMock($mockObject) {
$this->mockObject = $mockObject;
}
/**
* Register a function to run when invoking a specific hook.
* @param string $hook
* Hook name, e.g civicrm_pre.
* @param array $callable
* Function to call ie array(class, method).
* eg. array($this, mymethod)
*/
public function setHook($hook, $callable) {
$this->adhocHooks[$hook] = $callable;
}
/**
* Invoke standard, mock and ad hoc hooks.
*
* @param int $numParams
* Number of parameters to pass to the hook.
* @param mixed $arg1
* Parameter to be passed to the hook.
* @param mixed $arg2
* Parameter to be passed to the hook.
* @param mixed $arg3
* Parameter to be passed to the hook.
* @param mixed $arg4
* Parameter to be passed to the hook.
* @param mixed $arg5
* Parameter to be passed to the hook.
* @param mixed $arg6
* Parameter to be passed to the hook.
* @param string $fnSuffix
* Function suffix, this is effectively the hook name.
*
* @return mixed
*/
public function invokeViaUF(
$numParams,
&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
$fnSuffix) {
$params = array(&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6);
$fResult1 = $fResult2 = $fResult3 = NULL;
// run standard hooks
if ($this->civiModules === NULL) {
$this->civiModules = array();
$this->requireCiviModules($this->civiModules);
}
$fResult1 = $this->runHooks($this->civiModules, $fnSuffix, $numParams, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6);
// run mock object hooks
if ($this->mockObject && is_callable(array($this->mockObject, $fnSuffix))) {
$fResult2 = call_user_func(array($this->mockObject, $fnSuffix), $arg1, $arg2, $arg3, $arg4, $arg5, $arg6);
}
// run adhoc hooks
if (!empty($this->adhocHooks[$fnSuffix])) {
$fResult3 = call_user_func_array($this->adhocHooks[$fnSuffix], $params);
}
$result = array();
foreach (array($fResult1, $fResult2, $fResult3) as $fResult) {
if (!empty($fResult) && is_array($fResult)) {
$result = array_merge($result, $fResult);
}
}
return empty($result) ? TRUE : $result;
}
}

View file

@ -0,0 +1,213 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Hook_WordPress extends CRM_Utils_Hook {
/**
* @var bool
*/
private $isBuilt = FALSE;
/**
* @var array(string)
*/
private $allModules = NULL;
/**
* @var array(string)
*/
private $civiModules = NULL;
/**
* @var array(string)
*/
private $wordpressModules = NULL;
/**
* @var array(string)
*/
private $hooksThatReturn = array(
'civicrm_upgrade',
'civicrm_caseSummary',
'civicrm_dashboard',
);
/**
* Invoke hooks.
*
* @param int $numParams
* Number of parameters to pass to the hook.
* @param mixed $arg1
* Parameter to be passed to the hook.
* @param mixed $arg2
* Parameter to be passed to the hook.
* @param mixed $arg3
* Parameter to be passed to the hook.
* @param mixed $arg4
* Parameter to be passed to the hook.
* @param mixed $arg5
* Parameter to be passed to the hook.
* @param mixed $arg6
* Parameter to be passed to the hook.
* @param string $fnSuffix
* Function suffix, this is effectively the hook name.
*
* @return mixed
*/
public function invokeViaUF(
$numParams,
&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6,
$fnSuffix
) {
// do_action_ref_array is the default way of calling WordPress hooks
// because for the most part no return value is wanted. However, this is
// only generally true, so using do_action_ref_array() is only called for those
// hooks which do not require a return value. We exclude the following, which
// are incompatible with the WordPress Plugin API:
//
// civicrm_upgrade
// http://wiki.civicrm.org/confluence/display/CRMDOC43/hook_civicrm_upgrade
//
// civicrm_caseSummary
// http://wiki.civicrm.org/confluence/display/CRMDOC43/hook_civicrm_caseSummary
//
// civicrm_dashboard
// http://wiki.civicrm.org/confluence/display/CRMDOC43/hook_civicrm_dashboard
// distinguish between types of hook
if (!in_array($fnSuffix, $this->hooksThatReturn)) {
// only pass the arguments that have values
$args = array_slice(
array(&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6),
0,
$numParams
);
// Use WordPress Plugins API to modify $args
//
// Because $args are passed as references to the WordPress callbacks,
// runHooks subsequently receives appropriately modified parameters.
// protect from REST calls
if (function_exists('do_action_ref_array')) {
do_action_ref_array($fnSuffix, $args);
}
}
// The following is based on the logic of the Joomla hook file by allowing
// WordPress callbacks to do their stuff before runHooks gets called.
// It also follows the logic of the Drupal hook file by building the "module"
// (read "plugin") list and then calling runHooks directly. This should avoid
// the need for the post-processing that the Joomla hook file does.
// Note that hooks which require a return value are incompatible with the
// signature of apply_filters_ref_array and must therefore be called in
// global scope, like in Drupal. It's not ideal, but plugins can always route
// these calls to methods in their classes.
// At some point, those hooks could be pre-processed and called via the WordPress
// Plugin API, but it would change their signature and require the CiviCRM docs
// to be rewritten for those calls in WordPress. So it's been done this way for
// now. Ideally these hooks will be deprecated in favour of hooks that do not
// require return values.
// build list of registered plugin codes
$this->buildModuleList();
// Call runHooks the same way Drupal does
$moduleResult = $this->runHooks(
$this->allModules,
$fnSuffix,
$numParams,
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6
);
// finally, return
return empty($moduleResult) ? TRUE : $moduleResult;
}
/**
* Build the list of plugins ("modules" in CiviCRM terminology) to be processed for hooks.
*
* We need to do this to preserve the CiviCRM hook signatures for hooks that require
* a return value, since the WordPress Plugin API seems to be incompatible with them.
*
* Copied and adapted from: CRM/Utils/Hook/Drupal6.php
*/
public function buildModuleList() {
if ($this->isBuilt === FALSE) {
if ($this->wordpressModules === NULL) {
// include custom PHP file - copied from parent->commonBuildModuleList()
$config = CRM_Core_Config::singleton();
if (!empty($config->customPHPPathDir) &&
file_exists("{$config->customPHPPathDir}/civicrmHooks.php")
) {
@include_once "{$config->customPHPPathDir}/civicrmHooks.php";
}
// initialise with the pre-existing 'wordpress' prefix
$this->wordpressModules = array('wordpress');
// Use WordPress Plugin API to build list
// a plugin simply needs to declare its "unique_plugin_code" thus:
// add_filter('civicrm_wp_plugin_codes', 'function_that_returns_my_unique_plugin_code');
// protect from REST calls
if (function_exists('apply_filters')) {
$this->wordpressModules = apply_filters('civicrm_wp_plugin_codes', $this->wordpressModules);
}
}
if ($this->civiModules === NULL) {
$this->civiModules = array();
$this->requireCiviModules($this->civiModules);
}
$this->allModules = array_merge((array) $this->wordpressModules, (array) $this->civiModules);
if ($this->wordpressModules !== NULL && $this->civiModules !== NULL) {
// both CRM and CMS have bootstrapped, so this is the final list
$this->isBuilt = TRUE;
}
}
}
}

View file

@ -0,0 +1,80 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CiviCRM_Hook
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Http {
/**
* Parse the expiration time from a series of HTTP headers.
*
* @param array $headers
* @return int|NULL
* Expiration tme as seconds since epoch, or NULL if not cacheable.
*/
public static function parseExpiration($headers) {
$headers = CRM_Utils_Array::rekey($headers, function ($k, $v) {
return strtolower($k);
});
if (!empty($headers['cache-control'])) {
$cc = self::parseCacheControl($headers['cache-control']);
if ($cc['max-age'] && is_numeric($cc['max-age'])) {
return CRM_Utils_Time::getTimeRaw() + $cc['max-age'];
}
}
return NULL;
}
/**
* @param string $value
* Ex: "max-age=86400, public".
* @return array
* Ex: Array("max-age"=>86400, "public"=>1).
*/
public static function parseCacheControl($value) {
$result = array();
$parts = preg_split('/, */', $value);
foreach ($parts as $part) {
if (strpos($part, '=') !== FALSE) {
list ($key, $value) = explode('=', $part, 2);
$result[$key] = $value;
}
else {
$result[$part] = TRUE;
}
}
return $result;
}
}

View file

@ -0,0 +1,224 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* This class handles HTTP downloads
*
* FIXME: fetch() and get() report errors differently -- e.g.
* fetch() returns fatal and get() returns an error code. Should
* refactor both (or get a third-party HTTP library) but don't
* want to deal with that so late in the 4.3 dev cycle.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_HttpClient {
const STATUS_OK = 'ok';
const STATUS_WRITE_ERROR = 'write-error';
const STATUS_DL_ERROR = 'dl-error';
/**
* @var CRM_Utils_HttpClient
*/
protected static $singleton;
/**
* @var int|NULL
* seconds; or NULL to use system default
*/
protected $connectionTimeout;
/**
* @return CRM_Utils_HttpClient
*/
public static function singleton() {
if (!self::$singleton) {
self::$singleton = new CRM_Utils_HttpClient();
}
return self::$singleton;
}
/**
* @param null $connectionTimeout
*/
public function __construct($connectionTimeout = NULL) {
$this->connectionTimeout = $connectionTimeout;
}
/**
* Download the remote zipfile.
*
* @param string $remoteFile
* URL of a .zip file.
* @param string $localFile
* Path at which to store the .zip file.
* @return STATUS_OK|STATUS_WRITE_ERROR|STATUS_DL_ERROR
*/
public function fetch($remoteFile, $localFile) {
// Download extension zip file ...
if (!function_exists('curl_init')) {
CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
}
list($ch, $caConfig) = $this->createCurl($remoteFile);
if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
}
$fp = @fopen($localFile, "w");
if (!$fp) {
return self::STATUS_WRITE_ERROR;
}
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_exec($ch);
if (curl_errno($ch)) {
return self::STATUS_DL_ERROR;
}
else {
curl_close($ch);
}
fclose($fp);
return self::STATUS_OK;
}
/**
* Send an HTTP GET for a remote resource.
*
* @param string $remoteFile
* URL of remote file.
* @return array
* array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
*/
public function get($remoteFile) {
// Download extension zip file ...
if (!function_exists('curl_init')) {
// CRM-13805
CRM_Core_Session::setStatus(
ts('As a result, actions like retrieving the CiviCRM news feed will fail. Talk to your server administrator or hosting company to rectify this.'),
ts('Curl is not installed')
);
return array(self::STATUS_DL_ERROR, NULL);
}
list($ch, $caConfig) = $this->createCurl($remoteFile);
if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
// CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
return array(self::STATUS_DL_ERROR, NULL);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$data = curl_exec($ch);
if (curl_errno($ch)) {
return array(self::STATUS_DL_ERROR, $data);
}
else {
curl_close($ch);
}
return array(self::STATUS_OK, $data);
}
/**
* Send an HTTP POST for a remote resource.
*
* @param string $remoteFile
* URL of a .zip file.
* @param array $params
*
* @return array
* array(0 => STATUS_OK|STATUS_DL_ERROR, 1 => string)
*/
public function post($remoteFile, $params) {
// Download extension zip file ...
if (!function_exists('curl_init')) {
//CRM_Core_Error::fatal('Cannot install this extension - curl is not installed!');
return array(self::STATUS_DL_ERROR, NULL);
}
list($ch, $caConfig) = $this->createCurl($remoteFile);
if (preg_match('/^https:/', $remoteFile) && !$caConfig->isEnableSSL()) {
// CRM_Core_Error::fatal('Cannot install this extension - does not support SSL');
return array(self::STATUS_DL_ERROR, NULL);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POST, count($params));
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
$data = curl_exec($ch);
if (curl_errno($ch)) {
return array(self::STATUS_DL_ERROR, $data);
}
else {
curl_close($ch);
}
return array(self::STATUS_OK, $data);
}
/**
* @param string $remoteFile
* @return array
* (0 => resource, 1 => CA_Config_Curl)
*/
protected function createCurl($remoteFile) {
$caConfig = CA_Config_Curl::probe(array(
'verify_peer' => (bool) Civi::settings()->get('verifySSL'),
));
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $remoteFile);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
curl_setopt($ch, CURLOPT_VERBOSE, 0);
if ($this->isRedirectSupported()) {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
}
if ($this->connectionTimeout !== NULL) {
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectionTimeout);
}
if (preg_match('/^https:/', $remoteFile) && $caConfig->isEnableSSL()) {
curl_setopt_array($ch, $caConfig->toCurlOptions());
}
return array($ch, $caConfig);
}
/**
* @return bool
*/
public function isRedirectSupported() {
return (ini_get('open_basedir') == '') && (ini_get('safe_mode') == 'Off' || ini_get('safe_mode') == '' || ini_get('safe_mode') === FALSE);
}
}

View file

@ -0,0 +1,136 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* @file
* API for event export in iCalendar format
* as outlined in Internet Calendaring and
* Scheduling Core Object Specification
*/
class CRM_Utils_ICalendar {
/**
* Escape text elements for safe ICalendar use.
*
* @param string $text
* Text to escape.
*
* @return string
*/
public static function formatText($text) {
$text = strip_tags($text);
$text = str_replace("\\", "\\\\", $text);
$text = str_replace(',', '\,', $text);
$text = str_replace(';', '\;', $text);
$text = str_replace(array("\r\n", "\n", "\r"), "\\n ", $text);
$text = implode("\n ", str_split($text, 50));
return $text;
}
/**
* Restore iCal formatted text to normal.
*
* @param string $text
* Text to unescape.
*
* @return string
*/
public static function unformatText($text) {
$text = str_replace('\n ', "\n", $text);
$text = str_replace('\;', ';', $text);
$text = str_replace('\,', ',', $text);
$text = str_replace("\\\\", "\\", $text);
$text = str_replace("DQUOTE", "\"", $text);
return $text;
}
/**
* Escape date elements for safe ICalendar use.
*
* @param $date
* Date to escape.
*
* @param bool $gdata
*
* @return string
* Escaped date
*/
public static function formatDate($date, $gdata = FALSE) {
if ($gdata) {
return date("Y-m-d\TH:i:s.000",
strtotime($date)
);
}
else {
return date("Ymd\THis",
strtotime($date)
);
}
}
/**
* Send the ICalendar to the browser with the specified content type
* - 'text/calendar' : used for downloaded ics file
* - 'text/plain' : used for iCal formatted feed
* - 'text/xml' : used for gData or rss formatted feeds
*
*
* @param string $calendar
* The calendar data to be published.
* @param string $content_type
* @param string $charset
* The character set to use, defaults to 'us-ascii'.
* @param string $fileName
* The file name (for downloads).
* @param string $disposition
* How the file should be sent ('attachment' for downloads).
*/
public static function send($calendar, $content_type = 'text/calendar', $charset = 'us-ascii', $fileName = NULL, $disposition = NULL) {
$config = CRM_Core_Config::singleton();
$lang = $config->lcMessages;
CRM_Utils_System::setHttpHeader("Content-Language", $lang);
CRM_Utils_System::setHttpHeader("Content-Type", "$content_type; charset=$charset");
if ($content_type == 'text/calendar') {
CRM_Utils_System::setHttpHeader('Content-Length', strlen($calendar));
CRM_Utils_System::setHttpHeader("Content-Disposition", "$disposition; filename=\"$fileName\"");
CRM_Utils_System::setHttpHeader("Pragma", "no-cache");
CRM_Utils_System::setHttpHeader("Expires", "0");
CRM_Utils_System::setHttpHeader("Cache-Control", "no-cache, must-revalidate");
}
echo $calendar;
}
}

View file

@ -0,0 +1,128 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Parse Javascript content and extract translatable strings.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_JS {
/**
* Parse a javascript file for translatable strings.
*
* @param string $jsCode
* Raw Javascript code.
* @return array
* Array of translatable strings
*/
public static function parseStrings($jsCode) {
$strings = array();
// Match all calls to ts() in an array.
// Note: \s also matches newlines with the 's' modifier.
preg_match_all('~
[^\w]ts\s* # match "ts" with whitespace
\(\s* # match "(" argument list start
((?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+)\s*
[,\)] # match ")" or "," to finish
~sx', $jsCode, $matches);
foreach ($matches[1] as $text) {
$quote = $text[0];
// Remove newlines
$text = str_replace("\\\n", '', $text);
// Unescape escaped quotes
$text = str_replace('\\' . $quote, $quote, $text);
// Remove end quotes
$text = substr(ltrim($text, $quote), 0, -1);
$strings[$text] = $text;
}
return array_values($strings);
}
/**
* Identify duplicate, adjacent, identical closures and consolidate them.
*
* Note that you can only dedupe closures if they are directly adjacent and
* have exactly the same parameters.
*
* @param array $scripts
* Javascript source.
* @param array $localVars
* Ordered list of JS vars to identify the start of a closure.
* @param array $inputVals
* Ordered list of input values passed into the closure.
* @return string
* Javascript source.
*/
public static function dedupeClosures($scripts, $localVars, $inputVals) {
// Example opening: (function (angular, $, _) {
$opening = '\s*\(\s*function\s*\(\s*';
$opening .= implode(',\s*', array_map(function ($v) {
return preg_quote($v, '/');
}, $localVars));
$opening .= '\)\s*\{';
$opening = '/^' . $opening . '/';
// Example closing: })(angular, CRM.$, CRM._);
$closing = '\}\s*\)\s*\(\s*';
$closing .= implode(',\s*', array_map(function ($v) {
return preg_quote($v, '/');
}, $inputVals));
$closing .= '\);\s*';
$closing = "/$closing\$/";
$scripts = array_values($scripts);
for ($i = count($scripts) - 1; $i > 0; $i--) {
if (preg_match($closing, $scripts[$i - 1]) && preg_match($opening, $scripts[$i])) {
$scripts[$i - 1] = preg_replace($closing, '', $scripts[$i - 1]);
$scripts[$i] = preg_replace($opening, '', $scripts[$i]);
}
}
return $scripts;
}
/**
* This is a primitive comment stripper. It doesn't catch all comments
* and falls short of minification, but it doesn't munge Angular injections
* and is fast enough to run synchronously (without caching).
*
* At time of writing, running this against the Angular modules, this impl
* of stripComments currently adds 10-20ms and cuts ~7%.
*
* Please be extremely cautious about extending this. If you want better
* minification, you should probably remove this implementation,
* import a proper JSMin implementation, and cache its output.
*
* @param string $script
* @return string
*/
public static function stripComments($script) {
return preg_replace(":^\\s*//[^\n]+$:m", "", $script);
}
}

View file

@ -0,0 +1,95 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class handles functions for JSON format
*/
class CRM_Utils_JSON {
/**
* Output json to the client.
* @param mixed $input
*/
public static function output($input) {
CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
echo json_encode($input);
CRM_Utils_System::civiExit();
}
/**
* Do not use this function. See CRM-16353.
* @deprecated
*
* @param array $params
* Associated array of row elements.
* @param int $sEcho
* Datatable needs this to make it more secure.
* @param int $iTotal
* Total records.
* @param int $iFilteredTotal
* Total records on a page.
* @param array $selectorElements
* Selector elements.
* @return string
*/
public static function encodeDataTableSelector($params, $sEcho, $iTotal, $iFilteredTotal, $selectorElements) {
$sOutput = '{';
$sOutput .= '"sEcho": ' . intval($sEcho) . ', ';
$sOutput .= '"iTotalRecords": ' . $iTotal . ', ';
$sOutput .= '"iTotalDisplayRecords": ' . $iFilteredTotal . ', ';
$sOutput .= '"aaData": [ ';
foreach ((array) $params as $key => $value) {
$addcomma = FALSE;
$sOutput .= "[";
foreach ($selectorElements as $element) {
if ($addcomma) {
$sOutput .= ",";
}
// CRM-7130 --lets addslashes to only double quotes,
// since we are using it to quote the field value.
// str_replace helps to provide a break for new-line
$sOutput .= '"' . addcslashes(str_replace(array("\r\n", "\n", "\r"), '<br />', $value[$element]), '"\\') . '"';
// remove extra spaces and tab character that breaks dataTable CRM-12551
$sOutput = preg_replace("/\s+/", " ", $sOutput);
$addcomma = TRUE;
}
$sOutput .= "],";
}
$sOutput = substr_replace($sOutput, "", -1);
$sOutput .= '] }';
return $sOutput;
}
}

View file

@ -0,0 +1,535 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Mail {
/**
* Create a new mailer to send any mail from the application.
*
* Note: The mailer is opened in persistent mode.
*
* Note: You probably don't want to call this directly. Get a reference
* to the mailer through the container.
*
* @return Mail
*/
public static function createMailer() {
$mailingInfo = Civi::settings()->get('mailing_backend');
if ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB ||
(defined('CIVICRM_MAILER_SPOOL') && CIVICRM_MAILER_SPOOL)
) {
$mailer = self::_createMailer('CRM_Mailing_BAO_Spool', array());
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SMTP) {
if ($mailingInfo['smtpServer'] == '' || !$mailingInfo['smtpServer']) {
CRM_Core_Error::debug_log_message(ts('There is no valid smtp server setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the SMTP Server.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
CRM_Core_Error::fatal(ts('There is no valid smtp server setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the SMTP Server.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
}
$params['host'] = $mailingInfo['smtpServer'] ? $mailingInfo['smtpServer'] : 'localhost';
$params['port'] = $mailingInfo['smtpPort'] ? $mailingInfo['smtpPort'] : 25;
if ($mailingInfo['smtpAuth']) {
$params['username'] = $mailingInfo['smtpUsername'];
$params['password'] = CRM_Utils_Crypt::decrypt($mailingInfo['smtpPassword']);
$params['auth'] = TRUE;
}
else {
$params['auth'] = FALSE;
}
/*
* Set the localhost value, CRM-3153
* Use the host name of the web server, falling back to the base URL
* (eg when using the PHP CLI), and then falling back to localhost.
*/
$params['localhost'] = CRM_Utils_Array::value(
'SERVER_NAME',
$_SERVER,
CRM_Utils_Array::value(
'host',
parse_url(CIVICRM_UF_BASEURL),
'localhost'
)
);
// also set the timeout value, lets set it to 30 seconds
// CRM-7510
$params['timeout'] = 30;
// CRM-9349
$params['persist'] = TRUE;
$mailer = self::_createMailer('smtp', $params);
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SENDMAIL) {
if ($mailingInfo['sendmail_path'] == '' ||
!$mailingInfo['sendmail_path']
) {
CRM_Core_Error::debug_log_message(ts('There is no valid sendmail path setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the sendmail server.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
CRM_Core_Error::fatal(ts('There is no valid sendmail path setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the sendmail server.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
}
$params['sendmail_path'] = $mailingInfo['sendmail_path'];
$params['sendmail_args'] = $mailingInfo['sendmail_args'];
$mailer = self::_createMailer('sendmail', $params);
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MAIL) {
$mailer = self::_createMailer('mail', array());
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MOCK) {
$mailer = self::_createMailer('mock', array());
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_DISABLED) {
CRM_Core_Error::debug_log_message(ts('Outbound mail has been disabled. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
CRM_Core_Error::statusBounce(ts('Outbound mail has been disabled. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
}
else {
CRM_Core_Error::debug_log_message(ts('There is no valid SMTP server Setting Or SendMail path setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
CRM_Core_Error::debug_var('mailing_info', $mailingInfo);
CRM_Core_Error::statusBounce(ts('There is no valid SMTP server Setting Or sendMail path setting. Click <a href=\'%1\'>Administer >> System Setting >> Outbound Email</a> to set the OutBound Email.', array(1 => CRM_Utils_System::url('civicrm/admin/setting/smtp', 'reset=1'))));
}
return $mailer;
}
/**
* Create a new instance of a PEAR Mail driver.
*
* @param string $driver
* 'CRM_Mailing_BAO_Spool' or a name suitable for Mail::factory().
* @param array $params
* @return object
* More specifically, a class which implements the "send()" function
*/
public static function _createMailer($driver, $params) {
if ($driver == 'CRM_Mailing_BAO_Spool') {
$mailer = new CRM_Mailing_BAO_Spool($params);
}
else {
$mailer = Mail::factory($driver, $params);
}
CRM_Utils_Hook::alterMailer($mailer, $driver, $params);
return $mailer;
}
/**
* Wrapper function to send mail in CiviCRM. Hooks are called from this function. The input parameter
* is an associateive array which holds the values of field needed to send an email. These are:
*
* from : complete from envelope
* toName : name of person to send email
* toEmail : email address to send to
* cc : email addresses to cc
* bcc : email addresses to bcc
* subject : subject of the email
* text : text of the message
* html : html version of the message
* replyTo : reply-to header in the email
* attachments: an associative array of
* fullPath : complete pathname to the file
* mime_type: mime type of the attachment
* cleanName: the user friendly name of the attachmment
*
* @param array $params
* (by reference).
*
* @return bool
* TRUE if a mail was sent, else FALSE.
*/
public static function send(&$params) {
$defaultReturnPath = CRM_Core_BAO_MailSettings::defaultReturnPath();
$includeMessageId = CRM_Core_BAO_MailSettings::includeMessageId();
$emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
$from = CRM_Utils_Array::value('from', $params);
if (!$defaultReturnPath) {
$defaultReturnPath = self::pluckEmailFromHeader($from);
}
// first call the mail alter hook
CRM_Utils_Hook::alterMailParams($params, 'singleEmail');
// check if any module has aborted mail sending
if (!empty($params['abortMailSend']) || empty($params['toEmail'])) {
return FALSE;
}
$textMessage = CRM_Utils_Array::value('text', $params);
$htmlMessage = CRM_Utils_Array::value('html', $params);
$attachments = CRM_Utils_Array::value('attachments', $params);
// CRM-6224
if (trim(CRM_Utils_String::htmlToText($htmlMessage)) == '') {
$htmlMessage = FALSE;
}
$headers = array();
// CRM-10699 support custom email headers
if (!empty($params['headers'])) {
$headers = array_merge($headers, $params['headers']);
}
$headers['From'] = $params['from'];
$headers['To'] = self::formatRFC822Email(
CRM_Utils_Array::value('toName', $params),
CRM_Utils_Array::value('toEmail', $params),
FALSE
);
$headers['Cc'] = CRM_Utils_Array::value('cc', $params);
$headers['Bcc'] = CRM_Utils_Array::value('bcc', $params);
$headers['Subject'] = CRM_Utils_Array::value('subject', $params);
$headers['Content-Type'] = $htmlMessage ? 'multipart/mixed; charset=utf-8' : 'text/plain; charset=utf-8';
$headers['Content-Disposition'] = 'inline';
$headers['Content-Transfer-Encoding'] = '8bit';
$headers['Return-Path'] = CRM_Utils_Array::value('returnPath', $params, $defaultReturnPath);
// CRM-11295: Omit reply-to headers if empty; this avoids issues with overzealous mailservers
$replyTo = CRM_Utils_Array::value('replyTo', $params, CRM_Utils_Array::value('from', $params));
if (!empty($replyTo)) {
$headers['Reply-To'] = $replyTo;
}
$headers['Date'] = date('r');
if ($includeMessageId) {
$headers['Message-ID'] = '<' . uniqid('civicrm_', TRUE) . "@$emailDomain>";
}
if (!empty($params['autoSubmitted'])) {
$headers['Auto-Submitted'] = "Auto-Generated";
}
// make sure we has to have space, CRM-6977
foreach (array('From', 'To', 'Cc', 'Bcc', 'Reply-To', 'Return-Path') as $fld) {
if (isset($headers[$fld])) {
$headers[$fld] = str_replace('"<', '" <', $headers[$fld]);
}
}
// quote FROM, if comma is detected AND is not already quoted. CRM-7053
if (strpos($headers['From'], ',') !== FALSE) {
$from = explode(' <', $headers['From']);
$headers['From'] = self::formatRFC822Email(
$from[0],
substr(trim($from[1]), 0, -1),
TRUE
);
}
require_once 'Mail/mime.php';
$msg = new Mail_mime("\n");
if ($textMessage) {
$msg->setTxtBody($textMessage);
}
if ($htmlMessage) {
$msg->setHTMLBody($htmlMessage);
}
if (!empty($attachments)) {
foreach ($attachments as $fileID => $attach) {
$msg->addAttachment(
$attach['fullPath'],
$attach['mime_type'],
$attach['cleanName']
);
}
}
$message = self::setMimeParams($msg);
$headers = &$msg->headers($headers);
$to = array($params['toEmail']);
$result = NULL;
$mailer = \Civi::service('pear_mail');
// CRM-3795, CRM-7355, CRM-7557, CRM-9058, CRM-9887, CRM-12883, CRM-19173 and others ...
// The PEAR library requires different parameters based on the mailer used:
// * Mail_mail requires the Cc/Bcc recipients listed ONLY in the $headers variable
// * All other mailers require that all be recipients be listed in the $to array AND that
// the Bcc must not be present in $header as otherwise it will be shown to all recipients
// ref: https://pear.php.net/bugs/bug.php?id=8047, full thread and answer [2011-04-19 20:48 UTC]
if (get_class($mailer) != "Mail_mail") {
// get emails from headers, since these are
// combination of name and email addresses.
if (!empty($headers['Cc'])) {
$to[] = CRM_Utils_Array::value('Cc', $headers);
}
if (!empty($headers['Bcc'])) {
$to[] = CRM_Utils_Array::value('Bcc', $headers);
unset($headers['Bcc']);
}
}
if (is_object($mailer)) {
$errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
$result = $mailer->send($to, $headers, $message);
if (is_a($result, 'PEAR_Error')) {
$message = self::errorMessage($mailer, $result);
// append error message in case multiple calls are being made to
// this method in the course of sending a batch of messages.
CRM_Core_Session::setStatus($message, ts('Mailing Error'), 'error');
return FALSE;
}
// CRM-10699
CRM_Utils_Hook::postEmailSend($params);
return TRUE;
}
return FALSE;
}
/**
* @param $mailer
* @param $result
*
* @return string
*/
public static function errorMessage($mailer, $result) {
$message = '<p>' . ts('An error occurred when CiviCRM attempted to send an email (via %1). If you received this error after submitting on online contribution or event registration - the transaction was completed, but we were unable to send the email receipt.', array(
1 => 'SMTP',
)) . '</p>' . '<p>' . ts('The mail library returned the following error message:') . '<br /><span class="font-red"><strong>' . $result->getMessage() . '</strong></span></p>' . '<p>' . ts('This is probably related to a problem in your Outbound Email Settings (Administer CiviCRM &raquo; System Settings &raquo; Outbound Email), OR the FROM email address specifically configured for your contribution page or event. Possible causes are:') . '</p>';
if (is_a($mailer, 'Mail_smtp')) {
$message .= '<ul>' . '<li>' . ts('Your SMTP Username or Password are incorrect.') . '</li>' . '<li>' . ts('Your SMTP Server (machine) name is incorrect.') . '</li>' . '<li>' . ts('You need to use a Port other than the default port 25 in your environment.') . '</li>' . '<li>' . ts('Your SMTP server is just not responding right now (it is down for some reason).') . '</li>';
}
else {
$message .= '<ul>' . '<li>' . ts('Your Sendmail path is incorrect.') . '</li>' . '<li>' . ts('Your Sendmail argument is incorrect.') . '</li>';
}
$message .= '<li>' . ts('The FROM Email Address configured for this feature may not be a valid sender based on your email service provider rules.') . '</li>' . '</ul>' . '<p>' . ts('Check <a href="%1">this page</a> for more information.', array(
1 => CRM_Utils_System::docURL2('user/advanced-configuration/email-system-configuration', TRUE),
)) . '</p>';
return $message;
}
/**
* @param $to
* @param $headers
* @param $message
*/
public static function logger(&$to, &$headers, &$message) {
if (is_array($to)) {
$toString = implode(', ', $to);
$fileName = $to[0];
}
else {
$toString = $fileName = $to;
}
$content = "To: " . $toString . "\n";
foreach ($headers as $key => $val) {
$content .= "$key: $val\n";
}
$content .= "\n" . $message . "\n";
if (is_numeric(CIVICRM_MAIL_LOG)) {
$config = CRM_Core_Config::singleton();
// create the directory if not there
$dirName = $config->configAndLogDir . 'mail' . DIRECTORY_SEPARATOR;
CRM_Utils_File::createDir($dirName);
$fileName = md5(uniqid(CRM_Utils_String::munge($fileName))) . '.txt';
file_put_contents($dirName . $fileName,
$content
);
}
else {
file_put_contents(CIVICRM_MAIL_LOG, $content, FILE_APPEND);
}
}
/**
* Get the email address itself from a formatted full name + address string
*
* Ugly but working.
*
* @param string $header
* The full name + email address string.
*
* @return string
* the plucked email address
*/
public static function pluckEmailFromHeader($header) {
preg_match('/<([^<]*)>$/', $header, $matches);
if (isset($matches[1])) {
return $matches[1];
}
return NULL;
}
/**
* Get the Active outBound email.
*
* @return bool
* TRUE if valid outBound email configuration found, false otherwise.
*/
public static function validOutBoundMail() {
$mailingInfo = Civi::settings()->get('mailing_backend');
if ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_MAIL) {
return TRUE;
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SMTP) {
if (!isset($mailingInfo['smtpServer']) || $mailingInfo['smtpServer'] == '' ||
$mailingInfo['smtpServer'] == 'YOUR SMTP SERVER' ||
($mailingInfo['smtpAuth'] && ($mailingInfo['smtpUsername'] == '' || $mailingInfo['smtpPassword'] == ''))
) {
return FALSE;
}
return TRUE;
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_SENDMAIL) {
if (!$mailingInfo['sendmail_path'] || !$mailingInfo['sendmail_args']) {
return FALSE;
}
return TRUE;
}
elseif ($mailingInfo['outBound_option'] == CRM_Mailing_Config::OUTBOUND_OPTION_REDIRECT_TO_DB) {
return TRUE;
}
return FALSE;
}
/**
* @param $message
* @param array $params
*
* @return mixed
*/
public static function &setMimeParams(&$message, $params = NULL) {
static $mimeParams = NULL;
if (!$params) {
if (!$mimeParams) {
$mimeParams = array(
'text_encoding' => '8bit',
'html_encoding' => '8bit',
'head_charset' => 'utf-8',
'text_charset' => 'utf-8',
'html_charset' => 'utf-8',
);
}
$params = $mimeParams;
}
return $message->get($params);
}
/**
* @param string $name
* @param $email
* @param bool $useQuote
*
* @return null|string
*/
public static function formatRFC822Email($name, $email, $useQuote = FALSE) {
$result = NULL;
$name = trim($name);
// strip out double quotes if present at the beginning AND end
if (substr($name, 0, 1) == '"' &&
substr($name, -1, 1) == '"'
) {
$name = substr($name, 1, -1);
}
if (!empty($name)) {
// escape the special characters
$name = str_replace(array('<', '"', '>'),
array('\<', '\"', '\>'),
$name
);
if (strpos($name, ',') !== FALSE ||
$useQuote
) {
// quote the string if it has a comma
$name = '"' . $name . '"';
}
$result = "$name ";
}
$result .= "<{$email}>";
return $result;
}
/**
* Takes a string and checks to see if it needs to be escaped / double quoted
* and if so does the needful and return the formatted name
*
* This code has been copied and adapted from ezc/Mail/src/tools.php
*
* @param string $name
*
* @return string
*/
public static function formatRFC2822Name($name) {
$name = trim($name);
if (!empty($name)) {
// remove the quotes around the name part if they are already there
if (substr($name, 0, 1) == '"' && substr($name, -1) == '"') {
$name = substr($name, 1, -1);
}
// add slashes to " and \ and surround the name part with quotes
if (strpbrk($name, ",@<>:;'\"") !== FALSE) {
$name = '"' . addcslashes($name, '\\"') . '"';
}
}
return $name;
}
/**
* @param string $fileName
* @param string $html
* @param string $format
*
* @return array
*/
public static function appendPDF($fileName, $html, $format = NULL) {
$pdf_filename = CRM_Core_Config::singleton()->templateCompileDir . CRM_Utils_File::makeFileName($fileName);
// FIXME : CRM-7894
// xmlns attribute is required in XHTML but it is invalid in HTML,
// Also the namespace "xmlns=http://www.w3.org/1999/xhtml" is default,
// and will be added to the <html> tag even if you do not include it.
$html = preg_replace('/(<html)(.+?xmlns=["\'].[^\s]+["\'])(.+)?(>)/', '\1\3\4', $html);
file_put_contents($pdf_filename, CRM_Utils_PDF_Utils::html2pdf($html,
$fileName,
TRUE,
$format)
);
return array(
'fullPath' => $pdf_filename,
'mime_type' => 'application/pdf',
'cleanName' => $fileName,
);
}
}

View file

@ -0,0 +1,478 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
// we should consider moving these to the settings table
// before the 4.1 release
define('EMAIL_ACTIVITY_TYPE_ID', NULL);
define('MAIL_BATCH_SIZE', 50);
/**
* Class CRM_Utils_Mail_EmailProcessor.
*/
class CRM_Utils_Mail_EmailProcessor {
/**
* Process the default mailbox (ie. that is used by civiMail for the bounce)
*
* @param bool $is_create_activities
* Should activities be created
*/
public static function processBounces($is_create_activities) {
$dao = new CRM_Core_DAO_MailSettings();
$dao->domain_id = CRM_Core_Config::domainID();
$dao->is_default = TRUE;
$dao->find();
while ($dao->fetch()) {
self::_process(TRUE, $dao, $is_create_activities);
}
}
/**
* Delete old files from a given directory (recursively).
*
* @param string $dir
* Directory to cleanup.
* @param int $age
* Files older than this many seconds will be deleted (default: 60 days).
*/
public static function cleanupDir($dir, $age = 5184000) {
// return early if we cant read/write the dir
if (!is_writable($dir) or !is_readable($dir) or !is_dir($dir)) {
return;
}
foreach (scandir($dir) as $file) {
// dont go up the directory stack and skip new files/dirs
if ($file == '.' or $file == '..') {
continue;
}
if (filemtime("$dir/$file") > time() - $age) {
continue;
}
// its an old file/dir, so delete/recurse
is_dir("$dir/$file") ? self::cleanupDir("$dir/$file", $age) : unlink("$dir/$file");
}
}
/**
* Process the mailboxes that aren't default (ie. that aren't used by civiMail for the bounce).
*/
public static function processActivities() {
$dao = new CRM_Core_DAO_MailSettings();
$dao->domain_id = CRM_Core_Config::domainID();
$dao->is_default = FALSE;
$dao->find();
$found = FALSE;
while ($dao->fetch()) {
$found = TRUE;
self::_process(FALSE, $dao, TRUE);
}
if (!$found) {
CRM_Core_Error::fatal(ts('No mailboxes have been configured for Email to Activity Processing'));
}
return $found;
}
/**
* Process the mailbox for all the settings from civicrm_mail_settings.
*
* @param bool|string $civiMail if true, processing is done in CiviMail context, or Activities otherwise.
*/
public static function process($civiMail = TRUE) {
$dao = new CRM_Core_DAO_MailSettings();
$dao->domain_id = CRM_Core_Config::domainID();
$dao->find();
while ($dao->fetch()) {
self::_process($civiMail, $dao);
}
}
/**
* @param $civiMail
* @param CRM_Core_DAO_MailSettings $dao
* @param bool $is_create_activities
* Create activities.
*
* @throws Exception
*/
public static function _process($civiMail, $dao, $is_create_activities) {
// 0 = activities; 1 = bounce;
$usedfor = $dao->is_default;
$emailActivityTypeId
= (defined('EMAIL_ACTIVITY_TYPE_ID') && EMAIL_ACTIVITY_TYPE_ID)
? EMAIL_ACTIVITY_TYPE_ID
: CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Inbound Email');
if (!$emailActivityTypeId) {
CRM_Core_Error::fatal(ts('Could not find a valid Activity Type ID for Inbound Email'));
}
$config = CRM_Core_Config::singleton();
$verpSeperator = preg_quote($config->verpSeparator);
$twoDigitStringMin = $verpSeperator . '(\d+)' . $verpSeperator . '(\d+)';
$twoDigitString = $twoDigitStringMin . $verpSeperator;
$threeDigitString = $twoDigitString . '(\d+)' . $verpSeperator;
// FIXME: legacy regexen to handle CiviCRM 2.1 address patterns, with domain id and possible VERP part
$commonRegex = '/^' . preg_quote($dao->localpart) . '(b|bounce|c|confirm|o|optOut|r|reply|re|e|resubscribe|u|unsubscribe)' . $threeDigitString . '([0-9a-f]{16})(-.*)?@' . preg_quote($dao->domain) . '$/';
$subscrRegex = '/^' . preg_quote($dao->localpart) . '(s|subscribe)' . $twoDigitStringMin . '@' . preg_quote($dao->domain) . '$/';
// a common-for-all-actions regex to handle CiviCRM 2.2 address patterns
$regex = '/^' . preg_quote($dao->localpart) . '(b|c|e|o|r|u)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '$/';
// a tighter regex for finding bounce info in soft bounces mail bodies
$rpRegex = '/Return-Path:\s*' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/';
// a regex for finding bound info X-Header
$rpXheaderRegex = '/X-CiviMail-Bounce: ' . preg_quote($dao->localpart) . '(b)' . $twoDigitString . '([0-9a-f]{16})@' . preg_quote($dao->domain) . '/i';
// CiviMail in regex and Civimail in header !!!
// retrieve the emails
try {
$store = CRM_Mailing_MailStore::getStore($dao->name);
}
catch (Exception$e) {
$message = ts('Could not connect to MailStore for ') . $dao->username . '@' . $dao->server . '<p>';
$message .= ts('Error message: ');
$message .= '<pre>' . $e->getMessage() . '</pre><p>';
CRM_Core_Error::fatal($message);
}
// process fifty at a time, CRM-4002
while ($mails = $store->fetchNext(MAIL_BATCH_SIZE)) {
foreach ($mails as $key => $mail) {
// for every addressee: match address elements if it's to CiviMail
$matches = array();
$action = NULL;
if ($usedfor == 1) {
foreach ($mail->to as $address) {
if (preg_match($regex, $address->email, $matches)) {
list($match, $action, $job, $queue, $hash) = $matches;
break;
// FIXME: the below elseifs should be dropped when we drop legacy support
}
elseif (preg_match($commonRegex, $address->email, $matches)) {
list($match, $action, $_, $job, $queue, $hash) = $matches;
break;
}
elseif (preg_match($subscrRegex, $address->email, $matches)) {
list($match, $action, $_, $job) = $matches;
break;
}
}
// CRM-5471: if $matches is empty, it still might be a soft bounce sent
// to another address, so scan the body for Return-Path: …bounce-pattern…
if (!$matches and preg_match($rpRegex, $mail->generateBody(), $matches)) {
list($match, $action, $job, $queue, $hash) = $matches;
}
// if $matches is still empty, look for the X-CiviMail-Bounce header
// CRM-9855
if (!$matches and preg_match($rpXheaderRegex, $mail->generateBody(), $matches)) {
list($match, $action, $job, $queue, $hash) = $matches;
}
// With Mandrilla, the X-CiviMail-Bounce header is produced by generateBody
// is base64 encoded
// Check all parts
if (!$matches) {
$all_parts = $mail->fetchParts();
foreach ($all_parts as $k_part => $v_part) {
if ($v_part instanceof ezcMailFile) {
$p_file = $v_part->__get('fileName');
$c_file = file_get_contents($p_file);
if (preg_match($rpXheaderRegex, $c_file, $matches)) {
list($match, $action, $job, $queue, $hash) = $matches;
}
}
}
}
// if all else fails, check Delivered-To for possible pattern
if (!$matches and preg_match($regex, $mail->getHeader('Delivered-To'), $matches)) {
list($match, $action, $job, $queue, $hash) = $matches;
}
}
// preseve backward compatibility
if ($usedfor == 0 || $is_create_activities) {
// if its the activities that needs to be processed ..
try {
$mailParams = CRM_Utils_Mail_Incoming::parseMailingObject($mail);
}
catch (Exception $e) {
echo $e->getMessage();
$store->markIgnored($key);
continue;
}
require_once 'CRM/Utils/DeprecatedUtils.php';
$params = _civicrm_api3_deprecated_activity_buildmailparams($mailParams, $emailActivityTypeId);
$params['version'] = 3;
if (!empty($dao->activity_status)) {
$params['status_id'] = $dao->activity_status;
}
$result = civicrm_api('activity', 'create', $params);
if ($result['is_error']) {
$matches = FALSE;
echo "Failed Processing: {$mail->subject}. Reason: {$result['error_message']}\n";
}
else {
$matches = TRUE;
CRM_Utils_Hook::emailProcessor('activity', $params, $mail, $result);
echo "Processed as Activity: {$mail->subject}\n";
}
}
// if $matches is empty, this email is not CiviMail-bound
if (!$matches) {
$store->markIgnored($key);
continue;
}
// get $replyTo from either the Reply-To header or from From
// FIXME: make sure it works with Reply-Tos containing non-email stuff
$replyTo = $mail->getHeader('Reply-To') ? $mail->getHeader('Reply-To') : $mail->from->email;
// handle the action by passing it to the proper API call
// FIXME: leave only one-letter cases when dropping legacy support
if (!empty($action)) {
$result = NULL;
switch ($action) {
case 'b':
case 'bounce':
$text = '';
if ($mail->body instanceof ezcMailText) {
$text = $mail->body->text;
}
elseif ($mail->body instanceof ezcMailMultipart) {
if ($mail->body instanceof ezcMailMultipartReport) {
$part = $mail->body->getMachinePart();
if ($part instanceof ezcMailDeliveryStatus) {
foreach ($part->recipients as $rec) {
if (isset($rec["Diagnostic-Code"])) {
$text = $rec["Diagnostic-Code"];
break;
}
elseif (isset($rec["Description"])) {
$text = $rec["Description"];
break;
}
// no diagnostic info present - try getting the human readable part
elseif (isset($rec["Status"])) {
$text = $rec["Status"];
$textpart = $mail->body->getReadablePart();
if ($textpart != NULL and isset($textpart->text)) {
$text .= " " . $textpart->text;
}
else {
$text .= " Delivery failed but no diagnostic code or description.";
}
break;
}
}
}
elseif ($part != NULL and isset($part->text)) {
$text = $part->text;
}
elseif (($part = $mail->body->getReadablePart()) != NULL) {
$text = $part->text;
}
}
elseif ($mail->body instanceof ezcMailMultipartRelated) {
foreach ($mail->body->getRelatedParts() as $part) {
if (isset($part->subType) and $part->subType == 'plain') {
$text = $part->text;
break;
}
}
}
else {
foreach ($mail->body->getParts() as $part) {
if (isset($part->subType) and $part->subType == 'plain') {
$text = $part->text;
break;
}
}
}
}
if (
empty($text) &&
$mail->subject == "Delivery Status Notification (Failure)"
) {
// Exchange error - CRM-9361
foreach ($mail->body->getParts() as $part) {
if ($part instanceof ezcMailDeliveryStatus) {
foreach ($part->recipients as $rec) {
if ($rec["Status"] == "5.1.1") {
if (isset($rec["Description"])) {
$text = $rec["Description"];
}
else {
$text = $rec["Status"] . " Delivery to the following recipients failed";
}
break;
}
}
}
}
}
if (empty($text)) {
// If bounce processing fails, just take the raw body. Cf. CRM-11046
$text = $mail->generateBody();
// if text is still empty, lets fudge a blank text so the api call below will succeed
if (empty($text)) {
$text = ts('We could not extract the mail body from this bounce message.');
}
}
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'body' => $text,
'version' => 3,
// Setting is_transactional means it will rollback if
// it crashes part way through creating the bounce.
// If the api were standard & had a create this would be the
// default. Adding the standard api & deprecating this one
// would probably be the
// most consistent way to address this - but this is
// a quick hack.
'is_transactional' => 1,
);
$result = civicrm_api('Mailing', 'event_bounce', $params);
break;
case 'c':
case 'confirm':
// CRM-7921
$params = array(
'contact_id' => $job,
'subscribe_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_confirm', $params);
break;
case 'o':
case 'optOut':
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params);
break;
case 'r':
case 'reply':
// instead of text and HTML parts (4th and 6th params) send the whole email as the last param
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'bodyTxt' => NULL,
'replyTo' => $replyTo,
'bodyHTML' => NULL,
'fullEmail' => $mail->generate(),
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_reply', $params);
break;
case 'e':
case 're':
case 'resubscribe':
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_resubscribe', $params);
break;
case 's':
case 'subscribe':
$params = array(
'email' => $mail->from->email,
'group_id' => $job,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_subscribe', $params);
break;
case 'u':
case 'unsubscribe':
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_unsubscribe', $params);
break;
}
if ($result['is_error']) {
echo "Failed Processing: {$mail->subject}, Action: $action, Job ID: $job, Queue ID: $queue, Hash: $hash. Reason: {$result['error_message']}\n";
}
else {
CRM_Utils_Hook::emailProcessor('mailing', $params, $mail, $result, $action);
}
}
$store->markProcessed($key);
}
// CRM-7356 used by IMAP only
$store->expunge();
}
}
}

View file

@ -0,0 +1,505 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Mail_Incoming {
const
EMAILPROCESSOR_CREATE_INDIVIDUAL = 1,
EMAILPROCESSOR_OVERRIDE = 2,
EMAILPROCESSOR_IGNORE = 3;
/**
* @param $mail
* @param $attachments
*
* @return string
*/
public function formatMail($mail, &$attachments) {
$t = '';
$t .= "From: " . self::formatAddress($mail->from) . "\n";
$t .= "To: " . self::formatAddresses($mail->to) . "\n";
$t .= "Cc: " . self::formatAddresses($mail->cc) . "\n";
$t .= "Bcc: " . self::formatAddresses($mail->bcc) . "\n";
$t .= 'Date: ' . date(DATE_RFC822, $mail->timestamp) . "\n";
$t .= 'Subject: ' . $mail->subject . "\n";
$t .= "MessageId: " . $mail->messageId . "\n";
$t .= "\n";
$t .= self::formatMailPart($mail->body, $attachments);
return $t;
}
/**
* @param $part
* @param $attachments
*
* @throws Exception
*/
public static function formatMailPart($part, &$attachments) {
if ($part instanceof ezcMail) {
return self::formatMail($part, $attachments);
}
if ($part instanceof ezcMailText) {
return self::formatMailText($part, $attachments);
}
if ($part instanceof ezcMailFile) {
return self::formatMailFile($part, $attachments);
}
if ($part instanceof ezcMailRfc822Digest) {
return self::formatMailRfc822Digest($part, $attachments);
}
if ($part instanceof ezcMailMultiPart) {
return self::formatMailMultipart($part, $attachments);
}
if ($part instanceof ezcMailDeliveryStatus) {
return self::formatMailDeliveryStatus($part);
}
// CRM-19111 - Handle blank emails with a subject.
if (!$part) {
return NULL;
}
return self::formatMailUnrecognisedPart($part);
}
/**
* @param $part
* @param $attachments
*
* @throws Exception
*/
public function formatMailMultipart($part, &$attachments) {
if ($part instanceof ezcMailMultiPartAlternative) {
return self::formatMailMultipartAlternative($part, $attachments);
}
if ($part instanceof ezcMailMultiPartDigest) {
return self::formatMailMultipartDigest($part, $attachments);
}
if ($part instanceof ezcMailMultiPartRelated) {
return self::formatMailMultipartRelated($part, $attachments);
}
if ($part instanceof ezcMailMultiPartMixed) {
return self::formatMailMultipartMixed($part, $attachments);
}
if ($part instanceof ezcMailMultipartReport) {
return self::formatMailMultipartReport($part, $attachments);
}
if ($part instanceof ezcMailDeliveryStatus) {
return self::formatMailDeliveryStatus($part);
}
return self::formatMailUnrecognisedPart($part);
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailMultipartMixed($part, &$attachments) {
$t = '';
foreach ($part->getParts() as $key => $alternativePart) {
$t .= self::formatMailPart($alternativePart, $attachments);
}
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailMultipartRelated($part, &$attachments) {
$t = '';
$t .= "-RELATED MAIN PART-\n";
$t .= self::formatMailPart($part->getMainPart(), $attachments);
foreach ($part->getRelatedParts() as $key => $alternativePart) {
$t .= "-RELATED PART $key-\n";
$t .= self::formatMailPart($alternativePart, $attachments);
}
$t .= "-RELATED END-\n";
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailMultipartDigest($part, &$attachments) {
$t = '';
foreach ($part->getParts() as $key => $alternativePart) {
$t .= "-DIGEST-$key-\n";
$t .= self::formatMailPart($alternativePart, $attachments);
}
$t .= "-DIGEST END---\n";
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailRfc822Digest($part, &$attachments) {
$t = '';
$t .= "-DIGEST-ITEM-\n";
$t .= "Item:\n\n";
$t .= self::formatMailpart($part->mail, $attachments);
$t .= "-DIGEST ITEM END-\n";
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailMultipartAlternative($part, &$attachments) {
$t = '';
foreach ($part->getParts() as $key => $alternativePart) {
$t .= "-ALTERNATIVE ITEM $key-\n";
$t .= self::formatMailPart($alternativePart, $attachments);
}
$t .= "-ALTERNATIVE END-\n";
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public static function formatMailText($part, &$attachments) {
$t = "\n{$part->text}\n";
return $t;
}
/**
* @param $part
* @param $attachments
*
* @return string
*/
public function formatMailMultipartReport($part, &$attachments) {
$t = '';
foreach ($part->getParts() as $key => $reportPart) {
$t .= "-REPORT-$key-\n";
$t .= self::formatMailPart($reportPart, $attachments);
}
$t .= "-REPORT END---\n";
return $t;
}
/**
* @param $part
*
* @return string
*/
public function formatMailDeliveryStatus($part) {
$t = '';
$t .= "-DELIVERY STATUS BEGIN-\n";
$t .= $part->generateBody();
$t .= "-DELIVERY STATUS END-\n";
return $t;
}
/**
* @param $part
*
* @return string
*/
public function formatUnrecognisedPart($part) {
CRM_Core_Error::debug_log_message(ts('CRM_Utils_Mail_Incoming: Unable to handle message part of type "%1".', array('%1' => get_class($part))));
return ts('Unrecognised message part of type "%1".', array('%1' => get_class($part)));
}
/**
* @param $part
* @param $attachments
*
* @return null
*/
public function formatMailFile($part, &$attachments) {
$attachments[] = array(
'dispositionType' => $part->dispositionType,
'contentType' => $part->contentType,
'mimeType' => $part->mimeType,
'contentID' => $part->contentId,
'fullName' => $part->fileName,
);
return NULL;
}
/**
* @param $addresses
*
* @return string
*/
public function formatAddresses($addresses) {
$fa = array();
foreach ($addresses as $address) {
$fa[] = self::formatAddress($address);
}
return implode(', ', $fa);
}
/**
* @param $address
*
* @return string
*/
public function formatAddress($address) {
$name = '';
if (!empty($address->name)) {
$name = "{$address->name} ";
}
return $name . "<{$address->email}>";
}
/**
* @param $file
*
* @return array
* @throws Exception
*/
public function &parse(&$file) {
// check that the file exists and has some content
if (!file_exists($file) ||
!trim(file_get_contents($file))
) {
return CRM_Core_Error::createAPIError(ts('%1 does not exists or is empty',
array(1 => $file)
));
}
// explode email to digestable format
$set = new ezcMailFileSet(array($file));
$parser = new ezcMailParser();
$mail = $parser->parseMail($set);
if (!$mail) {
return CRM_Core_Error::createAPIError(ts('%1 could not be parsed',
array(1 => $file)
));
}
// since we only have one fileset
$mail = $mail[0];
$mailParams = self::parseMailingObject($mail);
return $mailParams;
}
/**
* @param $mail
*
* @return array
*/
public static function parseMailingObject(&$mail) {
$config = CRM_Core_Config::singleton();
// get ready for collecting data about this email
// and put it in a standardized format
$params = array('is_error' => 0);
// Sometimes $mail->from is unset because ezcMail didn't handle format
// of From header. CRM-19215.
if (!isset($mail->from)) {
if (preg_match('/^([^ ]*)( (.*))?$/', $mail->getHeader('from'), $matches)) {
$mail->from = new ezcMailAddress($matches[1], trim($matches[2]));
}
}
$params['from'] = array();
self::parseAddress($mail->from, $field, $params['from'], $mail);
// we definitely need a contact id for the from address
// if we dont have one, skip this email
if (empty($params['from']['id'])) {
return NULL;
}
$emailFields = array('to', 'cc', 'bcc');
foreach ($emailFields as $field) {
$value = $mail->$field;
self::parseAddresses($value, $field, $params, $mail);
}
// define other parameters
$params['subject'] = $mail->subject;
$params['date'] = date("YmdHi00",
strtotime($mail->getHeader("Date"))
);
$attachments = array();
$params['body'] = self::formatMailPart($mail->body, $attachments);
// format and move attachments to the civicrm area
if (!empty($attachments)) {
$date = date('YmdHis');
$config = CRM_Core_Config::singleton();
for ($i = 0; $i < count($attachments); $i++) {
$attachNum = $i + 1;
$fileName = basename($attachments[$i]['fullName']);
$newName = CRM_Utils_File::makeFileName($fileName);
$location = $config->uploadDir . $newName;
// move file to the civicrm upload directory
rename($attachments[$i]['fullName'], $location);
$mimeType = "{$attachments[$i]['contentType']}/{$attachments[$i]['mimeType']}";
$params["attachFile_$attachNum"] = array(
'uri' => $fileName,
'type' => $mimeType,
'upload_date' => $date,
'location' => $location,
);
}
}
return $params;
}
/**
* @param $address
* @param array $params
* @param $subParam
* @param $mail
*/
public static function parseAddress(&$address, &$params, &$subParam, &$mail) {
// CRM-9484
if (empty($address->email)) {
return;
}
$subParam['email'] = $address->email;
$subParam['name'] = $address->name;
$contactID = self::getContactID($subParam['email'],
$subParam['name'],
TRUE,
$mail
);
$subParam['id'] = $contactID ? $contactID : NULL;
}
/**
* @param $addresses
* @param $token
* @param array $params
* @param $mail
*/
public static function parseAddresses(&$addresses, $token, &$params, &$mail) {
$params[$token] = array();
foreach ($addresses as $address) {
$subParam = array();
self::parseAddress($address, $params, $subParam, $mail);
$params[$token][] = $subParam;
}
}
/**
* Retrieve a contact ID and if not present.
*
* Create one with this email
*
* @param string $email
* @param string $name
* @param bool $create
* @param string $mail
*
* @return int|null
*/
public static function getContactID($email, $name = NULL, $create = TRUE, &$mail) {
$dao = CRM_Contact_BAO_Contact::matchContactOnEmail($email, 'Individual');
$contactID = NULL;
if ($dao) {
$contactID = $dao->contact_id;
}
$result = NULL;
CRM_Utils_Hook::emailProcessorContact($email, $contactID, $result);
if (!empty($result)) {
if ($result['action'] == self::EMAILPROCESSOR_IGNORE) {
return NULL;
}
if ($result['action'] == self::EMAILPROCESSOR_OVERRIDE) {
return $result['contactID'];
}
// else this is now create individual
// so we just fall out and do what we normally do
}
if ($contactID) {
return $contactID;
}
if (!$create) {
return NULL;
}
// contact does not exist, lets create it
$params = array(
'contact_type' => 'Individual',
'email-Primary' => $email,
);
CRM_Utils_String::extractName($name, $params);
return CRM_Contact_BAO_Contact::createProfileContact($params,
CRM_Core_DAO::$_nullArray
);
}
}

View file

@ -0,0 +1,580 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Migrate_Export {
const XML_VALUE_SEPARATOR = ":;:;:;";
/**
* @var array description of export field mapping
*
* @code
* 'exampleEntityMappingName' => array(
* 'data' => array(), // placeholder; this will get filled-in during execution
* 'name' => 'CustomGroup', // per-item XML tag name
* 'scope' => 'CustomGroups', // container XML tag name
* 'required' => FALSE, // whether we *must* find records of this type
* 'idNameFields' => array('id', 'name'), // name of the (local/autogenerated) "id" and (portable) "name" columns
* 'idNameMap' => array(), // placeholder; this will get filled-in during execution
* ),
* @endcode
*/
protected $_xml;
/**
* Class constructor.
*/
public function __construct() {
$this->_xml = array(
'customGroup' => array(
'data' => array(),
'name' => 'CustomGroup',
'scope' => 'CustomGroups',
'required' => FALSE,
'idNameFields' => array('id', 'name'),
'idNameMap' => array(),
),
'customField' => array(
'data' => array(),
'name' => 'CustomField',
'scope' => 'CustomFields',
'required' => FALSE,
'idNameFields' => array('id', 'column_name'),
'idNameMap' => array(),
'mappedFields' => array(
array('optionGroup', 'option_group_id', 'option_group_name'),
array('customGroup', 'custom_group_id', 'custom_group_name'),
),
),
'optionGroup' => array(
'data' => array(),
'name' => 'OptionGroup',
'scope' => 'OptionGroups',
'required' => FALSE,
'idNameMap' => array(),
'idNameFields' => array('id', 'name'),
),
'relationshipType' => array(
'data' => array(),
'name' => 'RelationshipType',
'scope' => 'RelationshipTypes',
'required' => FALSE,
'idNameFields' => array('id', 'name_a_b'),
'idNameMap' => array(),
),
'locationType' => array(
'data' => array(),
'name' => 'LocationType',
'scope' => 'LocationTypes',
'required' => FALSE,
'idNameFields' => array('id', 'name'),
'idNameMap' => array(),
),
'optionValue' => array(
'data' => array(),
'name' => 'OptionValue',
'scope' => 'OptionValues',
'required' => FALSE,
'idNameMap' => array(),
'idNameFields' => array('value', 'name', 'prefix'),
'mappedFields' => array(
array('optionGroup', 'option_group_id', 'option_group_name'),
),
),
'profileGroup' => array(
'data' => array(),
'name' => 'ProfileGroup',
'scope' => 'ProfileGroups',
'required' => FALSE,
'idNameFields' => array('id', 'title'),
'idNameMap' => array(),
),
'profileField' => array(
'data' => array(),
'name' => 'ProfileField',
'scope' => 'ProfileFields',
'required' => FALSE,
'idNameMap' => array(),
'mappedFields' => array(
array('profileGroup', 'uf_group_id', 'profile_group_name'),
),
),
'profileJoin' => array(
'data' => array(),
'name' => 'ProfileJoin',
'scope' => 'ProfileJoins',
'required' => FALSE,
'idNameMap' => array(),
'mappedFields' => array(
array('profileGroup', 'uf_group_id', 'profile_group_name'),
),
),
'mappingGroup' => array(
'data' => array(),
'name' => 'MappingGroup',
'scope' => 'MappingGroups',
'required' => FALSE,
'idNameFields' => array('id', 'name'),
'idNameMap' => array(),
'mappedFields' => array(
array('optionValue', 'mapping_type_id', 'mapping_type_name', 'mapping_type'),
),
),
'mappingField' => array(
'data' => array(),
'name' => 'MappingField',
'scope' => 'MappingFields',
'required' => FALSE,
'idNameMap' => array(),
'mappedFields' => array(
array('mappingGroup', 'mapping_id', 'mapping_group_name'),
array('locationType', 'location_type_id', 'location_type_name'),
array('relationshipType', 'relationship_type_id', 'relationship_type_name'),
),
),
);
}
/**
* Scan local customizations and build an in-memory representation.
*/
public function build() {
// fetch the option group / values for
// activity type and event_type
$optionGroups = "( 'activity_type', 'event_type', 'mapping_type' )";
$sql = "
SELECT distinct(g.id), g.*
FROM civicrm_option_group g
WHERE g.name IN $optionGroups
";
$this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
$sql = "
SELECT distinct(g.id), g.*
FROM civicrm_option_group g,
civicrm_custom_field f,
civicrm_custom_group cg
WHERE f.option_group_id = g.id
AND f.custom_group_id = cg.id
AND cg.is_active = 1
";
$this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
$sql = "
SELECT v.*, g.name as prefix
FROM civicrm_option_value v,
civicrm_option_group g
WHERE v.option_group_id = g.id
AND g.name IN $optionGroups
";
$this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
$sql = "
SELECT distinct(v.id), v.*, g.name as prefix
FROM civicrm_option_value v,
civicrm_option_group g,
civicrm_custom_field f,
civicrm_custom_group cg
WHERE v.option_group_id = g.id
AND f.option_group_id = g.id
AND f.custom_group_id = cg.id
AND cg.is_active = 1
";
$this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
$sql = "
SELECT rt.*
FROM civicrm_relationship_type rt
WHERE rt.is_active = 1
";
$this->fetch('relationshipType', 'CRM_Contact_DAO_RelationshipType', $sql);
$sql = "
SELECT lt.*
FROM civicrm_location_type lt
WHERE lt.is_active = 1
";
$this->fetch('locationType', 'CRM_Core_DAO_LocationType', $sql);
$sql = "
SELECT cg.*
FROM civicrm_custom_group cg
WHERE cg.is_active = 1
";
$this->fetch('customGroup', 'CRM_Core_DAO_CustomGroup', $sql);
$sql = "
SELECT f.*
FROM civicrm_custom_field f,
civicrm_custom_group cg
WHERE f.custom_group_id = cg.id
AND cg.is_active = 1
";
$this->fetch('customField', 'CRM_Core_DAO_CustomField', $sql);
$this->fetch('profileGroup', 'CRM_Core_DAO_UFGroup');
$this->fetch('profileField', 'CRM_Core_DAO_UFField');
$sql = "
SELECT *
FROM civicrm_uf_join
WHERE entity_table IS NULL
AND entity_id IS NULL
";
$this->fetch('profileJoin', 'CRM_Core_DAO_UFJoin', $sql);
$this->fetch('mappingGroup', 'CRM_Core_DAO_Mapping');
$this->fetch('mappingField', 'CRM_Core_DAO_MappingField');
}
/**
* Build custom groups.
*
* @param array $customGroupIds
* List of custom groups to export.
*/
public function buildCustomGroups($customGroupIds) {
$customGroupIdsSql = implode(',', array_filter($customGroupIds, 'is_numeric'));
if (empty($customGroupIdsSql)) {
return;
}
$sql = "
SELECT distinct(g.id), g.*
FROM civicrm_option_group g,
civicrm_custom_field f,
civicrm_custom_group cg
WHERE f.option_group_id = g.id
AND f.custom_group_id = cg.id
AND cg.id in ($customGroupIdsSql)
";
$this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
$sql = "
SELECT distinct(v.id), v.*, g.name as prefix
FROM civicrm_option_value v,
civicrm_option_group g,
civicrm_custom_field f,
civicrm_custom_group cg
WHERE v.option_group_id = g.id
AND f.option_group_id = g.id
AND f.custom_group_id = cg.id
AND cg.id in ($customGroupIdsSql)
";
$this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
$sql = "
SELECT cg.*
FROM civicrm_custom_group cg
WHERE cg.id in ($customGroupIdsSql)
";
$this->fetch('customGroup', 'CRM_Core_DAO_CustomGroup', $sql);
$sql = "
SELECT f.*
FROM civicrm_custom_field f,
civicrm_custom_group cg
WHERE f.custom_group_id = cg.id
AND cg.id in ($customGroupIdsSql)
";
$this->fetch('customField', 'CRM_Core_DAO_CustomField', $sql);
}
/**
* @param array $ufGroupIds
* List of custom groups to export.
*/
public function buildUFGroups($ufGroupIds) {
$ufGroupIdsSql = implode(',', array_filter($ufGroupIds, 'is_numeric'));
if (empty($ufGroupIdsSql)) {
return;
}
$sql = "
SELECT cg.*
FROM civicrm_uf_group cg
WHERE cg.id IN ($ufGroupIdsSql)
";
$this->fetch('profileGroup', 'CRM_Core_DAO_UFGroup', $sql);
$sql = "
SELECT f.*
FROM civicrm_uf_field f,
civicrm_uf_group cg
WHERE f.uf_group_id = cg.id
AND cg.id IN ($ufGroupIdsSql)
";
$this->fetch('profileField', 'CRM_Core_DAO_UFField', $sql);
$sql = "
SELECT *
FROM civicrm_uf_join
WHERE entity_table IS NULL
AND entity_id IS NULL
AND uf_group_id IN ($ufGroupIdsSql)
";
$this->fetch('profileJoin', 'CRM_Core_DAO_UFJoin', $sql);
}
/**
* Render the in-memory representation as XML
*
* @return string
* XML
*/
public function toXML() {
$buffer = '<?xml version="1.0" encoding="iso-8859-1" ?>';
$buffer .= "\n\n<CustomData>\n";
foreach (array_keys($this->_xml) as $key) {
if (!empty($this->_xml[$key]['data'])) {
$buffer .= " <{$this->_xml[$key]['scope']}>\n";
foreach ($this->_xml[$key]['data'] as $item) {
$buffer .= $this->renderKeyValueXML($this->_xml[$key]['name'], $item);
}
$buffer .= " </{$this->_xml[$key]['scope']}>\n";
}
elseif ($this->_xml[$key]['required']) {
CRM_Core_Error::fatal("No records in DB for $key");
}
}
$buffer .= "</CustomData>\n";
return $buffer;
}
/**
* Generate an array-tree representation of the exported elements.
*
* @return array
*/
public function toArray() {
$result = array();
foreach (array_keys($this->_xml) as $key) {
if (!empty($this->_xml[$key]['data'])) {
$result[$this->_xml[$key]['name']] = array_values($this->_xml[$key]['data']);
}
}
return $result;
}
/**
* @param string $groupName
* @param string $daoName
* @param null $sql
*/
public function fetch($groupName, $daoName, $sql = NULL) {
$idNameFields = isset($this->_xml[$groupName]['idNameFields']) ? $this->_xml[$groupName]['idNameFields'] : NULL;
$mappedFields = isset($this->_xml[$groupName]['mappedFields']) ? $this->_xml[$groupName]['mappedFields'] : NULL;
$dao = new $daoName();
if ($sql) {
$dao->query($sql);
}
else {
$dao->find();
}
while ($dao->fetch()) {
$this->_xml[$groupName]['data'][$dao->id] = $this->exportDAO($this->_xml[$groupName]['name'], $dao, $mappedFields);
if ($idNameFields) {
// index the id/name fields so that we can translate from FK ids to FK names
if (isset($idNameFields[2])) {
$this->_xml[$groupName]['idNameMap'][$dao->{$idNameFields[2]} . '.' . $dao->{$idNameFields[0]}] = $dao->{$idNameFields[1]};
}
else {
$this->_xml[$groupName]['idNameMap'][$dao->{$idNameFields[0]}] = $dao->{$idNameFields[1]};
}
}
}
}
/**
* Compute any fields of the entity defined by the $mappedFields specification
*
* @param array $mappedFields
* Each item is an array(0 => MappedEntityname, 1 => InputFieldName (id-field), 2 => OutputFieldName (name-field), 3 => OptionalPrefix).
* @param CRM_Core_DAO $dao
* The entity for which we want to prepare mapped fields.
* @return array
* new fields
*/
public function computeMappedFields($mappedFields, $dao) {
$keyValues = array();
if ($mappedFields) {
foreach ($mappedFields as $mappedField) {
if (isset($dao->{$mappedField[1]})) {
if (isset($mappedField[3])) {
$label = $this->_xml[$mappedField[0]]['idNameMap']["{$mappedField[3]}." . $dao->{$mappedField[1]}];
}
else {
$label = $this->_xml[$mappedField[0]]['idNameMap'][$dao->{$mappedField[1]}];
}
$keyValues[$mappedField[2]] = $label;
}
}
}
return $keyValues;
}
/**
* @param string $objectName
* Business-entity/xml-tag name.
* @param CRM_Core_DAO $object
* @param $mappedFields
*
* @return array
*/
public function exportDAO($objectName, $object, $mappedFields) {
$dbFields = &$object->fields();
// Filter the list of keys and values so that we only export interesting stuff
$keyValues = array();
foreach ($dbFields as $name => $dontCare) {
// ignore all ids
if ($name == 'id' || substr($name, -3, 3) == '_id') {
continue;
}
if (isset($object->$name) && $object->$name !== NULL) {
// hack for extends_entity_column_value
if ($name == 'extends_entity_column_value') {
if (in_array($object->extends, array(
'Event',
'Activity',
'Relationship',
'Individual',
'Organization',
'Household',
'Case',
))) {
if ($object->extends == 'Event') {
$key = 'event_type';
}
elseif ($object->extends == 'Activity') {
$key = 'activity_type';
}
elseif ($object->extends == 'Relationship') {
$key = 'relationship_type';
}
elseif ($object->extends == 'Case') {
$key = 'case_type';
}
$types = explode(CRM_Core_DAO::VALUE_SEPARATOR, substr($object->$name, 1, -1));
$values = array();
if (in_array($object->extends, array('Individual', 'Organization', 'Household'))) {
$key = 'contact_type';
$values = $types;
}
else {
foreach ($types as $type) {
if (in_array($key, array('activity_type', 'event_type', 'case_type'))) {
$ogID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $key, 'id', 'name');
$ovParams = array('option_group_id' => $ogID, 'value' => $type);
CRM_Core_BAO_OptionValue::retrieve($ovParams, $oValue);
$values[] = $oValue['name'];
}
else {
$relTypeName = CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_RelationshipType', $type, 'name_a_b', 'id');
$values[] = $relTypeName;
}
}
}
$keyValues['extends_entity_column_value_option_group'] = $key;
$value = implode(',', $values);
$object->extends_entity_column_value = $value;
}
else {
echo "This extension: {$object->extends} is not yet handled";
exit();
}
}
$value = $object->$name;
if ($name == 'field_name') {
// hack for profile field_name
if (substr($value, 0, 7) == 'custom_') {
$cfID = substr($value, 7);
list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField::getTableColumnGroup($cfID);
$value = "custom.{$tableName}.{$columnName}";
}
}
$keyValues[$name] = $value;
}
}
$keyValues += $this->computeMappedFields($mappedFields, $object);
return $keyValues;
}
/**
* @param string $tagName
* @param array $keyValues
* @throws Exception
* @return string
* XML
*/
public function renderKeyValueXML($tagName, $keyValues) {
$xml = " <$tagName>";
foreach ($keyValues as $k => $v) {
$xml .= "\n " . $this->renderTextTag($k, str_replace(CRM_Core_DAO::VALUE_SEPARATOR, self::XML_VALUE_SEPARATOR, $v));
}
$xml .= "\n </$tagName>\n";
return $xml;
}
/**
* @param string $name
* Tag name.
* @param string $value
* Text.
* @param string $prefix
*
* @throws Exception
* @return string
* XML
*/
public function renderTextTag($name, $value, $prefix = '') {
if (!preg_match('/^[a-zA-Z0-9\_]+$/', $name)) {
throw new Exception("Malformed tag name: $name");
}
return $prefix . "<$name>" . htmlentities($value) . "</$name>";
}
}

View file

@ -0,0 +1,649 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Migrate_ExportJSON {
const CHUNK_SIZE = 128;
protected $_contactIDs;
protected $_allContactIDs;
protected $_values;
protected $_discoverContacts = FALSE;
protected $_renameGroups = 1;
protected $_renameTags = 1;
protected $_sitePrefix = 'Site 1';
/**
* @param array $params
*/
public function __construct(&$params) {
foreach ($params as $name => $value) {
$varName = '_' . $name;
$this->$varName = $value;
}
}
/**
* Split a large array of contactIDs into more manageable smaller chunks.
*
* @param array $contactIDs
*
* @return array
*/
public function &splitContactIDs(&$contactIDs) {
// contactIDs could be a real large array, so we split it up into
// smaller chunks and then general xml for each chunk
$chunks = array();
$current = 0;
$chunks[$current] = array();
$count = 0;
foreach ($contactIDs as $k => $v) {
$chunks[$current][$k] = $v;
$count++;
if ($count == self::CHUNK_SIZE) {
$current++;
$chunks[$current] = array();
$count = 0;
}
}
if (empty($chunks[$current])) {
unset($chunks[$current]);
}
return $chunks;
}
/**
* Given a set of contact IDs get the values.
*
* @param array $contactIDs
* @param array $additionalContactIDs
*/
public function getValues(&$contactIDs, &$additionalContactIDs) {
$this->contact($contactIDs);
$this->address($contactIDs);
$this->phone($contactIDs);
$this->email($contactIDs);
$this->im($contactIDs);
$this->website($contactIDs);
$this->note($contactIDs);
$this->group($contactIDs);
$this->groupContact($contactIDs);
$this->savedSearch($contactIDs);
$this->tag($contactIDs);
$this->entityTag($contactIDs);
$this->relationship($contactIDs, $additionalContactIDs);
$this->activity($contactIDs, $additionalContactIDs);
}
public function metaData() {
$optionGroupVars = array(
'prefix_id' => 'individual_prefix',
'suffix_id' => 'individual_suffix',
'gender_id' => 'gender',
'mobile_provider' => 'mobile_provider',
'phone_type' => 'phone_type',
'activity_type' => 'activity_type',
'status_id' => 'activity_status_id',
'priority_id' => 'activity_priority_id',
'medium_id' => 'encounter_medium',
'communication_style_id' => 'communication_style',
'email_greeting' => 'email_greeting',
'postal_greeting' => 'postal_greeting',
'addressee_id' => 'addressee',
);
$this->optionGroup($optionGroupVars);
$auxilaryTables = array(
'civicrm_location_type' => 'CRM_Core_DAO_LocationType',
'civicrm_relationship_type' => 'CRM_Contact_DAO_RelationshipType',
);
$this->auxTable($auxilaryTables);
}
/**
* @param $tables
*/
public function auxTable($tables) {
foreach ($tables as $tableName => $daoName) {
$fields = &$this->dbFields($daoName, TRUE);
$sql = "SELECT * from $tableName";
$this->sql($sql, $tableName, $fields);
}
}
/**
* @param $optionGroupVars
*/
public function optionGroup($optionGroupVars) {
$names = array_values($optionGroupVars);
$str = array();
foreach ($names as $name) {
$str[] = "'$name'";
}
$nameString = implode(",", $str);
$sql = "
SELECT *
FROM civicrm_option_group
WHERE name IN ( $nameString )
";
$fields = &$this->dbFields('CRM_Core_DAO_OptionGroup', TRUE);
$this->sql($sql, 'civicrm_option_group', $fields);
$sql = "
SELECT v.*
FROM civicrm_option_value v
INNER JOIN civicrm_option_group g ON v.option_group_id = g.id
WHERE g.name IN ( $nameString )
";
$fields = &$this->dbFields('CRM_Core_DAO_OptionValue', TRUE);
$this->sql($sql, 'civicrm_option_value', $fields);
}
/**
* @param $ids
* @param string $tableName
* @param $fields
* @param $whereField
* @param null $additionalWhereCond
*/
public function table(
&$ids,
$tableName,
&$fields,
$whereField,
$additionalWhereCond = NULL
) {
if (empty($ids)) {
return;
}
$idString = implode(',', $ids);
$sql = "
SELECT *
FROM $tableName
WHERE $whereField IN ( $idString )
";
if ($additionalWhereCond) {
$sql .= " AND $additionalWhereCond";
}
$this->sql($sql, $tableName, $fields);
}
/**
* @param $sql
* @param string $tableName
* @param $fields
*/
public function sql($sql, $tableName, &$fields) {
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
$value = array();
foreach ($fields as $name) {
if (empty($dao->$name)) {
$value[$name] = NULL;
}
else {
$value[$name] = $dao->$name;
}
}
$this->appendValue($dao->id, $tableName, $value);
}
$dao->free();
}
/**
* @param $contactIDs
*/
public function contact(&$contactIDs) {
$fields = &$this->dbFields('CRM_Contact_DAO_Contact', TRUE);
$this->table($contactIDs, 'civicrm_contact', $fields, 'id', NULL);
}
/**
* @param $contactIDs
*/
public function note(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_Note', TRUE);
$this->table($contactIDs, 'civicrm_note', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
}
/**
* @param $contactIDs
*/
public function phone(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_Phone', TRUE);
$this->table($contactIDs, 'civicrm_phone', $fields, 'contact_id', NULL);
}
/**
* @param $contactIDs
*/
public function email(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_Email', TRUE);
$this->table($contactIDs, 'civicrm_email', $fields, 'contact_id', NULL);
}
/**
* @param $contactIDs
*/
public function im(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_IM', TRUE);
$this->table($contactIDs, 'civicrm_im', $fields, 'contact_id', NULL);
}
/**
* @param $contactIDs
*/
public function website(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_Website', TRUE);
$this->table($contactIDs, 'civicrm_website', $fields, 'contact_id', NULL);
}
/**
* @param $contactIDs
*/
public function address(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_Email', TRUE);
$this->table($contactIDs, 'civicrm_address', $fields, 'contact_id', NULL);
}
/**
* @param $contactIDs
*/
public function groupContact(&$contactIDs) {
$fields = &$this->dbFields('CRM_Contact_DAO_GroupContact', TRUE);
$this->table($contactIDs, 'civicrm_group_contact', $fields, 'contact_id', NULL);
}
/**
* @todo support group inheritance
*
* Parent child group ids are encoded in a text string
*
* @param $contactIDs
*/
public function group(&$contactIDs) {
// handle groups only once
static $_groupsHandled = array();
$ids = implode(',', $contactIDs);
$sql = "
SELECT DISTINCT group_id
FROM civicrm_group_contact
WHERE contact_id IN ( $ids )
";
$dao = CRM_Core_DAO::executeQuery($sql);
$groupIDs = array();
while ($dao->fetch()) {
if (!isset($_groupsHandled[$dao->group_id])) {
$groupIDs[] = $dao->group_id;
$_groupsHandled[$dao->group_id] = 1;
}
}
$fields = &$this->dbFields('CRM_Contact_DAO_Group', TRUE);
$this->table($groupIDs, 'civicrm_group', $fields, 'id');
$this->savedSearch($groupIDs);
}
/**
* @todo support search builder and custom saved searches
* @param $groupIDs
*/
public function savedSearch(&$groupIDs) {
if (empty($groupIDs)) {
return;
}
$idString = implode(",", $groupIDs);
$sql = "
SELECT s.*
FROM civicrm_saved_search s
INNER JOIN civicrm_group g on g.saved_search_id = s.id
WHERE g.id IN ( $idString )
";
$fields = &$this->dbFields('CRM_Contact_DAO_SavedSearch', TRUE);
$this->sql($sql, 'civicrm_saved_search', $fields);
}
/**
* @param $contactIDs
*/
public function entityTag(&$contactIDs) {
$fields = &$this->dbFields('CRM_Core_DAO_EntityTag', TRUE);
$this->table($contactIDs, 'civicrm_entity_tag', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
}
/**
* @param $contactIDs
*/
public function tag(&$contactIDs) {
// handle tags only once
static $_tagsHandled = array();
$ids = implode(',', $contactIDs);
$sql = "
SELECT DISTINCT tag_id
FROM civicrm_entity_tag
WHERE entity_id IN ( $ids )
AND entity_table = 'civicrm_contact'
";
$dao = CRM_Core_DAO::executeQuery($sql);
$tagIDs = array();
while ($dao->fetch()) {
if (!isset($_tagsHandled[$dao->tag_id])) {
$tagIDs[] = $dao->tag_id;
$_tagsHandled[$dao->tag_id] = 1;
}
}
$fields = &$this->dbFields('CRM_Core_DAO_Tag', TRUE);
$this->table($tagIDs, 'civicrm_tag', $fields, 'id');
}
/**
* @param $contactIDs
* @param $additionalContacts
*/
public function relationship(&$contactIDs, &$additionalContacts) {
// handle relationships only once
static $_relationshipsHandled = array();
$ids = implode(',', $contactIDs);
$sql = "(
SELECT r.*
FROM civicrm_relationship r
WHERE r.contact_id_a IN ( $ids )
) UNION (
SELECT r.*
FROM civicrm_relationship r
WHERE r.contact_id_b IN ( $ids )
)
";
$fields = $this->dbFields('CRM_Contact_DAO_Relationship', TRUE);
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
if (isset($_relationshipsHandled[$dao->id])) {
continue;
}
$_relationshipsHandled[$dao->id] = $dao->id;
$relationship = array();
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
$relationship[$fld] = NULL;
}
else {
$relationship[$fld] = $dao->$fld;
}
}
$this->appendValue($dao->id, 'civicrm_relationship', $relationship);
$this->addAdditionalContacts(array(
$dao->contact_id_a,
$dao->contact_id_b,
),
$additionalContacts
);
}
$dao->free();
}
/**
* @param $contactIDs
* @param $additionalContacts
*/
public function activity(&$contactIDs, &$additionalContacts) {
static $_activitiesHandled = array();
$activityContacts = CRM_Activity_BAO_ActivityContact::buildOptions('record_type_id', 'validate');
$assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
$targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
$ids = implode(',', $contactIDs);
// query framing returning all contacts in valid activity
$sql = "
SELECT a.*, ac.id as acID, ac.activity_id, ac.contact_id, ac.record_type_id
FROM civicrm_activity a
INNER JOIN civicrm_activity_contact ac ON ac.activity_id = a.id
WHERE ac.contact_id IN ( $ids )
AND (a.activity_type_id != 3 AND a.activity_type_id != 20)
";
$fields = &$this->dbFields('CRM_Activity_DAO_Activity', TRUE);
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
// adding source, target and assignee contacts in additional contacts array
$this->addAdditionalContacts(array($dao->contact_id),
$additionalContacts
);
// append values of activity contacts
$activityContacts = array(
'id' => $dao->acID,
'contact_id' => $dao->contact_id,
'activity_id' => $dao->activity_id,
'record_type_id' => $dao->record_type_id,
);
$this->appendValue($dao->acID, 'civicrm_activity_contact', $activityContacts);
if (isset($_activitiesHandled[$dao->id])) {
continue;
}
$_activitiesHandled[$dao->id] = $dao->id;
$activity = array();
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
$activity[$fld] = NULL;
}
else {
$activity[$fld] = $dao->$fld;
}
}
// append activity value
$this->appendValue($dao->id, 'civicrm_activity', $activity);
}
$dao->free();
}
/**
* @param int $id
* @param string $name
* @param $value
*/
public function appendValue($id, $name, $value) {
if (empty($value)) {
return;
}
if (!isset($this->_values[$name])) {
$this->_values[$name] = array();
$this->_values[$name][] = array_keys($value);
}
$this->_values[$name][] = array_values($value);
}
/**
* @param string $daoName
* @param bool $onlyKeys
*
* @return array
*/
public function dbFields($daoName, $onlyKeys = FALSE) {
static $_fieldsRetrieved = array();
if (!isset($_fieldsRetrieved[$daoName])) {
$_fieldsRetrieved[$daoName] = array();
$daoFile = str_replace('_',
DIRECTORY_SEPARATOR,
$daoName
) . '.php';
include_once $daoFile;
$daoFields = &$daoName::fields();
foreach ($daoFields as $key => & $value) {
$_fieldsRetrieved[$daoName][$value['name']] = array(
'uniqueName' => $key,
'type' => $value['type'],
'title' => CRM_Utils_Array::value('title', $value, NULL),
);
}
}
if ($onlyKeys) {
return array_keys($_fieldsRetrieved[$daoName]);
}
else {
return $_fieldsRetrieved[$daoName];
}
}
/**
* @param $contactIDs
* @param $additionalContacts
*/
public function addAdditionalContacts($contactIDs, &$additionalContacts) {
if (!$this->_discoverContacts) {
return;
}
foreach ($contactIDs as $cid) {
if ($cid &&
!isset($this->_allContactIDs[$cid]) &&
!isset($additionalContacts[$cid])
) {
$additionalContacts[$cid] = $cid;
}
}
}
/**
* @param $contactIDs
*/
public function export(&$contactIDs) {
$chunks = &$this->splitContactIDs($contactIDs);
$additionalContactIDs = array();
foreach ($chunks as $chunk) {
$this->getValues($chunk, $additionalContactIDs);
}
if (!empty($additionalContactIDs)) {
$this->_allContactIDs = $this->_allContactIDs + $additionalContactIDs;
$this->export($additionalContactIDs);
}
}
/**
* @param string $fileName
* @param null $lastExportTime
* @param bool $discoverContacts
*/
public function run(
$fileName,
$lastExportTime = NULL,
$discoverContacts = FALSE
) {
$this->_discoverContacts = $discoverContacts;
if (!$lastExportTime) {
$sql = "
SELECT id
FROM civicrm_contact
";
}
else {
$sql = "(
SELECT DISTINCT entity_id
FROM civicrm_log
WHERE entity_table = 'civicrm_contact'
AND modified_date >= $lastExportTime
) UNION (
SELECT DISTINCT contact_id
FROM civicrm_subscription_history
WHERE date >= $lastExportTime
)
";
}
$dao = &CRM_Core_DAO::executeQuery($sql);
$contactIDs = array();
while ($dao->fetch()) {
$contactIDs[$dao->id] = $dao->id;
}
$this->_allContactIDs = $contactIDs;
$this->_values = array();
$this->metaData();
$this->export($contactIDs);
$json = json_encode($this->_values, JSON_NUMERIC_CHECK);
file_put_contents($fileName,
$json
);
// print_r( json_decode( $json ) );
}
}

View file

@ -0,0 +1,480 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Migrate_Import {
/**
* Class constructor.
*/
public function __construct() {
}
/**
* Import custom-data from an XML file.
*
* @param string $file
* Path to an XML file.
*
* @throws CRM_Core_Exception
*/
public function run($file) {
// read xml file
$dom = new DomDocument();
$xmlString = file_get_contents($file);
$load = $dom->loadXML($xmlString);
if (!$load) {
throw new CRM_Core_Exception("Failed to parse XML file \"$file\"");
}
$dom->xinclude();
$xml = simplexml_import_dom($dom);
return $this->runXmlElement($xml);
}
/**
* Import custom-data from an XML element.
*
* @param SimpleXMLElement $xml
*/
public function runXmlElement($xml) {
$idMap = array(
'custom_group' => array(),
'option_group' => array(),
);
// first create option groups and values if any
$this->optionGroups($xml, $idMap);
$this->optionValues($xml, $idMap);
$this->relationshipTypes($xml);
$this->contributionTypes($xml);
// now create custom groups
$this->customGroups($xml, $idMap);
$this->customFields($xml, $idMap);
// now create profile groups
$this->profileGroups($xml, $idMap);
$this->profileFields($xml, $idMap);
$this->profileJoins($xml, $idMap);
// create DB Template String sample data
$this->dbTemplateString($xml, $idMap);
// clean up all caches etc
CRM_Core_Config::clearDBCache();
}
/**
* @param CRM_Core_DAO $dao
* @param $xml
* @param bool $save
* @param null $keyName
*
* @return bool
*/
public function copyData(&$dao, &$xml, $save = FALSE, $keyName = NULL) {
if ($keyName) {
if (isset($xml->$keyName)) {
$dao->$keyName = (string ) $xml->$keyName;
if ($dao->find(TRUE)) {
CRM_Core_Session::setStatus(ts("Found %1, %2, %3",
array(
1 => $keyName,
2 => $dao->$keyName,
3 => $dao->__table,
)
), '', 'info');
return FALSE;
}
}
}
$fields = &$dao->fields();
foreach ($fields as $name => $dontCare) {
if (isset($xml->$name)) {
$value = (string ) $xml->$name;
$value = str_replace(CRM_Utils_Migrate_Export::XML_VALUE_SEPARATOR,
CRM_Core_DAO::VALUE_SEPARATOR,
$value
);
$dao->$name = $value;
}
}
if ($save) {
$dao->save();
}
return TRUE;
}
/**
* @param $xml
* @param $idMap
*/
public function optionGroups(&$xml, &$idMap) {
foreach ($xml->OptionGroups as $optionGroupsXML) {
foreach ($optionGroupsXML->OptionGroup as $optionGroupXML) {
$optionGroup = new CRM_Core_DAO_OptionGroup();
$this->copyData($optionGroup, $optionGroupXML, TRUE, 'name');
$idMap['option_group'][$optionGroup->name] = $optionGroup->id;
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function optionValues(&$xml, &$idMap) {
foreach ($xml->OptionValues as $optionValuesXML) {
foreach ($optionValuesXML->OptionValue as $optionValueXML) {
$optionValue = new CRM_Core_DAO_OptionValue();
$optionValue->option_group_id = $idMap['option_group'][(string ) $optionValueXML->option_group_name];
if (empty($optionValue->option_group_id)) {
//CRM-17410 check if option group already exist.
$optionValue->option_group_id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $optionValueXML->option_group_name, 'id', 'name');
}
$this->copyData($optionValue, $optionValueXML, FALSE, 'label');
if (!isset($optionValue->value)) {
$sql = "
SELECT MAX(ROUND(v.value)) + 1
FROM civicrm_option_value v
WHERE v.option_group_id = %1
";
$params = array(1 => array($optionValue->option_group_id, 'Integer'));
$optionValue->value = CRM_Core_DAO::singleValueQuery($sql, $params);
}
$optionValue->save();
}
}
}
/**
* @param $xml
*/
public function relationshipTypes(&$xml) {
foreach ($xml->RelationshipTypes as $relationshipTypesXML) {
foreach ($relationshipTypesXML->RelationshipType as $relationshipTypeXML) {
$relationshipType = new CRM_Contact_DAO_RelationshipType();
$this->copyData($relationshipType, $relationshipTypeXML, TRUE, 'name_a_b');
}
}
}
/**
* @param $xml
*/
public function contributionTypes(&$xml) {
foreach ($xml->ContributionTypes as $contributionTypesXML) {
foreach ($contributionTypesXML->ContributionType as $contributionTypeXML) {
$contributionType = new CRM_Financial_DAO_FinancialType();
$this->copyData($contributionType, $contributionTypeXML, TRUE, 'name');
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function customGroups(&$xml, &$idMap) {
foreach ($xml->CustomGroups as $customGroupsXML) {
foreach ($customGroupsXML->CustomGroup as $customGroupXML) {
$customGroup = new CRM_Core_DAO_CustomGroup();
if (!$this->copyData($customGroup, $customGroupXML, TRUE, 'name')) {
$idMap['custom_group'][$customGroup->name] = $customGroup->id;
continue;
}
$saveAgain = FALSE;
if (!isset($customGroup->table_name) ||
empty($customGroup->table_name)
) {
// fix table name
$customGroup->table_name = "civicrm_value_" . strtolower(CRM_Utils_String::munge($customGroup->title, '_', 32)) . "_{$customGroup->id}";
$saveAgain = TRUE;
}
// fix extends stuff if it exists
if (isset($customGroupXML->extends_entity_column_value_option_group) &&
isset($customGroupXML->extends_entity_column_value)
) {
$valueIDs = array();
$optionValues = explode(",", $customGroupXML->extends_entity_column_value);
$optValues = implode("','", $optionValues);
if (trim($customGroup->extends) != 'Participant') {
if ($customGroup->extends == 'Relationship') {
foreach ($optionValues as $key => $value) {
$relTypeId = CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_RelationshipType', $value, 'id', 'name_a_b');
$valueIDs[] = $relTypeId;
}
}
elseif (in_array($customGroup->extends, array('Individual', 'Organization', 'Household'))) {
$valueIDs = $optionValues;
}
else {
$sql = "
SELECT v.value
FROM civicrm_option_value v
INNER JOIN civicrm_option_group g ON g.id = v.option_group_id
WHERE g.name = %1
AND v.name IN ('$optValues')
";
$params = array(
1 => array(
(string ) $customGroupXML->extends_entity_column_value_option_group,
'String',
),
);
$dao = &CRM_Core_DAO::executeQuery($sql, $params);
while ($dao->fetch()) {
$valueIDs[] = $dao->value;
}
}
if (!empty($valueIDs)) {
$customGroup->extends_entity_column_value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
$valueIDs
) . CRM_Core_DAO::VALUE_SEPARATOR;
unset($valueIDs);
// Note: No need to set extends_entity_column_id here.
$saveAgain = TRUE;
}
}
else {
// when custom group extends 'Participant'
$sql = "
SELECT v.value
FROM civicrm_option_value v
INNER JOIN civicrm_option_group g ON g.id = v.option_group_id
WHERE g.name = 'custom_data_type'
AND v.name = %1
";
$params = array(
1 => array(
(string ) $customGroupXML->extends_entity_column_value_option_group,
'String',
),
);
$valueID = (int ) CRM_Core_DAO::singleValueQuery($sql, $params);
if ($valueID) {
$customGroup->extends_entity_column_id = $valueID;
}
$optionIDs = array();
switch ($valueID) {
case 1:
// ParticipantRole
$condition = "AND v.name IN ( '{$optValues}' )";
$optionIDs = CRM_Core_OptionGroup::values('participant_role', FALSE, FALSE, FALSE, $condition, 'name');
break;
case 2:
// ParticipantEventName
$condition = "( is_template IS NULL OR is_template != 1 ) AND title IN( '{$optValues}' )";
$optionIDs = CRM_Event_PseudoConstant::event(NULL, FALSE, $condition);
break;
case 3:
// ParticipantEventType
$condition = "AND v.name IN ( '{$optValues}' )";
$optionIDs = CRM_Core_OptionGroup::values('event_type', FALSE, FALSE, FALSE, $condition, 'name');
break;
}
if (is_array($optionIDs) && !empty($optionIDs)) {
$customGroup->extends_entity_column_value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
array_keys($optionIDs)
) . CRM_Core_DAO::VALUE_SEPARATOR;
$saveAgain = TRUE;
}
}
}
if ($saveAgain) {
$customGroup->save();
}
CRM_Core_BAO_CustomGroup::createTable($customGroup);
$idMap['custom_group'][$customGroup->name] = $customGroup->id;
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function customFields(&$xml, &$idMap) {
// Re-index by group id so we can build out the custom fields one table
// at a time, and then rebuild the table triggers at the end, rather than
// rebuilding the table triggers after each field is added (which is
// painfully slow).
$fields_indexed_by_group_id = array();
foreach ($xml->CustomFields as $customFieldsXML) {
$total = count($customFieldsXML->CustomField);
foreach ($customFieldsXML->CustomField as $customFieldXML) {
$id = $idMap['custom_group'][(string ) $customFieldXML->custom_group_name];
$fields_indexed_by_group_id[$id][] = $customFieldXML;
}
}
while (list($group_id, $fields) = each($fields_indexed_by_group_id)) {
$total = count($fields);
$count = 0;
while (list(, $customFieldXML) = each($fields)) {
$count++;
$customField = new CRM_Core_DAO_CustomField();
$customField->custom_group_id = $group_id;
$skipStore = FALSE;
if (!$this->copyData($customField, $customFieldXML, FALSE, 'label')) {
$skipStore = TRUE;
}
if (empty($customField->option_group_id) &&
isset($customFieldXML->option_group_name)
) {
$customField->option_group_id = $idMap['option_group'][(string ) $customFieldXML->option_group_name];
}
if ($skipStore) {
continue;
}
$customField->save();
// Only rebuild the table's trigger on the last field added to avoid un-necessary
// and slow rebuilds when adding many fields at the same time.
$triggerRebuild = FALSE;
if ($count == $total) {
$triggerRebuild = TRUE;
}
$indexExist = FALSE;
CRM_Core_BAO_CustomField::createField($customField, 'add', $indexExist, $triggerRebuild);
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function dbTemplateString(&$xml, &$idMap) {
foreach ($xml->Persistent as $persistentXML) {
foreach ($persistentXML->Persistent as $persistent) {
$persistentObj = new CRM_Core_DAO_Persistent();
if ($persistent->is_config == 1) {
$persistent->data = serialize(explode(',', $persistent->data));
}
$this->copyData($persistentObj, $persistent, TRUE, 'context');
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function profileGroups(&$xml, &$idMap) {
foreach ($xml->ProfileGroups as $profileGroupsXML) {
foreach ($profileGroupsXML->ProfileGroup as $profileGroupXML) {
$profileGroup = new CRM_Core_DAO_UFGroup();
$this->copyData($profileGroup, $profileGroupXML, TRUE, 'title');
$idMap['profile_group'][$profileGroup->name] = $profileGroup->id;
$idMap['profile_group'][$profileGroup->title] = $profileGroup->id;
}
}
}
/**
* @param $xml
* @param $idMap
*
* @throws Exception
*/
public function profileFields(&$xml, &$idMap) {
foreach ($xml->ProfileFields as $profileFieldsXML) {
foreach ($profileFieldsXML->ProfileField as $profileFieldXML) {
$profileField = new CRM_Core_DAO_UFField();
$profileField->uf_group_id = $idMap['profile_group'][(string ) $profileFieldXML->profile_group_name];
$this->copyData($profileField, $profileFieldXML, FALSE, 'field_name');
// fix field name
if (substr($profileField->field_name, 0, 7) == 'custom.') {
list($dontCare, $tableName, $columnName) = explode('.', $profileField->field_name);
$sql = "
SELECT f.id
FROM civicrm_custom_field f
INNER JOIN civicrm_custom_group g ON f.custom_group_id = g.id
WHERE g.table_name = %1
AND f.column_name = %2
";
$params = array(
1 => array($tableName, 'String'),
2 => array($columnName, 'String'),
);
$cfID = CRM_Core_DAO::singleValueQuery($sql, $params);
if (!$cfID) {
CRM_Core_Error::fatal(ts("Could not find custom field for %1, %2, %3",
array(
1 => $profileField->field_name,
2 => $tableName,
3 => $columnName,
)
) . "<br />");
}
$profileField->field_name = "custom_{$cfID}";
}
$profileField->save();
}
}
}
/**
* @param $xml
* @param $idMap
*/
public function profileJoins(&$xml, &$idMap) {
foreach ($xml->ProfileJoins as $profileJoinsXML) {
foreach ($profileJoinsXML->ProfileJoin as $profileJoinXML) {
$profileJoin = new CRM_Core_DAO_UFJoin();
$profileJoin->uf_group_id = $idMap['profile_group'][(string ) $profileJoinXML->profile_group_name];
$this->copyData($profileJoin, $profileJoinXML, FALSE, 'module');
$profileJoin->save();
}
}
}
}

View file

@ -0,0 +1,304 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Migrate_ImportJSON {
protected $_lookupCache;
protected $_saveMapping;
/**
* Class constructor.
*/
public function __construct() {
$this->_lookupCache = array();
$this->_saveMapping = array();
}
/**
* Run import.
*
* @param string $file
*/
public function run($file) {
$json = file_get_contents($file);
$decodedContacts = json_decode($json);
// migrate contact data
$this->contact($decodedContacts->civicrm_contact);
$this->email($decodedContacts->civicrm_email);
$this->phone($decodedContacts->civicrm_phone);
$this->address($decodedContacts->civicrm_address);
$this->note($decodedContacts->civicrm_note);
$this->relationship($decodedContacts->civicrm_relationship);
$this->activity($decodedContacts->civicrm_activity,
$decodedContacts->civicrm_activity_contact
);
$this->group($decodedContacts->civicrm_group,
$decodedContacts->civicrm_group_contact
);
$this->tag($decodedContacts->civicrm_tag,
$decodedContacts->civicrm_entity_tag
);
// clean up all caches etc
CRM_Core_Config::clearDBCache();
}
/**
* @param $contact
*/
public function contact(&$contact) {
$this->restore($contact,
'CRM_Contact_DAO_Contact',
array('id' => 'civicrm_contact'),
array('birth_date', 'deceased_date', 'created_date', 'modified_date')
);
}
/**
* @param $email
*/
public function email(&$email) {
$this->restore($email,
'CRM_Core_DAO_Email',
array('contact_id' => 'civicrm_contact')
);
}
/**
* @param $phone
*/
public function phone(&$phone) {
$this->restore($phone,
'CRM_Core_DAO_Phone',
array('contact_id' => 'civicrm_contact')
);
}
/**
* @param $address
*/
public function address(&$address) {
$this->restore($address,
'CRM_Core_DAO_Address',
array('contact_id' => 'civicrm_contact')
);
}
/**
* @param $note
*/
public function note(&$note) {
$this->restore($note,
'CRM_Core_DAO_Note',
array('contact_id' => 'civicrm_contact'),
array('modified_date')
);
}
/**
* @param $relationship
*/
public function relationship(&$relationship) {
$this->restore($relationship,
'CRM_Contact_DAO_Relationship',
array(
'contact_id_a' => 'civicrm_contact',
'contact_id_b' => 'civicrm_contact',
)
);
}
/**
* @param $activity
* @param $activityContacts
*/
public function activity($activity, $activityContacts) {
$this->restore($activity,
'CRM_Activity_DAO_Activity',
NULL,
array('activity_date_time')
);
$this->restore($activityContacts,
'CRM_Activity_DAO_ActivityContact',
array(
'contact_id' => 'civicrm_contact',
'activity_id' => 'civicrm_activity',
)
);
}
/**
* @param $group
* @param $groupContact
*/
public function group($group, $groupContact) {
$this->restore($group,
'CRM_Contact_DAO_Group',
NULL,
array('cache_date', 'refresh_date')
);
$this->restore($groupContact,
'CRM_Contact_DAO_GroupContact',
array(
'group_id' => 'civicrm_group',
'contact_id' => 'civicrm_contact',
)
);
}
/**
* @param $tag
* @param $entityTag
*/
public function tag($tag, $entityTag) {
$this->restore($tag,
'CRM_Core_DAO_Tag',
array(
'created_id' => 'civicrm_contact',
'parent_id' => 'civicrm_tag',
)
);
$this->restore($entityTag,
'CRM_Core_DAO_EntityTag',
array(
'entity_id' => 'civicrm_contact',
'tag_id' => 'civicrm_tag',
)
);
}
/**
* @param $chunk
* @param string $daoName
* @param null $lookUpMapping
* @param null $dateFields
*/
public function restore(&$chunk, $daoName, $lookUpMapping = NULL, $dateFields = NULL) {
$object = new $daoName();
$tableName = $object->__table;
if (is_array($lookUpMapping)) {
$lookUpMapping['id'] = $tableName;
}
else {
$lookUpMapping = array('id' => $tableName);
}
foreach ($lookUpMapping as $columnName => $tableName) {
$this->populateCache($tableName);
}
$saveMapping = FALSE;
$columns = $chunk[0];
foreach ($chunk as $key => $value) {
if ($key) {
$object = new $daoName();
foreach ($columns as $k => $column) {
if ($column == 'id') {
$childID = $value[$k];
$masterID = CRM_Utils_Array::value($value[$k],
$this->_lookupCache[$tableName],
NULL
);
if ($masterID) {
$object->id = $masterID;
}
}
else {
if (array_key_exists($column, $lookUpMapping)) {
$object->$column = $this->_lookupCache[$lookUpMapping[$column]][$value[$k]];
}
elseif (!empty($dateFields) && in_array($column, $dateFields)) {
$object->$column = CRM_Utils_Date::isoToMysql($value[$k]);
}
else {
$object->$column = $value[$k];
}
}
}
$object->save();
if (!$masterID) {
$this->_lookupCache[$tableName][$childID] = $object->id;
$this->_saveMapping[$tableName] = TRUE;
}
}
}
}
public function saveCache() {
$sql = "INSERT INTO civicrm_migration_mapping (master_id, slave_id, entity_table ) VALUES ";
foreach ($this->_lookupCache as $tableName => & $values) {
if (!$this->_saveMapping[$tableName]) {
continue;
}
$mapValues = array();
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_migration_mapping where entity_table = '$tableName'");
foreach ($values as $childID => $masterID) {
$mapValues[] = "($masterID,$childID,'$tableName' )";
}
$insertSQL = $sql . implode(",\n", $mapValues);
CRM_Core_DAO::executeQuery($insertSQL);
}
}
/**
* @param string $tableName
*/
public function populateCache($tableName) {
if (isset($this->_lookupCache[$tableName])) {
return;
}
$this->_lookupCache[$tableName] = array();
$this->_saveMapping[$tableName] = FALSE;
$query = "SELECT master_id, slave_id
FROM civicrm_migration_mapping
WHERE entity_table = '{$tableName}'
";
$dao = CRM_Core_DAO::executeQuery($query);
while ($dao->fetch()) {
$this->_lookupCache[$dao->slave_id] = $dao->master_id;
}
}
}

View file

@ -0,0 +1,144 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Money utilties
*/
class CRM_Utils_Money {
static $_currencySymbols = NULL;
/**
* Format a monetary string.
*
* Format a monetary string basing on the amount provided,
* ISO currency code provided and a format string consisting of:
*
* %a - the formatted amount
* %C - the currency ISO code (e.g., 'USD') if provided
* %c - the currency symbol (e.g., '$') if available
*
* @param float $amount
* The monetary amount to display (1234.56).
* @param string $currency
* The three-letter ISO currency code ('USD').
* @param string $format
* The desired currency format.
* @param bool $onlyNumber
* @param string $valueFormat
* The desired monetary value display format (e.g. '%!i').
*
* @return string
* formatted monetary string
*
*/
public static function format($amount, $currency = NULL, $format = NULL, $onlyNumber = FALSE, $valueFormat = NULL) {
if (CRM_Utils_System::isNull($amount)) {
return '';
}
$config = CRM_Core_Config::singleton();
if (!$format) {
$format = $config->moneyformat;
}
if (!$valueFormat) {
$valueFormat = $config->moneyvalueformat;
}
if ($onlyNumber) {
// money_format() exists only in certain PHP install (CRM-650)
if (is_numeric($amount) and function_exists('money_format')) {
$amount = money_format($valueFormat, $amount);
}
return $amount;
}
if (!self::$_currencySymbols) {
self::$_currencySymbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', array(
'keyColumn' => 'name',
'labelColumn' => 'symbol',
));
}
if (!$currency) {
$currency = $config->defaultCurrency;
}
// money_format() exists only in certain PHP install (CRM-650)
// setlocale() affects native gettext (CRM-11054, CRM-9976)
if (is_numeric($amount) && function_exists('money_format')) {
$lc = setlocale(LC_MONETARY, 0);
setlocale(LC_MONETARY, 'en_US.utf8', 'en_US', 'en_US.utf8', 'en_US', 'C');
$amount = money_format($valueFormat, $amount);
setlocale(LC_MONETARY, $lc);
}
$rep = array(
',' => $config->monetaryThousandSeparator,
'.' => $config->monetaryDecimalPoint,
);
// If it contains tags, means that HTML was passed and the
// amount is already converted properly,
// so don't mess with it again.
if (strpos($amount, '<') === FALSE) {
$amount = strtr($amount, $rep);
}
$replacements = array(
'%a' => $amount,
'%C' => $currency,
'%c' => CRM_Utils_Array::value($currency, self::$_currencySymbols, $currency),
);
return strtr($format, $replacements);
}
/**
* This is a placeholder function for calculating the number of decimal places for a currency.
*
* Currently code assumes 2 decimal places but some currencies (bitcoin, middle eastern) have
* more. By using this function we can signpost the locations where the number of decimal places is
* currency specific for future enhancement.
*
* @param string $currency
*
* @return int
* Number of decimal places.
*/
public static function getCurrencyPrecision($currency = NULL) {
return 2;
}
}

View file

@ -0,0 +1,92 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Simple static helpers for network operations
*/
class CRM_Utils_Network {
/**
* Try connecting to a TCP service; if it fails, retry. Repeat until serverStartupTimeOut elapses.
*
* @param $host
* @param $port
* @param int $serverStartupTimeOut
* Seconds.
* @param float $interval
* Seconds to wait in between pollings.
*
* @return bool
* TRUE if service is online
*/
public static function waitForServiceStartup($host, $port, $serverStartupTimeOut, $interval = 0.333) {
$start = time();
$end = $start + $serverStartupTimeOut;
$found = FALSE;
$interval_usec = (int) 1000000 * $interval;
while (!$found && $end >= time()) {
$found = self::checkService($host, $port, $end - time());
if ($found) {
return TRUE;
}
usleep($interval_usec);
}
return FALSE;
}
/**
* Check whether a TCP service is available on $host and $port.
*
* @param string $host
* @param string $port
* @param string $serverConnectionTimeOut
*
* @return bool
*/
public static function checkService($host, $port, $serverConnectionTimeOut) {
$old_error_reporting = error_reporting();
error_reporting($old_error_reporting & ~E_WARNING);
try {
$fh = fsockopen($host, $port, $errno, $errstr, $serverConnectionTimeOut);
if ($fh) {
fclose($fh);
error_reporting($old_error_reporting);
return TRUE;
}
}
catch (Exception $e) {
}
error_reporting($old_error_reporting);
return FALSE;
}
}

View file

@ -0,0 +1,118 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_Number
*/
class CRM_Utils_Number {
/**
* Create a random number with a given precision.
*
* @param array $precision
* (int $significantDigits, int $postDecimalDigits).
*
* @return float
*
* @link https://dev.mysql.com/doc/refman/5.1/en/fixed-point-types.html
*/
public static function createRandomDecimal($precision) {
list ($sigFigs, $decFigs) = $precision;
$rand = rand(0, pow(10, $sigFigs) - 1);
return $rand / pow(10, $decFigs);
}
/**
* Given a number, coerce it to meet the precision requirement. If possible, it should
* keep the number as-is. If necessary, this may drop the least-significant digits
* and/or move the decimal place.
*
* @param int|float $keyValue
* @param array $precision
* (int $significantDigits, int $postDecimalDigits).
* @return float
* @link https://dev.mysql.com/doc/refman/5.1/en/fixed-point-types.html
*/
public static function createTruncatedDecimal($keyValue, $precision) {
list ($sigFigs, $decFigs) = $precision;
$sign = ($keyValue < 0) ? '-1' : 1;
$val = str_replace('.', '', abs($keyValue)); // ex: -123.456 ==> 123456
$val = substr($val, 0, $sigFigs); // ex: 123456 => 1234
// Move any extra digits after decimal
$extraFigs = strlen($val) - ($sigFigs - $decFigs);
if ($extraFigs > 0) {
return $sign * $val / pow(10, $extraFigs); // ex: 1234 => 1.234
}
else {
return $sign * $val;
}
}
/**
* Some kind of numbery-looky-printy thing.
*
* @param string $size
* @param bool $checkForPostMax
*
* @return int
*/
public static function formatUnitSize($size, $checkForPostMax = FALSE) {
if ($size) {
$last = strtolower($size{strlen($size) - 1});
switch ($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$size *= 1024;
case 'm':
$size *= 1024;
case 'k':
$size *= 1024;
}
if ($checkForPostMax) {
$maxImportFileSize = self::formatUnitSize(ini_get('upload_max_filesize'));
$postMaxSize = self::formatUnitSize(ini_get('post_max_size'));
if ($maxImportFileSize > $postMaxSize && $postMaxSize == $size) {
CRM_Core_Session::setStatus(ts("Note: Upload max filesize ('upload_max_filesize') should not exceed Post max size ('post_max_size') as defined in PHP.ini, please check with your system administrator."), ts("Warning"), "alert");
}
// respect php.ini upload_max_filesize
if ($size > $maxImportFileSize && $size !== $postMaxSize) {
$size = $maxImportFileSize;
CRM_Core_Session::setStatus(ts("Note: Please verify your configuration for Maximum File Size (in MB) <a href='%1'>Administrator >> System Settings >> Misc</a>. It should support 'upload_max_size' as defined in PHP.ini.Please check with your system administrator.", array(1 => CRM_Utils_System::url('civicrm/admin/setting/misc', 'reset=1'))), ts("Warning"), "alert");
}
}
return $size;
}
}
}

View file

@ -0,0 +1,570 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'packages/OpenFlashChart/php-ofc-library/open-flash-chart.php';
/**
* Build various graphs using Open Flash Chart library.
*/
class CRM_Utils_OpenFlashChart {
/**
* Colours.
* @var array
*/
private static $_colours = array(
"#C3CC38",
"#C8B935",
"#CEA632",
"#D3932F",
"#D9802C",
"#FA6900",
"#DC9B57",
"#F78F01",
"#5AB56E",
"#6F8069",
"#C92200",
"#EB6C5C",
);
/**
* Build The Bar Gharph.
*
* @param array $params
* Assoc array of name/value pairs.
*
* @return object
* $chart object of open flash chart.
*/
public static function &barChart(&$params) {
$chart = NULL;
if (empty($params)) {
return $chart;
}
if (empty($params['multiValues'])) {
$params['multiValues'] = array($params['values']);
}
$values = CRM_Utils_Array::value('multiValues', $params);
if (!is_array($values) || empty($values)) {
return $chart;
}
// get the required data.
$chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
$xValues = $yValues = array();
$xValues = array_keys($values[0]);
$yValues = array_values($values[0]);
// set y axis parameters.
$yMin = 0;
// calculate max scale for graph.
$yMax = ceil(max($yValues));
if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
$yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
}
$ySteps = $yMax / 5;
$bars = array();
$config = CRM_Core_Config::singleton();
$symbol = $config->defaultCurrencySymbol;
foreach ($values as $barCount => $barVal) {
$bars[$barCount] = new bar_glass();
$yValues = array_values($barVal);
foreach ($yValues as &$yVal) {
// type casting is required for chart to render values correctly
$yVal = (double) $yVal;
}
$bars[$barCount]->set_values($yValues);
if ($barCount > 0) {
// FIXME: for bars > 2, we'll need to come out with other colors
$bars[$barCount]->colour('#BF3B69');
}
if ($barKey = CRM_Utils_Array::value($barCount, CRM_Utils_Array::value('barKeys', $params))) {
$bars[$barCount]->key($barKey, 12);
}
// call user define function to handle on click event.
if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
$bars[$barCount]->set_on_click($onClickFunName);
}
// get the currency to set in tooltip.
$tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
$bars[$barCount]->set_tooltip($tooltip);
}
// create x axis label obj.
$xLabels = new x_axis_labels();
// set_labels function requires xValues array of string or x_axis_label
// so type casting array values to string values
array_walk($xValues, function (&$value, $index) {
$value = (string) $value;
});
$xLabels->set_labels($xValues);
// set angle for labels.
if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
$xLabels->rotate($xLabelAngle);
}
// create x axis obj.
$xAxis = new x_axis();
$xAxis->set_labels($xLabels);
// create y axis and set range.
$yAxis = new y_axis();
$yAxis->set_range($yMin, $yMax, $ySteps);
// create chart title obj.
$title = new title($chartTitle);
// create chart.
$chart = new open_flash_chart();
// add x axis w/ labels to chart.
$chart->set_x_axis($xAxis);
// add y axis values to chart.
$chart->add_y_axis($yAxis);
// set title to chart.
$chart->set_title($title);
// add bar element to chart.
foreach ($bars as $bar) {
$chart->add_element($bar);
}
// add x axis legend.
if ($xName = CRM_Utils_Array::value('xname', $params)) {
$xLegend = new x_legend($xName);
$xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
$chart->set_x_legend($xLegend);
}
// add y axis legend.
if ($yName = CRM_Utils_Array::value('yname', $params)) {
$yLegend = new y_legend($yName);
$yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
$chart->set_y_legend($yLegend);
}
return $chart;
}
/**
* Build The Pie Gharph.
*
* @param array $params
* Assoc array of name/value pairs.
*
* @return object
* $chart object of open flash chart.
*/
public static function &pieChart(&$params) {
$chart = NULL;
if (empty($params)) {
return $chart;
}
$allValues = CRM_Utils_Array::value('values', $params);
if (!is_array($allValues) || empty($allValues)) {
return $chart;
}
// get the required data.
$values = array();
foreach ($allValues as $label => $value) {
$values[] = new pie_value((double) $value, $label);
}
$graphTitle = !empty($params['legend']) ? $params['legend'] : ts('Pie Chart');
// get the currency.
$config = CRM_Core_Config::singleton();
$symbol = $config->defaultCurrencySymbol;
$pie = new pie();
$pie->radius(100);
// call user define function to handle on click event.
if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
$pie->on_click($onClickFunName);
}
$pie->set_start_angle(35);
$pie->add_animation(new pie_fade());
$pie->add_animation(new pie_bounce(2));
// set the tooltip.
$tooltip = CRM_Utils_Array::value('tip', $params, "Amount is $symbol #val# of $symbol #total# <br>#percent#");
$pie->set_tooltip($tooltip);
// set colours.
$pie->set_colours(self::$_colours);
$pie->set_values($values);
// create chart.
$chart = new open_flash_chart();
// create chart title obj.
$title = new title($graphTitle);
$chart->set_title($title);
$chart->add_element($pie);
$chart->x_axis = NULL;
return $chart;
}
/**
* Build The 3-D Bar Gharph.
*
* @param array $params
* Assoc array of name/value pairs.
*
* @return object
* $chart object of open flash chart.
*/
public static function &bar_3dChart(&$params) {
$chart = NULL;
if (empty($params)) {
return $chart;
}
// $params['values'] should contains the values for each
// criteria defined in $params['criteria']
$values = CRM_Utils_Array::value('values', $params);
$criteria = CRM_Utils_Array::value('criteria', $params);
if (!is_array($values) || empty($values) || !is_array($criteria) || empty($criteria)) {
return $chart;
}
// get the required data.
$xReferences = $xValueLabels = $xValues = $yValues = array();
foreach ($values as $xVal => $yVal) {
if (!is_array($yVal) || empty($yVal)) {
continue;
}
$xValueLabels[] = (string) $xVal;
foreach ($criteria as $criteria) {
$xReferences[$criteria][$xVal] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
$yValues[] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
}
}
if (empty($xReferences)) {
return $chart;
}
// get the currency.
$config = CRM_Core_Config::singleton();
$symbol = $config->defaultCurrencySymbol;
// set the tooltip.
$tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
$count = 0;
foreach ($xReferences as $criteria => $values) {
$toolTipVal = $tooltip;
// for separate tooltip for each criteria
if (is_array($tooltip)) {
$toolTipVal = CRM_Utils_Array::value($criteria, $tooltip, "$symbol #val#");
}
// create bar_3d object
$xValues[$count] = new bar_3d();
// set colour pattel
$xValues[$count]->set_colour(self::$_colours[$count]);
// define colur pattel with bar criteria
$xValues[$count]->key((string) $criteria, 12);
// define bar chart values
$xValues[$count]->set_values(array_values($values));
// set tooltip
$xValues[$count]->set_tooltip($toolTipVal);
$count++;
}
$chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
// set y axis parameters.
$yMin = 0;
// calculate max scale for graph.
$yMax = ceil(max($yValues));
if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
$yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
}
// if max value of y-axis <= 0, then set default values
if ($yMax <= 0) {
$ySteps = 1;
$yMax = 5;
}
else {
$ySteps = $yMax / 5;
}
// create x axis label obj.
$xLabels = new x_axis_labels();
$xLabels->set_labels($xValueLabels);
// set angle for labels.
if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
$xLabels->rotate($xLabelAngle);
}
// create x axis obj.
$xAxis = new x_axis();
$xAxis->set_labels($xLabels);
// create y axis and set range.
$yAxis = new y_axis();
$yAxis->set_range($yMin, $yMax, $ySteps);
// create chart title obj.
$title = new title($chartTitle);
// create chart.
$chart = new open_flash_chart();
// add x axis w/ labels to chart.
$chart->set_x_axis($xAxis);
// add y axis values to chart.
$chart->add_y_axis($yAxis);
// set title to chart.
$chart->set_title($title);
foreach ($xValues as $bar) {
// add bar element to chart.
$chart->add_element($bar);
}
// add x axis legend.
if ($xName = CRM_Utils_Array::value('xname', $params)) {
$xLegend = new x_legend($xName);
$xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
$chart->set_x_legend($xLegend);
}
// add y axis legend.
if ($yName = CRM_Utils_Array::value('yname', $params)) {
$yLegend = new y_legend($yName);
$yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
$chart->set_y_legend($yLegend);
}
return $chart;
}
/**
* @param $rows
* @param $chart
* @param $interval
*
* @return array
*/
public static function chart($rows, $chart, $interval) {
$lcInterval = strtolower($interval);
$label = ucfirst($lcInterval);
$chartData = $dateKeys = array();
$intervalLabels = array(
'year' => ts('Yearly'),
'fiscalyear' => ts('Yearly (Fiscal)'),
'month' => ts('Monthly'),
'quarter' => ts('Quarterly'),
'week' => ts('Weekly'),
'yearweek' => ts('Weekly'),
);
switch ($lcInterval) {
case 'month':
case 'quarter':
case 'week':
case 'yearweek':
foreach ($rows['receive_date'] as $key => $val) {
list($year, $month) = explode('-', $val);
$dateKeys[] = substr($rows[$interval][$key], 0, 3) . ' of ' . $year;
}
$legend = $intervalLabels[$lcInterval];
break;
default:
foreach ($rows['receive_date'] as $key => $val) {
list($year, $month) = explode('-', $val);
$dateKeys[] = $year;
}
$legend = ts("%1", array(1 => $label));
if (!empty($intervalLabels[$lcInterval])) {
$legend = $intervalLabels[$lcInterval];
}
break;
}
if (!empty($dateKeys)) {
$graph = array();
if (!array_key_exists('multiValue', $rows)) {
$rows['multiValue'] = array($rows['value']);
}
foreach ($rows['multiValue'] as $key => $val) {
$graph[$key] = array_combine($dateKeys, $rows['multiValue'][$key]);
}
$chartData = array(
'legend' => "$legend " . CRM_Utils_Array::value('legend', $rows, ts('Contribution')) . ' ' . ts('Summary'),
'values' => $graph[0],
'multiValues' => $graph,
'barKeys' => CRM_Utils_Array::value('barKeys', $rows, array()),
);
}
// rotate the x labels.
$chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $rows, 0);
if (!empty($rows['tip'])) {
$chartData['tip'] = $rows['tip'];
}
// legend
$chartData['xname'] = CRM_Utils_Array::value('xname', $rows);
$chartData['yname'] = CRM_Utils_Array::value('yname', $rows);
// carry some chart params if pass.
foreach (array(
'xSize',
'ySize',
'divName',
) as $f) {
if (!empty($rows[$f])) {
$chartData[$f] = $rows[$f];
}
}
return self::buildChart($chartData, $chart);
}
/**
* @param $rows
* @param $chart
* @param $interval
* @param $chartInfo
*
* @return array
*/
public static function reportChart($rows, $chart, $interval, &$chartInfo) {
foreach ($interval as $key => $val) {
$graph[$val] = $rows['value'][$key];
}
$chartData = array(
'values' => $graph,
'legend' => $chartInfo['legend'],
'xname' => $chartInfo['xname'],
'yname' => $chartInfo['yname'],
);
// rotate the x labels.
$chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $chartInfo, 20);
if (!empty($chartInfo['tip'])) {
$chartData['tip'] = $chartInfo['tip'];
}
// carry some chart params if pass.
foreach (array(
'xSize',
'ySize',
'divName',
) as $f) {
if (!empty($rows[$f])) {
$chartData[$f] = $rows[$f];
}
}
return self::buildChart($chartData, $chart);
}
/**
* @param array $params
* @param $chart
*
* @return array
*/
public static function buildChart(&$params, $chart) {
$openFlashChart = array();
if ($chart && is_array($params) && !empty($params)) {
// build the chart objects.
$chartObj = CRM_Utils_OpenFlashChart::$chart($params);
$openFlashChart = array();
if ($chartObj) {
// calculate chart size.
$xSize = CRM_Utils_Array::value('xSize', $params, 400);
$ySize = CRM_Utils_Array::value('ySize', $params, 300);
if ($chart == 'barChart') {
$ySize = CRM_Utils_Array::value('ySize', $params, 250);
$xSize = 60 * count($params['values']);
// hack to show tooltip.
if ($xSize < 200) {
$xSize = (count($params['values']) > 1) ? 100 * count($params['values']) : 170;
}
elseif ($xSize > 600 && count($params['values']) > 1) {
$xSize = (count($params['values']) + 400 / count($params['values'])) * count($params['values']);
}
}
// generate unique id for this chart instance
$uniqueId = md5(uniqid(rand(), TRUE));
$openFlashChart["chart_{$uniqueId}"]['size'] = array('xSize' => $xSize, 'ySize' => $ySize);
$openFlashChart["chart_{$uniqueId}"]['object'] = $chartObj;
// assign chart data to template
$template = CRM_Core_Smarty::singleton();
$template->assign('uniqueId', $uniqueId);
$template->assign("openFlashChartData", json_encode($openFlashChart));
}
}
return $openFlashChart;
}
}

View file

@ -0,0 +1,172 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_OptionBag.
*/
class CRM_Utils_OptionBag implements ArrayAccess, IteratorAggregate, Countable {
protected $data;
/**
* @param array $data
*/
public function __construct($data = array()) {
$this->data = $data;
}
/**
* @return array
*/
public function getArray() {
return $this->data;
}
/**
* Retrieve a value from the bag.
*
* @param string $key
* @param string|null $type
* @param mixed $default
* @return mixed
* @throws API_Exception
*/
public function get($key, $type = NULL, $default = NULL) {
if (!array_key_exists($key, $this->data)) {
return $default;
}
if (!$type) {
return $this->data[$key];
}
$r = CRM_Utils_Type::validate($this->data[$key], $type);
if ($r !== NULL) {
return $r;
}
else {
throw new \API_Exception(ts("Could not find valid value for %1 (%2)", array(1 => $key, 2 => $type)));
}
}
/**
* @param $key
*
* @return bool
*/
public function has($key) {
return isset($this->data[$key]);
}
/**
* (PHP 5 &gt;= 5.0.0)
* Whether a offset exists
* @link http://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset
* An offset to check for.
*
* @return bool
* true on success or false on failure.
* The return value will be casted to boolean if non-boolean was returned.
*/
public function offsetExists($offset) {
return array_key_exists($offset, $this->data);
}
/**
* (PHP 5 &gt;= 5.0.0)
* Offset to retrieve
* @link http://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset
* The offset to retrieve.
*
* @return mixed
* Can return all value types.
*/
public function offsetGet($offset) {
return $this->data[$offset];
}
/**
* (PHP 5 &gt;= 5.0.0)
* Offset to set
* @link http://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset
* The offset to assign the value to.
*
* @param mixed $value
* The value to set.
*/
public function offsetSet($offset, $value) {
$this->data[$offset] = $value;
}
/**
* (PHP 5 &gt;= 5.0.0)
* Offset to unset
* @link http://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset
* The offset to unset.
*/
public function offsetUnset($offset) {
unset($this->data[$offset]);
}
/**
* (PHP 5 &gt;= 5.0.0)
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
*
* @return Traversable
* An instance of an object implementing Iterator or
* Traversable
*/
public function getIterator() {
return new ArrayIterator($this->data);
}
/**
* (PHP 5 &gt;= 5.1.0)
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
*
* @return int
* The custom count as an integer.
* The return value is cast to an integer.
*/
public function count() {
return count($this->data);
}
}

View file

@ -0,0 +1,232 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'TbsZip/tbszip.php';
/**
* Class CRM_Utils_PDF_Document.
*/
class CRM_Utils_PDF_Document {
public static $ooxmlMap = array(
'docx' => array(
'dataFile' => 'word/document.xml',
'startTag' => '<w:body>',
'pageBreak' => '<w:p><w:pPr><w:pStyle w:val="Normal"/><w:rPr></w:rPr></w:pPr><w:r><w:rPr></w:rPr></w:r><w:r><w:br w:type="page"/></w:r></w:p>',
'endTag' => '</w:body></w:document>',
),
'odt' => array(
'dataFile' => 'content.xml',
'startTag' => '<office:body>',
'pageBreak' => '<text:p text:style-name="Standard"></text:p>',
'endTag' => '</office:body></office:document-content>',
),
);
/**
* Convert html to a Doc file.
*
* @param array $pages
* List of HTML snippets.
* @param string $fileName
* The logical filename to return to client.
* Ex: "HelloWorld.odt".
* @param array|int $format
*/
public static function html2doc($pages, $fileName, $format = array()) {
if (is_array($format)) {
// PDF Page Format parameters passed in - merge with defaults
$format += CRM_Core_BAO_PdfFormat::getDefaultValues();
}
else {
// PDF Page Format ID passed in
$format = CRM_Core_BAO_PdfFormat::getById($format);
}
$paperSize = CRM_Core_BAO_PaperSize::getByName($format['paper_size']);
$metric = CRM_Core_BAO_PdfFormat::getValue('metric', $format);
$pageStyle = array(
'orientation' => CRM_Core_BAO_PdfFormat::getValue('orientation', $format),
'pageSizeW' => self::toTwip($paperSize['width'], $paperSize['metric']),
'pageSizeH' => self::toTwip($paperSize['height'], $paperSize['metric']),
'marginTop' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_top', $format), $metric),
'marginRight' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_right', $format), $metric),
'marginBottom' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_bottom', $format), $metric),
'marginLeft' => self::toTwip(CRM_Core_BAO_PdfFormat::getValue('margin_left', $format), $metric),
);
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$phpWord->getDocInfo()
->setCreator(CRM_Core_DAO::getFieldValue('CRM_Contact_BAO_Contact', CRM_Core_Session::getLoggedInContactID(), 'display_name'));
foreach ((array) $pages as $page => $html) {
$section = $phpWord->addSection($pageStyle + array('breakType' => 'nextPage'));
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $html);
}
self::printDoc($phpWord, $ext, $fileName);
}
/**
* @param object|string $phpWord
* @param string $ext
* File extension/type.
* Ex: docx, odt, html.
* @param string $fileName
* The logical filename to return to client.
* Ex: "HelloWorld.odt".
* Alternatively, a full path of a file to display. This seems sketchy.
* Ex: "/var/lib/data/HelloWorld.odt".
*/
public static function printDoc($phpWord, $ext, $fileName) {
$formats = array(
'docx' => 'Word2007',
'odt' => 'ODText',
'html' => 'HTML',
// todo
'pdf' => 'PDF',
);
if (realpath($fileName)) {
$phpWord = \PhpOffice\PhpWord\IOFactory::load($fileName, $formats[$ext]);
}
\PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(TRUE); //CRM-20015
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $formats[$ext]);
CRM_Utils_System::setHttpHeader('Content-Type', "application/$ext");
CRM_Utils_System::setHttpHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
$objWriter->save("php://output");
}
/**
* @param $value
* @param $metric
* @return int
*/
public static function toTwip($value, $metric) {
$point = CRM_Utils_PDF_Utils::convertMetric($value, $metric, 'pt');
return \PhpOffice\PhpWord\Shared\Converter::pointToTwip($point);
}
/**
* @param array $path docx/odt file path
* @param string $type File type
*
* @return array
* Return extracted content of document in HTML and document type
*/
public static function docReader($path, $type) {
$type = array_search($type, CRM_Core_SelectValues::documentApplicationType());
$fileType = ($type == 'docx') ? 'Word2007' : 'ODText';
$phpWord = \PhpOffice\PhpWord\IOFactory::load($path, $fileType);
$phpWordHTML = new \PhpOffice\PhpWord\Writer\HTML($phpWord);
// return the html content for tokenreplacment and eventually used for document download
return array($phpWordHTML->getWriterPart('Body')->write(), $type);
}
/**
* Extract content of docx/odt file
*
* @param string $filePath Document file path
* @param string $docType File type of document
*
* @return array
* [string, clsTbsZip]
*/
public static function unzipDoc($filePath, $docType) {
$dataFile = self::$ooxmlMap[$docType]['dataFile'];
$zip = new clsTbsZip();
$zip->Open($filePath);
$content = $zip->FileRead($dataFile);
return array($content, $zip);
}
/**
* Modify contents of docx/odt file(s) and later merged into one final document
*
* @param array $contents
* Content of formatted/token-replaced document.
* List of HTML snippets.
* @param string $fileName
* The logical filename to return to client.
* Ex: "HelloWorld.odt".
* @param string $docType
* Document type e.g. odt/docx
* @param clsTbsZip $zip
* Zip archive
* @param bool $returnFinalContent
* Return the content of file document as a string used in unit test
*
* @return string
*/
public static function printDocuments($contents, $fileName, $docType, $zip, $returnFinalContent = FALSE) {
$dataMap = self::$ooxmlMap[$docType];
$finalContent = $zip->FileRead($dataMap['dataFile']);
// token-replaced document contents of each contact will be merged into final document
foreach ($contents as $key => $content) {
if ($key == 0) {
$finalContent = $content;
continue;
}
// 1. fetch the start position of document body
// 2. later fetch only the body part starting from position $start
// 3. replace closing body tag with pageBreak
// 4. append the $content to the finalContent
$start = strpos($content, $dataMap['startTag']);
$content = substr($content, $start);
$content = str_replace($dataMap['startTag'], $dataMap['pageBreak'], $content);
$finalContent = str_replace($dataMap['endTag'], $content, $finalContent);
}
if ($returnFinalContent) {
return $finalContent;
}
// Replace the loaded document file content located at $filePath with $finaContent
$zip->FileReplace($dataMap['dataFile'], $finalContent, TBSZIP_STRING);
$zip->Flush(TBSZIP_DOWNLOAD, $fileName);
}
}

View file

@ -0,0 +1,290 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Class to print labels in Avery or custom formats
* functionality and smarts to the base PDF_Label.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_PDF_Label
*/
class CRM_Utils_PDF_Label extends TCPDF {
// make these properties public due to
// CRM-5880
// Default label format values
public $defaults;
// Current label format values
public $format;
// Name of format
public $formatName;
// Left margin of labels
public $marginLeft;
// Top margin of labels
public $marginTop;
// Horizontal space between 2 labels
public $xSpace;
// Vertical space between 2 labels
public $ySpace;
// Number of labels horizontally
public $xNumber;
// Number of labels vertically
public $yNumber;
// Width of label
public $width;
// Height of label
public $height;
// Line Height of label - used in event code
public $lineHeight = 0;
// Space between text and left edge of label
public $paddingLeft;
// Space between text and top edge of label
public $paddingTop;
// Character size (in points)
public $charSize;
// Metric used for all PDF doc measurements
public $metricDoc;
// Name of the font
public $fontName;
// 'B' bold, 'I' italic, 'BI' bold+italic
public $fontStyle;
// Paper size name
public $paperSize;
// Paper orientation
public $orientation;
// Paper dimensions array (w, h)
public $paper_dimensions;
// Counter for positioning labels
public $countX = 0;
// Counter for positioning labels
public $countY = 0;
/**
* Constructor.
*
* @param $format
* Either the name of a Label Format in the Option Value table.
* or an array of Label Format values.
* @param string|\Unit $unit Unit of measure for the PDF document
*/
public function __construct($format, $unit = 'mm') {
if (is_array($format)) {
// Custom format
$tFormat = $format;
}
else {
// Saved format
$tFormat = CRM_Core_BAO_LabelFormat::getByName($format);
}
$this->LabelSetFormat($tFormat, $unit);
parent::__construct($this->orientation, $this->metricDoc, $this->paper_dimensions);
$this->generatorMethod = NULL;
$this->SetFont($this->fontName, $this->fontStyle);
$this->SetFontSize($this->charSize);
$this->SetMargins(0, 0);
$this->SetAutoPageBreak(FALSE);
$this->setPrintHeader(FALSE);
$this->setPrintFooter(FALSE);
}
/**
* @param $objectinstance
* @param string $methodname
*/
public function SetGenerator($objectinstance, $methodname = 'generateLabel') {
$this->generatorMethod = $methodname;
$this->generatorObject = $objectinstance;
}
/**
* @param string $name
* @param bool $convert
*
* @return float|int|mixed
*/
public function getFormatValue($name, $convert = FALSE) {
if (isset($this->format[$name])) {
$value = $this->format[$name];
$metric = $this->format['metric'];
}
else {
$value = CRM_Utils_Array::value($name, $this->defaults);
$metric = $this->defaults['metric'];
}
if ($convert) {
$value = CRM_Utils_PDF_Utils::convertMetric($value, $metric, $this->metricDoc);
}
return $value;
}
/**
* initialize label format settings.
*
* @param $format
* @param $unit
*/
public function LabelSetFormat(&$format, $unit) {
$this->defaults = CRM_Core_BAO_LabelFormat::getDefaultValues();
$this->format = &$format;
$this->formatName = $this->getFormatValue('name');
$this->paperSize = $this->getFormatValue('paper-size');
$this->orientation = $this->getFormatValue('orientation');
$this->fontName = $this->getFormatValue('font-name');
$this->charSize = $this->getFormatValue('font-size');
$this->fontStyle = $this->getFormatValue('font-style');
$this->xNumber = $this->getFormatValue('NX');
$this->yNumber = $this->getFormatValue('NY');
$this->metricDoc = $unit;
$this->marginLeft = $this->getFormatValue('lMargin', TRUE);
$this->marginTop = $this->getFormatValue('tMargin', TRUE);
$this->xSpace = $this->getFormatValue('SpaceX', TRUE);
$this->ySpace = $this->getFormatValue('SpaceY', TRUE);
$this->width = $this->getFormatValue('width', TRUE);
$this->height = $this->getFormatValue('height', TRUE);
$this->paddingLeft = $this->getFormatValue('lPadding', TRUE);
$this->paddingTop = $this->getFormatValue('tPadding', TRUE);
$paperSize = CRM_Core_BAO_PaperSize::getByName($this->paperSize);
$w = CRM_Utils_PDF_Utils::convertMetric($paperSize['width'], $paperSize['metric'], $this->metricDoc);
$h = CRM_Utils_PDF_Utils::convertMetric($paperSize['height'], $paperSize['metric'], $this->metricDoc);
$this->paper_dimensions = array($w, $h);
}
/**
* Generate the pdf of one label (can be modified using SetGenerator)
*
* @param string $text
*/
public function generateLabel($text) {
$args = array(
'w' => $this->width,
'h' => 0,
'txt' => $text,
'border' => 0,
'align' => 'L',
'fill' => 0,
'ln' => 0,
'x' => '',
'y' => '',
'reseth' => TRUE,
'stretch' => 0,
'ishtml' => FALSE,
'autopadding' => FALSE,
'maxh' => $this->height,
);
CRM_Utils_Hook::alterMailingLabelParams($args);
if ($args['ishtml'] == TRUE) {
$this->writeHTMLCell($args['w'], $args['h'],
$args['x'], $args['y'],
$args['txt'], $args['border'],
$args['ln'], $args['fill'],
$args['reseth'], $args['align'],
$args['autopadding']
);
}
else {
$this->multiCell($args['w'], $args['h'],
$args['txt'], $args['border'],
$args['align'], $args['fill'],
$args['ln'], $args['x'],
$args['y'], $args['reseth'],
$args['stretch'], $args['ishtml'],
$args['autopadding'], $args['maxh']
);
}
}
/**
* Print a label.
*
* @param $texte
*/
public function AddPdfLabel($texte) {
if ($this->countX == $this->xNumber) {
// Page full, we start a new one
$this->AddPage();
$this->countX = 0;
$this->countY = 0;
}
$posX = $this->marginLeft + ($this->countX * ($this->width + $this->xSpace));
$posY = $this->marginTop + ($this->countY * ($this->height + $this->ySpace));
$this->SetXY($posX + $this->paddingLeft, $posY + $this->paddingTop);
if ($this->generatorMethod) {
call_user_func_array(array($this->generatorObject, $this->generatorMethod), array($texte));
}
else {
$this->generateLabel($texte);
}
$this->countY++;
if ($this->countY == $this->yNumber) {
// End of column reached, we start a new one
$this->countX++;
$this->countY = 0;
}
}
/**
* Get the available font names.
*
* @return array
*/
public function getFontNames() {
// Define labels for TCPDF core fonts
$fontLabel = array(
'courier' => ts('Courier'),
'helvetica' => ts('Helvetica'),
'times' => ts('Times New Roman'),
'dejavusans' => ts('Deja Vu Sans (UTF-8)'),
);
// Check to see if we have any additional fonts to add. You can specify more fonts in
// civicrm.settings.php via: $config['CiviCRM Preferences']['additional_fonts']
// CRM-13307
$additionalFonts = Civi::settings()->get('additional_fonts');
if (is_array($additionalFonts)) {
$fontLabel = array_merge($fontLabel, $additionalFonts);
}
$tcpdfFonts = $this->fontlist;
foreach ($tcpdfFonts as $fontName) {
if (array_key_exists($fontName, $fontLabel)) {
$list[$fontName] = $fontLabel[$fontName];
}
}
return $list;
}
}

View file

@ -0,0 +1,320 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
use Dompdf\Dompdf;
use Dompdf\Options;
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_PDF_Utils {
/**
* @param array $text
* List of HTML snippets.
* @param string $fileName
* The logical filename to display.
* Ex: "HelloWorld.pdf".
* @param bool $output
* FALSE to display PDF. TRUE to return as string.
* @param null $pdfFormat
* Unclear. Possibly PdfFormat or formValues.
*
* @return string|void
*/
public static function html2pdf(&$text, $fileName = 'civicrm.pdf', $output = FALSE, $pdfFormat = NULL) {
if (is_array($text)) {
$pages = &$text;
}
else {
$pages = array($text);
}
// Get PDF Page Format
$format = CRM_Core_BAO_PdfFormat::getDefaultValues();
if (is_array($pdfFormat)) {
// PDF Page Format parameters passed in
$format = array_merge($format, $pdfFormat);
}
else {
// PDF Page Format ID passed in
$format = CRM_Core_BAO_PdfFormat::getById($pdfFormat);
}
$paperSize = CRM_Core_BAO_PaperSize::getByName($format['paper_size']);
$paper_width = self::convertMetric($paperSize['width'], $paperSize['metric'], 'pt');
$paper_height = self::convertMetric($paperSize['height'], $paperSize['metric'], 'pt');
// dompdf requires dimensions in points
$paper_size = array(0, 0, $paper_width, $paper_height);
$orientation = CRM_Core_BAO_PdfFormat::getValue('orientation', $format);
$metric = CRM_Core_BAO_PdfFormat::getValue('metric', $format);
$t = CRM_Core_BAO_PdfFormat::getValue('margin_top', $format);
$r = CRM_Core_BAO_PdfFormat::getValue('margin_right', $format);
$b = CRM_Core_BAO_PdfFormat::getValue('margin_bottom', $format);
$l = CRM_Core_BAO_PdfFormat::getValue('margin_left', $format);
$stationery_path_partial = CRM_Core_BAO_PdfFormat::getValue('stationery', $format);
$stationery_path = NULL;
if (strlen($stationery_path_partial)) {
$doc_root = $_SERVER['DOCUMENT_ROOT'];
$stationery_path = $doc_root . "/" . $stationery_path_partial;
}
$margins = array($metric, $t, $r, $b, $l);
$config = CRM_Core_Config::singleton();
// Add a special region for the HTML header of PDF files:
$pdfHeaderRegion = CRM_Core_Region::instance('export-document-header', FALSE);
$htmlHeader = ($pdfHeaderRegion) ? $pdfHeaderRegion->render('', FALSE) : '';
$html = "
<html>
<head>
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
<style>@page { margin: {$t}{$metric} {$r}{$metric} {$b}{$metric} {$l}{$metric}; }</style>
<style type=\"text/css\">@import url({$config->userFrameworkResourceURL}css/print.css);</style>
{$htmlHeader}
</head>
<body>
<div id=\"crm-container\">\n";
// Strip <html>, <header>, and <body> tags from each page
$htmlElementstoStrip = array(
'@<head[^>]*?>.*?</head>@siu',
'@<script[^>]*?>.*?</script>@siu',
'@<body>@siu',
'@</body>@siu',
'@<html[^>]*?>@siu',
'@</html>@siu',
'@<!DOCTYPE[^>]*?>@siu',
);
$htmlElementsInstead = array('', '', '', '', '', '');
foreach ($pages as & $page) {
$page = preg_replace($htmlElementstoStrip,
$htmlElementsInstead,
$page
);
}
// Glue the pages together
$html .= implode("\n<div style=\"page-break-after: always\"></div>\n", $pages);
$html .= "
</div>
</body>
</html>";
if ($config->wkhtmltopdfPath) {
return self::_html2pdf_wkhtmltopdf($paper_size, $orientation, $margins, $html, $output, $fileName);
}
else {
return self::_html2pdf_dompdf($paper_size, $orientation, $html, $output, $fileName);
//return self::_html2pdf_tcpdf($paper_size, $orientation, $margins, $html, $output, $fileName, $stationery_path);
}
}
/**
* Convert html to tcpdf.
*
* @param $paper_size
* @param $orientation
* @param $margins
* @param $html
* @param $output
* @param $fileName
* @param $stationery_path
*/
public static function _html2pdf_tcpdf($paper_size, $orientation, $margins, $html, $output, $fileName, $stationery_path) {
// Documentation on the TCPDF library can be found at: http://www.tcpdf.org
// This function also uses the FPDI library documented at: http://www.setasign.com/products/fpdi/about/
// Syntax borrowed from https://github.com/jake-mw/CDNTaxReceipts/blob/master/cdntaxreceipts.functions.inc
require_once 'tcpdf/tcpdf.php';
require_once 'FPDI/fpdi.php'; // This library is only in the 'packages' area as of version 4.5
$paper_size_arr = array($paper_size[2], $paper_size[3]);
$pdf = new TCPDF($orientation, 'pt', $paper_size_arr);
$pdf->Open();
if (is_readable($stationery_path)) {
$pdf->SetStationery($stationery_path);
}
$pdf->SetAuthor('');
$pdf->SetKeywords('CiviCRM.org');
$pdf->setPageUnit($margins[0]);
$pdf->SetMargins($margins[4], $margins[1], $margins[2], TRUE);
$pdf->setJPEGQuality('100');
$pdf->SetAutoPageBreak(TRUE, $margins[3]);
$pdf->AddPage();
$ln = TRUE;
$fill = FALSE;
$reset_parm = FALSE;
$cell = FALSE;
$align = '';
// output the HTML content
$pdf->writeHTML($html, $ln, $fill, $reset_parm, $cell, $align);
// reset pointer to the last page
$pdf->lastPage();
// close and output the PDF
$pdf->Close();
$pdf_file = 'CiviLetter' . '.pdf';
$pdf->Output($pdf_file, 'D');
CRM_Utils_System::civiExit(1);
}
/**
* @param $paper_size
* @param $orientation
* @param $html
* @param $output
* @param string $fileName
*
* @return string
*/
public static function _html2pdf_dompdf($paper_size, $orientation, $html, $output, $fileName) {
// CRM-12165 - Remote file support required for image handling.
$options = new Options();
$options->set('isRemoteEnabled', TRUE);
$dompdf = new DOMPDF($options);
$dompdf->set_paper($paper_size, $orientation);
$dompdf->load_html($html);
$dompdf->render();
if ($output) {
return $dompdf->output();
}
else {
// CRM-19183 remove .pdf extension from filename
$fileName = basename($fileName, ".pdf");
$dompdf->stream($fileName);
}
}
/**
* @param $paper_size
* @param $orientation
* @param $margins
* @param $html
* @param $output
* @param string $fileName
*/
public static function _html2pdf_wkhtmltopdf($paper_size, $orientation, $margins, $html, $output, $fileName) {
require_once 'packages/snappy/src/autoload.php';
$config = CRM_Core_Config::singleton();
$snappy = new Knp\Snappy\Pdf($config->wkhtmltopdfPath);
$snappy->setOption("page-width", $paper_size[2] . "pt");
$snappy->setOption("page-height", $paper_size[3] . "pt");
$snappy->setOption("orientation", $orientation);
$snappy->setOption("margin-top", $margins[1] . $margins[0]);
$snappy->setOption("margin-right", $margins[2] . $margins[0]);
$snappy->setOption("margin-bottom", $margins[3] . $margins[0]);
$snappy->setOption("margin-left", $margins[4] . $margins[0]);
$pdf = $snappy->getOutputFromHtml($html);
if ($output) {
return $pdf;
}
else {
CRM_Utils_System::setHttpHeader('Content-Type', 'application/pdf');
CRM_Utils_System::setHttpHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
echo $pdf;
}
}
/**
* convert value from one metric to another.
*
* @param $value
* @param $from
* @param $to
* @param null $precision
*
* @return float|int
*/
public static function convertMetric($value, $from, $to, $precision = NULL) {
switch ($from . $to) {
case 'incm':
$value *= 2.54;
break;
case 'inmm':
$value *= 25.4;
break;
case 'inpt':
$value *= 72;
break;
case 'cmin':
$value /= 2.54;
break;
case 'cmmm':
$value *= 10;
break;
case 'cmpt':
$value *= 72 / 2.54;
break;
case 'mmin':
$value /= 25.4;
break;
case 'mmcm':
$value /= 10;
break;
case 'mmpt':
$value *= 72 / 25.4;
break;
case 'ptin':
$value /= 72;
break;
case 'ptcm':
$value *= 2.54 / 72;
break;
case 'ptmm':
$value *= 25.4 / 72;
break;
}
if (!is_null($precision)) {
$value = round($value, $precision);
}
return $value;
}
}

View file

@ -0,0 +1,338 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* This class extends the PEAR pager object by substituting standard default pager arguments
* We also extract the pageId from either the GET variables or the POST variable (since we
* use a POST to jump to a specific page). At some point we should evaluate if we want
* to use Pager_Jumping instead. We've changed the format to allow navigation by jumping
* to a page and also First, Prev CURRENT Next Last
*/
require_once 'Pager/Sliding.php';
/**
* Class CRM_Utils_Pager
*/
class CRM_Utils_Pager extends Pager_Sliding {
/**
* Constants for static parameters of the pager
*/
const ROWCOUNT = 50, PAGE_ID = 'crmPID', PAGE_ID_TOP = 'crmPID', PAGE_ID_BOTTOM = 'crmPID_B', PAGE_ROWCOUNT = 'crmRowCount';
/**
* The output of the pager. This is a name/value array with various keys
* that an application could use to display the pager
*
* @var array
*/
public $_response;
/**
* The pager constructor. Takes a few values, and then assigns a lot of defaults
* to the PEAR pager class
* We have embedded some html in this class. Need to figure out how to export this
* to the top level at some point in time
*
* @param array $params
*
* @return \CRM_Utils_Pager the newly created and initialized pager object
*/
public function __construct($params) {
if ($params['status'] === NULL) {
$params['status'] = ts('Contacts %%StatusMessage%%');
}
$this->initialize($params);
parent::__construct($params);
list($offset, $limit) = $this->getOffsetAndRowCount();
$start = $offset + 1;
$end = $offset + $limit;
if ($end > $params['total']) {
$end = $params['total'];
}
if ($params['total'] == 0) {
$statusMessage = '';
}
else {
$statusMessage = ts('%1 - %2 of %3', array(1 => $start, 2 => $end, 3 => $params['total']));
}
$params['status'] = str_replace('%%StatusMessage%%', $statusMessage, $params['status']);
$this->_response = array(
'first' => $this->getFirstPageLink(),
'back' => $this->getBackPageLink(),
'next' => $this->getNextPageLink(),
'last' => $this->getLastPageLink(),
'currentPage' => $this->getCurrentPageID(),
'numPages' => $this->numPages(),
'csvString' => CRM_Utils_Array::value('csvString', $params),
'status' => CRM_Utils_Array::value('status', $params),
'buttonTop' => CRM_Utils_Array::value('buttonTop', $params),
'buttonBottom' => CRM_Utils_Array::value('buttonBottom', $params),
'currentLocation' => $this->getCurrentLocation(),
);
/**
* A page cannot have two variables with the same form name. Hence in the
* pager display, we have a form submission at the top with the normal
* page variable, but a different form element for one at the bottom.
*/
$this->_response['titleTop'] = ts('Page %1 of %2', array(
1 => '<input size="2" maxlength="4" name="' . self::PAGE_ID . '" type="text" value="' . $this->_response['currentPage'] . '" />',
2 => $this->_response['numPages'],
));
$this->_response['titleBottom'] = ts('Page %1 of %2', array(
1 => '<input size="2" maxlength="4" name="' . self::PAGE_ID_BOTTOM . '" type="text" value="' . $this->_response['currentPage'] . '" />',
2 => $this->_response['numPages'],
));
}
/**
* Helper function to assign remaining pager options as good default
* values.
*
* @param array $params
* The set of options needed to initialize the parent constructor.
*
* @return array
*/
public function initialize(&$params) {
// set the mode for the pager to Sliding
$params['mode'] = 'Sliding';
// also set the urlVar to be a crm specific get variable.
$params['urlVar'] = self::PAGE_ID;
// set this to a small value, since we dont use this functionality
$params['delta'] = 1;
$params['totalItems'] = $params['total'];
$params['append'] = TRUE;
$params['separator'] = '';
$params['spacesBeforeSeparator'] = 1;
$params['spacesAfterSeparator'] = 1;
$params['extraVars'] = array('force' => 1);
$params['excludeVars'] = array('reset', 'snippet', 'section');
// set previous and next text labels
$params['prevImg'] = ' ' . ts('&lt; Previous');
$params['nextImg'] = ts('Next &gt;') . ' ';
// set first and last text fragments
$params['firstPagePre'] = '';
$params['firstPageText'] = ' ' . ts('&lt;&lt; First');
$params['firstPagePost'] = '';
$params['lastPagePre'] = '';
$params['lastPageText'] = ts('Last &gt;&gt;') . ' ';
$params['lastPagePost'] = '';
if (isset($params['pageID'])) {
$params['currentPage'] = $this->getPageID($params['pageID'], $params);
}
$params['perPage'] = $this->getPageRowCount($params['rowCount']);
return $params;
}
/**
* Figure out the current page number based on value of
* GET / POST variables. Hierarchy rules are followed,
* POST over-rides a GET, a POST at the top overrides
* a POST at the bottom (of the page)
*
* @param int $defaultPageId
* DefaultPageId current pageId.
*
* @param array $params
*
* @return int
* new pageId to display to the user
*/
public function getPageID($defaultPageId = 1, &$params) {
// POST has higher priority than GET vars
// else if a value is set that has higher priority and finally the GET var
$currentPage = $defaultPageId;
if (!empty($_POST)) {
if (isset($_POST[CRM_Utils_Array::value('buttonTop', $params)]) && isset($_POST[self::PAGE_ID])) {
$currentPage = max((int ) @$_POST[self::PAGE_ID], 1);
}
elseif (isset($_POST[$params['buttonBottom']]) && isset($_POST[self::PAGE_ID_BOTTOM])) {
$currentPage = max((int ) @$_POST[self::PAGE_ID_BOTTOM], 1);
}
elseif (isset($_POST[self::PAGE_ID])) {
$currentPage = max((int ) @$_POST[self::PAGE_ID], 1);
}
elseif (isset($_POST[self::PAGE_ID_BOTTOM])) {
$currentPage = max((int ) @$_POST[self::PAGE_ID_BOTTOM]);
}
}
elseif (isset($_GET[self::PAGE_ID])) {
$currentPage = max((int ) @$_GET[self::PAGE_ID], 1);
}
return $currentPage;
}
/**
* Get the number of rows to display from either a GET / POST variable
*
* @param int $defaultPageRowCount
* The default value if not set.
*
* @return int
* the rowCount value to use
*/
public function getPageRowCount($defaultPageRowCount = self::ROWCOUNT) {
// POST has higher priority than GET vars
if (isset($_POST[self::PAGE_ROWCOUNT])) {
$rowCount = max((int ) @$_POST[self::PAGE_ROWCOUNT], 1);
}
elseif (isset($_GET[self::PAGE_ROWCOUNT])) {
$rowCount = max((int ) @$_GET[self::PAGE_ROWCOUNT], 1);
}
else {
$rowCount = $defaultPageRowCount;
}
return $rowCount;
}
/**
* Use the pager class to get the pageId and Offset.
*
* @return array
* an array of the pageID and offset
*/
public function getOffsetAndRowCount() {
$pageId = $this->getCurrentPageID();
if (!$pageId) {
$pageId = 1;
}
$offset = ($pageId - 1) * $this->_perPage;
return array($offset, $this->_perPage);
}
/**
* @return string
*/
public function getCurrentLocation() {
$config = CRM_Core_Config::singleton();
$path = CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET);
return CRM_Utils_System::url($path, CRM_Utils_System::getLinksUrl(self::PAGE_ID, FALSE, TRUE), FALSE, NULL, FALSE) . $this->getCurrentPageID();
}
/**
* @return string
*/
public function getFirstPageLink() {
if ($this->isFirstPage()) {
return '';
}
$href = $this->makeURL(self::PAGE_ID, 1);
return $this->formatLink($href, str_replace('%d', 1, $this->_altFirst), $this->_firstPagePre . $this->_firstPageText . $this->_firstPagePost) .
$this->_spacesBefore . $this->_spacesAfter;
}
/**
* @return string
*/
public function getLastPageLink() {
if ($this->isLastPage()) {
return '';
}
$href = $this->makeURL(self::PAGE_ID, $this->_totalPages);
return $this->formatLink($href, str_replace('%d', $this->_totalPages, $this->_altLast), $this->_lastPagePre . $this->_lastPageText . $this->_lastPagePost);
}
/**
* @return string
*/
public function getBackPageLink() {
if ($this->_currentPage > 1) {
$href = $this->makeURL(self::PAGE_ID, $this->getPreviousPageID());
return $this->formatLink($href, $this->_altPrev, $this->_prevImg) . $this->_spacesBefore . $this->_spacesAfter;
}
return '';
}
/**
* @return string
*/
public function getNextPageLink() {
if ($this->_currentPage < $this->_totalPages) {
$href = $this->makeURL(self::PAGE_ID, $this->getNextPageID());
return $this->_spacesAfter .
$this->formatLink($href, $this->_altNext, $this->_nextImg) .
$this->_spacesBefore . $this->_spacesAfter;
}
return '';
}
/**
* Build a url for pager links.
*
* @param string $key
* @param string $value
*
* @return string
*/
public function makeURL($key, $value) {
$href = CRM_Utils_System::makeURL($key, TRUE);
// CRM-12212 Remove alpha sort param
if (strpos($href, '&amp;sortByCharacter=')) {
$href = preg_replace('#(.*)\&amp;sortByCharacter=[^&]*(.*)#', '\1\2', $href);
}
return $href . $value;
}
/**
* Output the html pager link.
* @param string $href
* @param string $title
* @param string $image
* @return string
*/
private function formatLink($href, $title, $image) {
return sprintf('<a class="crm-pager-link action-item crm-hover-button" href="%s" title="%s">%s</a>', $href, $title, $image);
}
}

View file

@ -0,0 +1,200 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* This class is for displaying alphabetical bar
*/
class CRM_Utils_PagerAToZ {
/**
* Returns the alphabetic array for sorting by character.
*
* @param array $query
* The query object.
* @param string $sortByCharacter
* The character that we are potentially sorting on.
*
* @param bool $isDAO
*
* @return string
* The html formatted string
*/
public static function getAToZBar(&$query, $sortByCharacter, $isDAO = FALSE) {
$AToZBar = self::createLinks($query, $sortByCharacter, $isDAO);
return $AToZBar;
}
/**
* Return the all the static characters.
*
* @return array
* is an array of static characters
*/
public static function getStaticCharacters() {
$staticAlphabets = array(
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
);
return $staticAlphabets;
}
/**
* Return the all the dynamic characters.
*
* @param $query
* @param $isDAO
*
* @return array
* is an array of dynamic characters
*/
public static function getDynamicCharacters(&$query, $isDAO) {
if ($isDAO) {
$result = $query;
}
else {
$result = $query->alphabetQuery();
}
if (!$result) {
return NULL;
}
$dynamicAlphabets = array();
while ($result->fetch()) {
$dynamicAlphabets[] = $result->sort_name;
}
return $dynamicAlphabets;
}
/**
* Create the links.
*
* @param array $query
* The form values for search.
* @param string $sortByCharacter
* The character that we are potentially sorting on.
*
* @param $isDAO
*
* @return array
* with links
*/
public static function createLinks(&$query, $sortByCharacter, $isDAO) {
$AToZBar = self::getStaticCharacters();
$dynamicAlphabets = self::getDynamicCharacters($query, $isDAO);
if (!$dynamicAlphabets) {
return NULL;
}
$AToZBar = array_merge($AToZBar, $dynamicAlphabets);
sort($AToZBar, SORT_STRING);
$AToZBar = array_unique($AToZBar);
// get the current path
$path = CRM_Utils_System::currentPath();
$qfKey = NULL;
if (isset($query->_formValues)) {
$qfKey = CRM_Utils_Array::value('qfKey', $query->_formValues);
}
if (empty($qfKey)) {
// CRM-20943 Can only pass variables by reference and also cannot use $this so using $empty setting to NULL which is default.
$emptyVariable = NULL;
$qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $emptyVariable, FALSE, NULL, $_REQUEST);
}
$aToZBar = array();
foreach ($AToZBar as $key => $link) {
if ($link === NULL) {
continue;
}
$element = array();
if (in_array($link, $dynamicAlphabets)) {
$klass = '';
if ($link == $sortByCharacter) {
$element['class'] = "active";
$klass = 'class="active"';
}
$url = CRM_Utils_System::url($path, "force=1&qfKey=$qfKey&sortByCharacter=");
// we do it this way since we want the url to be encoded but not the link character
// since that seems to mess up drupal utf-8 encoding etc
$url .= urlencode($link);
$element['item'] = sprintf('<a href="%s" %s>%s</a>',
$url,
$klass,
$link
);
}
else {
$element['item'] = $link;
}
$aToZBar[] = $element;
}
$url = sprintf(
'<a href="%s">%s</a>',
CRM_Utils_System::url(
$path,
"force=1&qfKey=$qfKey&sortByCharacter=all"
),
ts('All')
);
$aToZBar[] = array('item' => $url);
return $aToZBar;
}
}

View file

@ -0,0 +1,175 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Utilities for manipulating/inspecting CRM_*_PseudoConstant classes.
*/
class CRM_Utils_PseudoConstant {
/**
* CiviCRM pseudoconstant classes for wrapper functions.
*/
private static $constantClasses = array(
'CRM_Core_PseudoConstant',
'CRM_Event_PseudoConstant',
'CRM_Contribute_PseudoConstant',
'CRM_Member_PseudoConstant',
);
/**
* @var array
* ($name => $className)
*/
private static $constants = NULL;
/**
* Get constant.
*
* Wrapper for Pseudoconstant methods. We use this so the calling function
* doesn't need to know which class the Pseudoconstant is on
* (some are on the Contribute_Pseudoconstant Class etc
*
*
* @param string $constant
*
* @return array
* array reference of all relevant constant
*/
public static function getConstant($constant) {
$class = self::findConstantClass($constant);
if ($class) {
return $class::$constant();
}
}
/**
* Flush constant.
*
* Wrapper for Pseudoconstant methods. We use this so the calling function
* doesn't need to know which class the Pseudoconstant is on
* (some are on the Contribute_Pseudoconsant Class etc
*
*
* @param $constant
*
* @return array
* array reference of all relevant constant
*/
public static function flushConstant($constant) {
$class = self::findConstantClass($constant);
if ($class) {
$class::flush(lcfirst($constant));
//@todo the rule is api functions should only be called from within the api - we
// should move this function to a Core class
$name = _civicrm_api_get_entity_name_from_camel($constant);
CRM_Core_OptionGroup::flush($name);
return TRUE;
}
}
/**
* Determine where a constant lives.
*
* If there's a full, preloaded map, use it. Otherwise, use search
* class space.
*
* @param string $constant
* Constant-name.
*
* @return string|NULL
* class-name
*/
public static function findConstantClass($constant) {
if (self::$constants !== NULL && isset(self::$constants[$constant])) {
return self::$constants[$constant];
}
foreach (self::$constantClasses as $class) {
if (method_exists($class, lcfirst($constant))) {
return $class;
}
}
return NULL;
}
/**
* Scan for a list of pseudo-constants. A pseudo-constant is recognized by listing
* any static properties which have corresponding static methods.
*
* This may be inefficient and should generally be avoided.
*
* @return array
* Array of string, constant names
*/
public static function findConstants() {
if (self::$constants === NULL) {
self::$constants = array();
foreach (self::$constantClasses as $class) {
foreach (self::findConstantsByClass($class) as $constant) {
self::$constants[$constant] = $class;
}
}
}
return array_keys(self::$constants);
}
/**
* Scan for a list of pseudo-constants. A pseudo-constant is recognized by listing
* any static properties which have corresponding static methods.
*
* This may be inefficient and should generally be avoided.
*
* @param $class
*
* @return array
* Array of string, constant names
*/
public static function findConstantsByClass($class) {
$clazz = new ReflectionClass($class);
$classConstants = array_intersect(
CRM_Utils_Array::collect('name', $clazz->getProperties(ReflectionProperty::IS_STATIC)),
CRM_Utils_Array::collect('name', $clazz->getMethods(ReflectionMethod::IS_STATIC))
);
return $classConstants;
}
/**
* Flush all caches related to pseudo-constants.
*
* This may be inefficient and should generally be avoided.
*/
public static function flushAll() {
foreach (self::findConstants() as $constant) {
self::flushConstant($constant);
}
CRM_Core_PseudoConstant::flush();
}
}

View file

@ -0,0 +1,511 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class CRM_Utils_QueryFormatter
*
* This class is a bad idea. It exists for the unholy reason that a single installation
* may have up to three query engines (MySQL LIKE, MySQL FTS, Solr) processing the same
* query-text. It labors* to take the user's search expression and provide similar search
* semantics in different contexts. It is unknown whether this labor will be fruitful
* or in vain.
*/
class CRM_Utils_QueryFormatter {
/**
* Generate queries using SQL LIKE expressions.
*/
const LANG_SQL_LIKE = 'like';
/**
* Generate queries using MySQL FTS expressions.
*/
const LANG_SQL_FTS = 'fts';
/**
* Generate queries using MySQL's boolean FTS expressions.
*/
const LANG_SQL_FTSBOOL = 'ftsbool';
/**
* Generate queries using Solr expressions.
*/
const LANG_SOLR = 'solr';
/**
* Attempt to leave the text as-is.
*/
const MODE_NONE = 'simple';
/**
* Attempt to treat the input text as a phrase
*/
const MODE_PHRASE = 'phrase';
/**
* Attempt to treat the input text as a phrase with
* wildcards on each end.
*/
const MODE_WILDPHRASE = 'wildphrase';
/**
* Attempt to treat individual word as if it
* had wildcards at the start and end.
*/
const MODE_WILDWORDS = 'wildwords';
/**
* Attempt to treat individual word as if it
* had a wildcard at the end.
*/
const MODE_WILDWORDS_SUFFIX = 'wildwords-suffix';
static protected $singleton;
/**
* @param bool $fresh
* @return CRM_Utils_QueryFormatter
*/
public static function singleton($fresh = FALSE) {
if ($fresh || self::$singleton === NULL) {
$mode = Civi::settings()->get('fts_query_mode');
self::$singleton = new CRM_Utils_QueryFormatter($mode);
}
return self::$singleton;
}
/**
* @var string
* eg MODE_NONE
*/
protected $mode;
/**
* @param string $mode
* Eg MODE_NONE.
*/
public function __construct($mode) {
$this->mode = $mode;
}
/**
* @param mixed $mode
*/
public function setMode($mode) {
$this->mode = $mode;
}
/**
* @return mixed
*/
public function getMode() {
return $this->mode;
}
/**
* @param string $text
* @param string $language
* Eg LANG_SQL_LIKE, LANG_SQL_FTS, LANG_SOLR.
* @throws CRM_Core_Exception
* @return string
*/
public function format($text, $language) {
$text = trim($text);
switch ($language) {
case self::LANG_SOLR:
case self::LANG_SQL_FTS:
$text = $this->_formatFts($text, $this->mode);
break;
case self::LANG_SQL_FTSBOOL:
$text = $this->_formatFtsBool($text, $this->mode);
break;
case self::LANG_SQL_LIKE:
$text = $this->_formatLike($text, $this->mode);
break;
default:
$text = NULL;
}
if ($text === NULL) {
throw new CRM_Core_Exception("Unrecognized combination: language=[{$language}] mode=[{$this->mode}]");
}
return $text;
}
/**
* Create a SQL WHERE expression for matching against a list of
* text columns.
*
* @param string $table
* Eg "civicrm_note" or "civicrm_note mynote".
* @param array|string $columns
* List of columns to search against.
* Eg "first_name" or "activity_details".
* @param string $queryText
* @return string
* SQL, eg "MATCH (col1) AGAINST (queryText)" or "col1 LIKE '%queryText%'"
*/
public function formatSql($table, $columns, $queryText) {
if ($queryText === '*' || $queryText === '%' || empty($queryText)) {
return '(1)';
}
$strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
if (strpos($table, ' ') === FALSE) {
$tableName = $tableAlias = $table;
}
else {
list ($tableName, $tableAlias) = explode(' ', $table);
}
if (is_scalar($columns)) {
$columns = array($columns);
}
$clauses = array();
if (CRM_Core_InnoDBIndexer::singleton()
->hasDeclaredIndex($tableName, $columns)
) {
$formattedQuery = $this->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL);
$prefixedFieldNames = array();
foreach ($columns as $fieldName) {
$prefixedFieldNames[] = "$tableAlias.$fieldName";
}
$clauses[] = sprintf("MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE)",
implode(',', $prefixedFieldNames),
$strtolower(CRM_Core_DAO::escapeString($formattedQuery))
);
}
else {
//CRM_Core_Session::setStatus(ts('Cannot use FTS for %1 (%2)', array(
// 1 => $table,
// 2 => implode(', ', $fullTextFields),
//)));
$formattedQuery = $this->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_LIKE);
$escapedText = $strtolower(CRM_Core_DAO::escapeString($formattedQuery));
foreach ($columns as $fieldName) {
$clauses[] = "$tableAlias.$fieldName LIKE '{$escapedText}'";
}
}
return implode(' OR ', $clauses);
}
/**
* Format Fts.
*
* @param string $text
* @param $mode
*
* @return mixed
*/
protected function _formatFts($text, $mode) {
$result = NULL;
// normalize user-inputted wildcards
$text = str_replace('%', '*', $text);
if (empty($text)) {
$result = '*';
}
elseif (strpos($text, '*') !== FALSE) {
// if user supplies their own wildcards, then don't do any sophisticated changes
$result = $text;
}
else {
switch ($mode) {
case self::MODE_NONE:
$result = $text;
break;
case self::MODE_PHRASE:
$result = '"' . $text . '"';
break;
case self::MODE_WILDPHRASE:
$result = '"*' . $text . '*"';
break;
case self::MODE_WILDWORDS:
$result = $this->mapWords($text, '*word*');
break;
case self::MODE_WILDWORDS_SUFFIX:
$result = $this->mapWords($text, 'word*');
break;
default:
$result = NULL;
}
}
return $this->dedupeWildcards($result, '%');
}
/**
* Format FTS.
*
* @param string $text
* @param $mode
*
* @return mixed
*/
protected function _formatFtsBool($text, $mode) {
$result = NULL;
$operators = array('+', '-', '~', '(', ')');
$wildCards = array('@', '%', '*');
$expression = preg_quote(implode('', array_merge($operators, $wildCards)), '/');
//Return if searched string ends with an unsupported operator.
//Or if the string contains an invalid joint occurrence of operators.
foreach ($operators as $val) {
if ($text == '@' || CRM_Utils_String::endsWith($text, $val) || preg_match("/[{$expression}]{2,}/", $text)) {
$csid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', 'CRM_Contact_Form_Search_Custom_FullText', 'value', 'name');
$url = CRM_Utils_System::url("civicrm/contact/search/custom", "csid={$csid}&reset=1");
$operators = implode("', '", $operators);
CRM_Core_Error::statusBounce("Full-Text Search does not support the use of a search with two attached operators or string ending with any of these operators ('{$operators}' or a single '@'). Please adjust your search term and try again.", $url, 'Invalid Search String');
}
}
// normalize user-inputted wildcards
$text = str_replace('%', '*', $text);
if (empty($text)) {
$result = '*';
}
elseif (strpos($text, '+') !== FALSE || strpos($text, '-') !== FALSE) {
// if user supplies their own include/exclude operators, use text as is (with trailing wildcard)
$result = $this->mapWords($text, 'word*');
}
elseif (strpos($text, '*') !== FALSE) {
// if user supplies their own wildcards, then don't do any sophisticated changes
$result = $this->mapWords($text, '+word');
}
elseif (preg_match('/^(["\']).*\1$/m', $text)) {
// if surrounded by quotes, use term as is
$result = $text;
}
else {
switch ($mode) {
case self::MODE_NONE:
$result = $this->mapWords($text, '+word', TRUE);
break;
case self::MODE_PHRASE:
$result = '+"' . $text . '"';
break;
case self::MODE_WILDPHRASE:
$result = '+"*' . $text . '*"';
break;
case self::MODE_WILDWORDS:
$result = $this->mapWords($text, '+*word*');
break;
case self::MODE_WILDWORDS_SUFFIX:
$result = $this->mapWords($text, '+word*');
break;
default:
$result = NULL;
}
}
return $this->dedupeWildcards($result, '%');
}
/**
* Format like.
*
* @param $text
* @param $mode
*
* @return mixed
*/
protected function _formatLike($text, $mode) {
$result = NULL;
if (empty($text)) {
$result = '%';
}
elseif (strpos($text, '%') !== FALSE) {
// if user supplies their own wildcards, then don't do any sophisticated changes
$result = $text;
}
else {
switch ($mode) {
case self::MODE_NONE:
case self::MODE_PHRASE:
case self::MODE_WILDPHRASE:
$result = "%" . $text . "%";
break;
case self::MODE_WILDWORDS:
case self::MODE_WILDWORDS_SUFFIX:
$result = "%" . preg_replace('/[ \r\n]+/', '%', $text) . '%';
break;
default:
$result = NULL;
}
}
return $this->dedupeWildcards($result, '%');
}
/**
* @param string $text
* User-supplied query string.
* @param string $template
* A prototypical description of each word, eg "word%" or "word*" or "*word*".
* @param bool $quotes
* True if each searched keyword need to be surrounded with quotes.
* @return string
*/
protected function mapWords($text, $template, $quotes = FALSE) {
$result = array();
foreach ($this->parseWords($text, $quotes) as $word) {
$result[] = str_replace('word', $word, $template);
}
return implode(' ', $result);
}
/**
* @param $text
* @bool $quotes
* @return array
*/
protected function parseWords($text, $quotes) {
//NYSS 9692 special handling for emails
if (preg_match('/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/', $text)) {
$parts = explode('@', $text);
$parts[1] = stristr($parts[1], '.', TRUE);
$text = implode(' ', $parts);
}
//NYSS also replace other occurrences of @
$replacedText = preg_replace('/[ \r\n\t\@]+/', ' ', trim($text));
//filter empty values if any
$keywords = array_filter(explode(' ', $replacedText));
//Ensure each searched keywords are wrapped in double quotes.
if ($quotes) {
foreach ($keywords as &$val) {
if (!is_numeric($val)) {
$val = "\"{$val}\"";
}
}
}
return $keywords;
}
/**
* @param $text
* @param $wildcard
* @return mixed
*/
protected function dedupeWildcards($text, $wildcard) {
if ($text === NULL) {
return NULL;
}
// don't use preg_replace because $wildcard might be special char
while (strpos($text, "{$wildcard}{$wildcard}") !== FALSE) {
$text = str_replace("{$wildcard}{$wildcard}", "{$wildcard}", $text);
}
return $text;
}
/**
* Get modes.
*
* @return array
*/
public static function getModes() {
return array(
self::MODE_NONE,
self::MODE_PHRASE,
self::MODE_WILDPHRASE,
self::MODE_WILDWORDS,
self::MODE_WILDWORDS_SUFFIX,
);
}
/**
* Get languages.
*
* @return array
*/
public static function getLanguages() {
return array(
self::LANG_SOLR,
self::LANG_SQL_FTS,
self::LANG_SQL_FTSBOOL,
self::LANG_SQL_LIKE,
);
}
/**
* @param $text
*
* Ex: drush eval 'civicrm_initialize(); CRM_Utils_QueryFormatter::dumpExampleTable("firstword secondword");'
*/
public static function dumpExampleTable($text) {
$width = strlen($text) + 8;
$buf = '';
$buf .= sprintf("%-{$width}s", 'mode');
foreach (self::getLanguages() as $lang) {
$buf .= sprintf("%-{$width}s", $lang);
}
$buf .= "\n";
foreach (self::getModes() as $mode) {
$formatter = new CRM_Utils_QueryFormatter($mode);
$buf .= sprintf("%-{$width}s", $mode);
foreach (self::getLanguages() as $lang) {
$buf .= sprintf("%-{$width}s", $formatter->format($text, $lang));
}
$buf .= "\n";
}
echo $buf;
}
}

View file

@ -0,0 +1,729 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* This class handles all REST client requests.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_REST {
/**
* Number of seconds we should let a REST process idle
*/
static $rest_timeout = 0;
/**
* Cache the actual UF Class
*/
public $ufClass;
/**
* Class constructor. This caches the real user framework class locally,
* so we can use it for authentication and validation.
*
* @internal param string $uf The userframework class
*/
public function __construct() {
// any external program which call Rest Server is responsible for
// creating and attaching the session
$args = func_get_args();
$this->ufClass = array_shift($args);
}
/**
* Simple ping function to test for liveness.
*
* @param string $var
* The string to be echoed.
*
* @return string
*/
public static function ping($var = NULL) {
$session = CRM_Core_Session::singleton();
$key = $session->get('key');
// $session->set( 'key', $var );
return self::simple(array('message' => "PONG: $key"));
}
/**
* Generates values needed for error messages.
* @param string $message
*
* @return array
*/
public static function error($message = 'Unknown Error') {
$values = array(
'error_message' => $message,
'is_error' => 1,
);
return $values;
}
/**
* Generates values needed for non-error responses.
* @param array $params
*
* @return array
*/
public static function simple($params) {
$values = array('is_error' => 0);
$values += $params;
return $values;
}
/**
* @return string
*/
public function run() {
$result = self::handle();
return self::output($result);
}
/**
* @return string
*/
public function bootAndRun() {
$response = $this->loadCMSBootstrap();
if (is_array($response)) {
return self::output($response);
}
return $this->run();
}
/**
* @param $result
*
* @return string
*/
public static function output(&$result) {
$requestParams = CRM_Utils_Request::exportValues();
$hier = FALSE;
if (is_scalar($result)) {
if (!$result) {
$result = 0;
}
$result = self::simple(array('result' => $result));
}
elseif (is_array($result)) {
if (CRM_Utils_Array::isHierarchical($result)) {
$hier = TRUE;
}
elseif (!array_key_exists('is_error', $result)) {
$result['is_error'] = 0;
}
}
else {
$result = self::error('Could not interpret return values from function.');
}
if (!empty($requestParams['json'])) {
if (!empty($requestParams['prettyprint'])) {
// Don't set content-type header for api explorer output
return self::jsonFormated(array_merge($result));
}
CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
return json_encode(array_merge($result));
}
if (isset($result['count'])) {
$count = ' count="' . $result['count'] . '" ';
}
else {
$count = "";
}
$xml = "<?xml version=\"1.0\"?>
<ResultSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" $count>
";
// check if this is a single element result (contact_get etc)
// or multi element
if ($hier) {
foreach ($result['values'] as $k => $v) {
if (is_array($v)) {
$xml .= "<Result>\n" . CRM_Utils_Array::xml($v) . "</Result>\n";
}
elseif (!is_object($v)) {
$xml .= "<Result>\n<id>{$k}</id><value>{$v}</value></Result>\n";
}
}
}
else {
$xml .= "<Result>\n" . CRM_Utils_Array::xml($result) . "</Result>\n";
}
$xml .= "</ResultSet>\n";
return $xml;
}
/**
* @param $data
*
* @deprecated - switch to native JSON_PRETTY_PRINT when we drop support for php 5.3
*
* @return string
*/
public static function jsonFormated($data) {
// If php is 5.4+ we can use the native method
if (defined('JSON_PRETTY_PRINT')) {
return json_encode($data, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE);
}
// PHP 5.3 shim
$json = str_replace('\/', '/', json_encode($data));
$tabcount = 0;
$result = '';
$inquote = FALSE;
$inarray = FALSE;
$ignorenext = FALSE;
$tab = "\t";
$newline = "\n";
for ($i = 0; $i < strlen($json); $i++) {
$char = $json[$i];
if ($ignorenext) {
$result .= $char;
$ignorenext = FALSE;
}
else {
switch ($char) {
case '{':
if ($inquote) {
$result .= $char;
}
else {
$inarray = FALSE;
$tabcount++;
$result .= $char . $newline . str_repeat($tab, $tabcount);
}
break;
case '}':
if ($inquote) {
$result .= $char;
}
else {
$tabcount--;
$result = trim($result) . $newline . str_repeat($tab, $tabcount) . $char;
}
break;
case ',':
if ($inquote || $inarray) {
$result .= $char;
}
else {
$result .= $char . $newline . str_repeat($tab, $tabcount);
}
break;
case '"':
$inquote = !$inquote;
$result .= $char;
break;
case '\\':
if ($inquote) {
$ignorenext = TRUE;
}
$result .= $char;
break;
case '[':
$inarray = TRUE;
$result .= $char;
break;
case ']':
$inarray = FALSE;
$result .= $char;
break;
default:
$result .= $char;
}
}
}
return $result;
}
/**
* @return array|int
*/
public static function handle() {
$requestParams = CRM_Utils_Request::exportValues();
// Get the function name being called from the q parameter in the query string
$q = CRM_Utils_Array::value('q', $requestParams);
// or for the rest interface, from fnName
$r = CRM_Utils_Array::value('fnName', $requestParams);
if (!empty($r)) {
$q = $r;
}
$entity = CRM_Utils_Array::value('entity', $requestParams);
if (empty($entity) && !empty($q)) {
$args = explode('/', $q);
// If the function isn't in the civicrm namespace, reject the request.
if ($args[0] != 'civicrm') {
return self::error('Unknown function invocation.');
}
// If the query string is malformed, reject the request.
// Does this mean it will reject it
if ((count($args) != 3) && ($args[1] != 'ping')) {
return self::error('Unknown function invocation.');
}
$store = NULL;
if ($args[1] == 'ping') {
return self::ping();
}
}
else {
// or the api format (entity+action)
$args = array();
$args[0] = 'civicrm';
$args[1] = CRM_Utils_Array::value('entity', $requestParams);
$args[2] = CRM_Utils_Array::value('action', $requestParams);
}
// Everyone should be required to provide the server key, so the whole
// interface can be disabled in more change to the configuration file.
// first check for civicrm site key
if (!CRM_Utils_System::authenticateKey(FALSE)) {
$docLink = CRM_Utils_System::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki");
$key = CRM_Utils_Array::value('key', $requestParams);
if (empty($key)) {
return self::error("FATAL: mandatory param 'key' missing. More info at: " . $docLink);
}
return self::error("FATAL: 'key' is incorrect. More info at: " . $docLink);
}
// At this point we know we are not calling ping which does not require authentication.
// Therefore we now need a valid server key and API key.
// Check and see if a valid secret API key is provided.
$api_key = CRM_Utils_Request::retrieve('api_key', 'String', $store, FALSE, NULL, 'REQUEST');
if (!$api_key || strtolower($api_key) == 'null') {
return self::error("FATAL: mandatory param 'api_key' (user key) missing");
}
$valid_user = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $api_key, 'id', 'api_key');
// If we didn't find a valid user, die
if (empty($valid_user)) {
return self::error("User API key invalid");
}
return self::process($args, self::buildParamList());
}
/**
* @param $args
* @param array $params
*
* @return array|int
*/
public static function process(&$args, $params) {
$params['check_permissions'] = TRUE;
$fnName = $apiFile = NULL;
// clean up all function / class names. they should be alphanumeric and _ only
for ($i = 1; $i <= 3; $i++) {
if (!empty($args[$i])) {
$args[$i] = CRM_Utils_String::munge($args[$i]);
}
}
// incase of ajax functions className is passed in url
if (isset($params['className'])) {
$params['className'] = CRM_Utils_String::munge($params['className']);
// functions that are defined only in AJAX.php can be called via
// rest interface
if (!CRM_Core_Page_AJAX::checkAuthz('method', $params['className'], $params['fnName'])) {
return self::error('Unknown function invocation.');
}
return call_user_func(array($params['className'], $params['fnName']), $params);
}
if (!array_key_exists('version', $params)) {
$params['version'] = 3;
}
if ($params['version'] == 2) {
$result['is_error'] = 1;
$result['error_message'] = "FATAL: API v2 not accessible from ajax/REST";
$result['deprecated'] = "Please upgrade to API v3";
return $result;
}
if ($_SERVER['REQUEST_METHOD'] == 'GET' &&
strtolower(substr($args[2], 0, 3)) != 'get' &&
strtolower($args[2] != 'check')) {
// get only valid for non destructive methods
require_once 'api/v3/utils.php';
return civicrm_api3_create_error("SECURITY: All requests that modify the database must be http POST, not GET.",
array(
'IP' => $_SERVER['REMOTE_ADDR'],
'level' => 'security',
'referer' => $_SERVER['HTTP_REFERER'],
'reason' => 'Destructive HTTP GET',
)
);
}
// trap all fatal errors
$errorScope = CRM_Core_TemporaryErrorScope::create(array('CRM_Utils_REST', 'fatal'));
$result = civicrm_api($args[1], $args[2], $params);
unset($errorScope);
if ($result === FALSE) {
return self::error('Unknown error.');
}
return $result;
}
/**
* @return array|mixed|null
*/
public static function &buildParamList() {
$requestParams = CRM_Utils_Request::exportValues();
$params = array();
$skipVars = array(
'q' => 1,
'json' => 1,
'key' => 1,
'api_key' => 1,
'entity' => 1,
'action' => 1,
);
if (array_key_exists('json', $requestParams) && $requestParams['json'][0] == "{") {
$params = json_decode($requestParams['json'], TRUE);
if ($params === NULL) {
CRM_Utils_JSON::output(array('is_error' => 1, 'error_message', 'Unable to decode supplied JSON.'));
}
}
foreach ($requestParams as $n => $v) {
if (!array_key_exists($n, $skipVars)) {
$params[$n] = $v;
}
}
if (array_key_exists('return', $requestParams) && is_array($requestParams['return'])) {
foreach ($requestParams['return'] as $key => $v) {
$params['return.' . $key] = 1;
}
}
return $params;
}
/**
* @param $pearError
*/
public static function fatal($pearError) {
CRM_Utils_System::setHttpHeader('Content-Type', 'text/xml');
$error = array();
$error['code'] = $pearError->getCode();
$error['error_message'] = $pearError->getMessage();
$error['mode'] = $pearError->getMode();
$error['debug_info'] = $pearError->getDebugInfo();
$error['type'] = $pearError->getType();
$error['user_info'] = $pearError->getUserInfo();
$error['to_string'] = $pearError->toString();
$error['is_error'] = 1;
echo self::output($error);
CRM_Utils_System::civiExit();
}
/**
* used to load a template "inline", eg. for ajax, without having to build a menu for each template
*/
public static function loadTemplate() {
$request = CRM_Utils_Request::retrieve('q', 'String');
if (FALSE !== strpos($request, '..')) {
die ("SECURITY FATAL: the url can't contain '..'. Please report the issue on the forum at civicrm.org");
}
$request = explode('/', $request);
$entity = _civicrm_api_get_camel_name($request[2]);
$tplfile = _civicrm_api_get_camel_name($request[3]);
$tpl = 'CRM/' . $entity . '/Page/Inline/' . $tplfile . '.tpl';
$smarty = CRM_Core_Smarty::singleton();
CRM_Utils_System::setTitle("$entity::$tplfile inline $tpl");
if (!$smarty->template_exists($tpl)) {
CRM_Utils_System::setHttpHeader("Status", "404 Not Found");
die ("Can't find the requested template file templates/$tpl");
}
if (array_key_exists('id', $_GET)) {// special treatmenent, because it's often used
$smarty->assign('id', (int) $_GET['id']);// an id is always positive
}
$pos = strpos(implode(array_keys($_GET)), '<');
if ($pos !== FALSE) {
die ("SECURITY FATAL: one of the param names contains &lt;");
}
$param = array_map('htmlentities', $_GET);
unset($param['q']);
$smarty->assign_by_ref("request", $param);
if (!array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) ||
$_SERVER['HTTP_X_REQUESTED_WITH'] != "XMLHttpRequest"
) {
$smarty->assign('tplFile', $tpl);
$config = CRM_Core_Config::singleton();
$content = $smarty->fetch('CRM/common/' . strtolower($config->userFramework) . '.tpl');
if (!defined('CIVICRM_UF_HEAD') && $region = CRM_Core_Region::instance('html-header', FALSE)) {
CRM_Utils_System::addHTMLHead($region->render(''));
}
CRM_Utils_System::appendTPLFile($tpl, $content);
return CRM_Utils_System::theme($content);
}
else {
$content = "<!-- .tpl file embedded: $tpl -->\n";
CRM_Utils_System::appendTPLFile($tpl, $content);
echo $content . $smarty->fetch($tpl);
CRM_Utils_System::civiExit();
}
}
/**
* This is a wrapper so you can call an api via json (it returns json too)
* http://example.org/civicrm/api/json?entity=Contact&action=Get"&json={"contact_type":"Individual","email.get.email":{}}
* to take all the emails from individuals.
* Works for POST & GET (POST recommended).
*/
public static function ajaxJson() {
$requestParams = CRM_Utils_Request::exportValues();
require_once 'api/v3/utils.php';
$config = CRM_Core_Config::singleton();
if (!$config->debug && (!array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) ||
$_SERVER['HTTP_X_REQUESTED_WITH'] != "XMLHttpRequest"
)
) {
$error = civicrm_api3_create_error("SECURITY ALERT: Ajax requests can only be issued by javascript clients, eg. CRM.api3().",
array(
'IP' => $_SERVER['REMOTE_ADDR'],
'level' => 'security',
'referer' => $_SERVER['HTTP_REFERER'],
'reason' => 'CSRF suspected',
)
);
CRM_Utils_JSON::output($error);
}
if (empty($requestParams['entity'])) {
CRM_Utils_JSON::output(civicrm_api3_create_error('missing entity param'));
}
if (empty($requestParams['entity'])) {
CRM_Utils_JSON::output(civicrm_api3_create_error('missing entity entity'));
}
if (!empty($requestParams['json'])) {
$params = json_decode($requestParams['json'], TRUE);
}
$entity = CRM_Utils_String::munge(CRM_Utils_Array::value('entity', $requestParams));
$action = CRM_Utils_String::munge(CRM_Utils_Array::value('action', $requestParams));
if (!is_array($params)) {
CRM_Utils_JSON::output(array(
'is_error' => 1,
'error_message' => 'invalid json format: ?{"param_with_double_quote":"value"}',
));
}
$params['check_permissions'] = TRUE;
$params['version'] = 3;
$_GET['json'] = $requestParams['json'] = 1; // $requestParams is local-only; this line seems pointless unless there's a side-effect influencing other functions
if (!$params['sequential']) {
$params['sequential'] = 1;
}
// trap all fatal errors
$errorScope = CRM_Core_TemporaryErrorScope::create(array('CRM_Utils_REST', 'fatal'));
$result = civicrm_api($entity, $action, $params);
unset($errorScope);
echo self::output($result);
CRM_Utils_System::civiExit();
}
/**
* Run ajax request.
*
* @return array
*/
public static function ajax() {
$requestParams = CRM_Utils_Request::exportValues();
// this is driven by the menu system, so we can use permissioning to
// restrict calls to this etc
// the request has to be sent by an ajax call. First line of protection against csrf
$config = CRM_Core_Config::singleton();
if (!$config->debug &&
(!array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) ||
$_SERVER['HTTP_X_REQUESTED_WITH'] != "XMLHttpRequest"
)
) {
require_once 'api/v3/utils.php';
$error = civicrm_api3_create_error("SECURITY ALERT: Ajax requests can only be issued by javascript clients, eg. CRM.api3().",
array(
'IP' => $_SERVER['REMOTE_ADDR'],
'level' => 'security',
'referer' => $_SERVER['HTTP_REFERER'],
'reason' => 'CSRF suspected',
)
);
CRM_Utils_JSON::output($error);
}
$q = CRM_Utils_Array::value('fnName', $requestParams);
if (!$q) {
$entity = CRM_Utils_Array::value('entity', $requestParams);
$action = CRM_Utils_Array::value('action', $requestParams);
if (!$entity || !$action) {
$err = array('error_message' => 'missing mandatory params "entity=" or "action="', 'is_error' => 1);
echo self::output($err);
CRM_Utils_System::civiExit();
}
$args = array('civicrm', $entity, $action);
}
else {
$args = explode('/', $q);
}
// get the class name, since all ajax functions pass className
$className = CRM_Utils_Array::value('className', $requestParams);
// If the function isn't in the civicrm namespace, reject the request.
if (($args[0] != 'civicrm' && count($args) != 3) && !$className) {
return self::error('Unknown function invocation.');
}
// Support for multiple api calls
if (isset($entity) && $entity === 'api3') {
$result = self::processMultiple();
}
else {
$result = self::process($args, self::buildParamList());
}
echo self::output($result);
CRM_Utils_System::civiExit();
}
/**
* Callback for multiple ajax api calls from CRM.api3()
* @return array
*/
public static function processMultiple() {
$output = array();
foreach (json_decode($_REQUEST['json'], TRUE) as $key => $call) {
$args = array(
'civicrm',
$call[0],
$call[1],
);
$output[$key] = self::process($args, CRM_Utils_Array::value(2, $call, array()));
}
return $output;
}
/**
* @return array|NULL
* NULL if execution should proceed; array if the response is already known
*/
public function loadCMSBootstrap() {
$requestParams = CRM_Utils_Request::exportValues();
$q = CRM_Utils_Array::value('q', $requestParams);
$args = explode('/', $q);
// Proceed with bootstrap for "?entity=X&action=Y"
// Proceed with bootstrap for "?q=civicrm/X/Y" but not "?q=civicrm/ping"
if (!empty($q)) {
if (count($args) == 2 && $args[1] == 'ping') {
CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE);
return NULL; // this is pretty wonky but maybe there's some reason I can't see
}
if (count($args) != 3) {
return self::error('ERROR: Malformed REST path');
}
if ($args[0] != 'civicrm') {
return self::error('ERROR: Malformed REST path');
}
// Therefore we have reasonably well-formed "?q=civicrm/X/Y"
}
if (!CRM_Utils_System::authenticateKey(FALSE)) {
// FIXME: At time of writing, this doesn't actually do anything because
// authenticateKey abends, but that's a bad behavior which sends a
// malformed response.
CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE);
return self::error('Failed to authenticate key');
}
$uid = NULL;
if (!$uid) {
$store = NULL;
$api_key = CRM_Utils_Request::retrieve('api_key', 'String', $store, FALSE, NULL, 'REQUEST');
if (empty($api_key)) {
CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE);
return self::error("FATAL: mandatory param 'api_key' (user key) missing");
}
$contact_id = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $api_key, 'id', 'api_key');
if ($contact_id) {
$uid = CRM_Core_BAO_UFMatch::getUFId($contact_id);
}
}
if ($uid && $contact_id) {
CRM_Utils_System::loadBootStrap(array('uid' => $uid), TRUE, FALSE);
$session = CRM_Core_Session::singleton();
$session->set('ufID', $uid);
$session->set('userID', $contact_id);
CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1',
array(1 => array($contact_id, 'Integer'))
);
return NULL;
}
else {
CRM_Utils_System::loadBootStrap(array(), FALSE, FALSE);
return self::error('ERROR: No CMS user associated with given api-key');
}
}
}

View file

@ -0,0 +1,102 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_ReCAPTCHA {
protected $_captcha = NULL;
protected $_name = NULL;
protected $_url = NULL;
protected $_phrase = NULL;
/**
* Singleton.
*
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
*
* @var CRM_Utils_ReCAPTCHA
*/
static private $_singleton = NULL;
/**
* Singleton function used to manage this object.
*
* @return object
*/
public static function &singleton() {
if (self::$_singleton === NULL) {
self::$_singleton = new CRM_Utils_ReCAPTCHA();
}
return self::$_singleton;
}
/**
* Add element to form.
*
* @param CRM_Core_Form $form
*/
public static function add(&$form) {
$error = NULL;
$config = CRM_Core_Config::singleton();
$useSSL = FALSE;
if (!function_exists('recaptcha_get_html')) {
require_once 'packages/recaptcha/recaptchalib.php';
}
// See if we are using SSL
if (CRM_Utils_System::isSSL()) {
$useSSL = TRUE;
}
$html = recaptcha_get_html($config->recaptchaPublicKey, $error, $useSSL);
$form->assign('recaptchaHTML', $html);
$form->assign('recaptchaOptions', $config->recaptchaOptions);
$form->add(
'text',
'g-recaptcha-response',
'reCaptcha',
NULL,
TRUE
);
$form->registerRule('recaptcha', 'callback', 'validate', 'CRM_Utils_ReCAPTCHA');
if ($form->isSubmitted() && empty($form->_submitValues['g-recaptcha-response'])) {
$form->setElementError(
'g-recaptcha-response',
ts('Please go back and complete the CAPTCHA at the bottom of this form.')
);
}
}
}

View file

@ -0,0 +1,254 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Recent items utility class.
*/
class CRM_Utils_Recent {
/**
* Store name
*
* @var string
*/
const MAX_ITEMS = 30, STORE_NAME = 'CRM_Utils_Recent';
/**
* The list of recently viewed items.
*
* @var array
*/
static private $_recent = NULL;
/**
* Maximum stack size
* @var int
*/
static private $_maxItems = 10;
/**
* Initialize this class and set the static variables.
*/
public static function initialize() {
$maxItemsSetting = Civi::settings()->get('recentItemsMaxCount');
if (isset($maxItemsSetting) && $maxItemsSetting > 0 && $maxItemsSetting < self::MAX_ITEMS) {
self::$_maxItems = $maxItemsSetting;
}
if (!self::$_recent) {
$session = CRM_Core_Session::singleton();
self::$_recent = $session->get(self::STORE_NAME);
if (!self::$_recent) {
self::$_recent = array();
}
}
}
/**
* Return the recently viewed array.
*
* @return array
* the recently viewed array
*/
public static function &get() {
self::initialize();
return self::$_recent;
}
/**
* Add an item to the recent stack.
*
* @param string $title
* The title to display.
* @param string $url
* The link for the above title.
* @param string $id
* Object id.
* @param $type
* @param int $contactId
* @param string $contactName
* @param array $others
*/
public static function add(
$title,
$url,
$id,
$type,
$contactId,
$contactName,
$others = array()
) {
self::initialize();
if (!self::isProviderEnabled($type)) {
return;
}
$session = CRM_Core_Session::singleton();
// make sure item is not already present in list
for ($i = 0; $i < count(self::$_recent); $i++) {
if (self::$_recent[$i]['url'] == $url) {
// delete item from array
array_splice(self::$_recent, $i, 1);
break;
}
}
if (!is_array($others)) {
$others = array();
}
array_unshift(self::$_recent,
array(
'title' => $title,
'url' => $url,
'id' => $id,
'type' => $type,
'contact_id' => $contactId,
'contactName' => $contactName,
'subtype' => CRM_Utils_Array::value('subtype', $others),
'isDeleted' => CRM_Utils_Array::value('isDeleted', $others, FALSE),
'image_url' => CRM_Utils_Array::value('imageUrl', $others),
'edit_url' => CRM_Utils_Array::value('editUrl', $others),
'delete_url' => CRM_Utils_Array::value('deleteUrl', $others),
)
);
if (count(self::$_recent) > self::$_maxItems) {
array_pop(self::$_recent);
}
CRM_Utils_Hook::recent(self::$_recent);
$session->set(self::STORE_NAME, self::$_recent);
}
/**
* Delete an item from the recent stack.
*
* @param array $recentItem
* Array of the recent Item to be removed.
*/
public static function del($recentItem) {
self::initialize();
$tempRecent = self::$_recent;
self::$_recent = array();
// make sure item is not already present in list
for ($i = 0; $i < count($tempRecent); $i++) {
if (!($tempRecent[$i]['id'] == $recentItem['id'] &&
$tempRecent[$i]['type'] == $recentItem['type']
)
) {
self::$_recent[] = $tempRecent[$i];
}
}
$session = CRM_Core_Session::singleton();
$session->set(self::STORE_NAME, self::$_recent);
}
/**
* Delete an item from the recent stack.
*
* @param string $id
* Contact id that had to be removed.
*/
public static function delContact($id) {
self::initialize();
$tempRecent = self::$_recent;
self::$_recent = array();
// rebuild recent.
for ($i = 0; $i < count($tempRecent); $i++) {
// don't include deleted contact in recent.
if (CRM_Utils_Array::value('contact_id', $tempRecent[$i]) == $id) {
continue;
}
self::$_recent[] = $tempRecent[$i];
}
$session = CRM_Core_Session::singleton();
$session->set(self::STORE_NAME, self::$_recent);
}
/**
* Check if a provider is allowed to add stuff.
* If correspondig setting is empty, all are allowed
*
* @param string $providerName
*/
public static function isProviderEnabled($providerName) {
// Join contact types to providerName 'Contact'
$contactTypes = CRM_Contact_BAO_ContactType::contactTypes(TRUE);
if (in_array($providerName, $contactTypes)) {
$providerName = 'Contact';
}
$allowed = TRUE;
// Use core setting recentItemsProviders if configured
$providersPermitted = Civi::settings()->get('recentItemsProviders');
if ($providersPermitted) {
$allowed = in_array($providerName, $providersPermitted);
}
// Else allow
return $allowed;
}
/**
* Gets the list of available providers to civi's recent items stack
*/
public static function getProviders() {
$providers = array(
'Contact' => ts('Contacts'),
'Relationship' => ts('Relationships'),
'Activity' => ts('Activities'),
'Note' => ts('Notes'),
'Group' => ts('Groups'),
'Case' => ts('Cases'),
'Contribution' => ts('Contributions'),
'Participant' => ts('Participants'),
'Grant' => ts('Grants'),
'Membership' => ts('Memberships'),
'Pledge' => ts('Pledges'),
'Event' => ts('Events'),
'Campaign' => ts('Campaigns'),
);
return $providers;
}
}

View file

@ -0,0 +1,189 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Class for managing a http request
*/
class CRM_Utils_Request {
/**
* Get a unique ID for the request.
*
* This unique ID is assigned to mysql when the connection is opened and is
* available in PHP.
*
* The intent is that it is available for logging purposes and for triggers.
*
* The resulting string is 17 characters long. This consists of 13 characters of uniqid
* and 4 more random characters.
*
* Uniqid is unique to the microsecond - to make it more unique we add 4 more characters
* but stop short of the full 23 character string that a prefix would generate.
*
* It is intended that this string will be saved to log tables so striking a balance between
* uniqueness and length is important. Note that I did check & lining up with byte values
* (e.g 16 characters) does not confer any benefits. Using a CHAR field rather than VARCHAR
* may improve speed, if indexed.
*
* @return string
*/
public static function id() {
if (!isset(\Civi::$statics[__CLASS__]['id'])) {
\Civi::$statics[__CLASS__]['id'] = uniqid() . CRM_Utils_String::createRandom(CRM_Utils_String::ALPHANUMERIC, 4);
}
return \Civi::$statics[__CLASS__]['id'];
}
/**
* Retrieve a value from the request (GET/POST/REQUEST)
*
* @param string $name
* Name of the variable to be retrieved.
* @param string $type
* Type of the variable (see CRM_Utils_Type for details).
* @param object $store
* Session scope where variable is stored.
* @param bool $abort
* TRUE, if the variable is required.
* @param mixed $default
* Default value of the variable if not present.
* @param string $method
* Where to look for the variable - 'GET', 'POST' or 'REQUEST'.
*
* @return mixed
* The value of the variable
*/
public static function retrieve($name, $type, &$store = NULL, $abort = FALSE, $default = NULL, $method = 'REQUEST') {
// hack to detect stuff not yet converted to new style
if (!is_string($type)) {
CRM_Core_Error::backtrace();
CRM_Core_Error::fatal(ts("Please convert retrieve call to use new function signature"));
}
$value = NULL;
switch ($method) {
case 'GET':
$value = self::getValue($name, $_GET);
break;
case 'POST':
$value = self::getValue($name, $_POST);
break;
default:
$value = self::getValue($name, $_REQUEST);
break;
}
if (isset($value) &&
(CRM_Utils_Type::validate($value, $type, $abort, $name) === NULL)
) {
$value = NULL;
}
if (!isset($value) && $store) {
$value = $store->get($name);
}
if (!isset($value) && $abort) {
CRM_Core_Error::fatal(ts("Could not find valid value for %1", array(1 => $name)));
}
if (!isset($value) && $default) {
$value = $default;
}
// minor hack for action
if ($name == 'action' && is_string($value)) {
$value = CRM_Core_Action::resolve($value);
}
if (isset($value) && $store) {
$store->set($name, $value);
}
return $value;
}
/**
* @param string $name
* Name of the variable to be retrieved.
*
* @param array $method - '$_GET', '$_POST' or '$_REQUEST'.
*
* @return mixed
* The value of the variable
*/
public static function getValue($name, $method) {
if (isset($method[$name])) {
return $method[$name];
}
// CRM-18384 - decode incorrect keys generated when &amp; is present in url
foreach ($method as $key => $value) {
if (strpos($key, 'amp;') !== FALSE) {
$method[str_replace('amp;', '', $key)] = $method[$key];
if (isset($method[$name])) {
return $method[$name];
}
else {
continue;
}
}
}
return NULL;
}
/**
* This is a replacement for $_REQUEST which includes $_GET/$_POST
* but excludes $_COOKIE / $_ENV / $_SERVER.
*
* @return array
*/
public static function exportValues() {
// For more discussion of default $_REQUEST handling, see:
// http://www.php.net/manual/en/reserved.variables.request.php
// http://www.php.net/manual/en/ini.core.php#ini.request-order
// http://www.php.net/manual/en/ini.core.php#ini.variables-order
$result = array();
if ($_GET) {
$result = array_merge($result, $_GET);
}
if ($_POST) {
$result = array_merge($result, $_POST);
}
return $result;
}
}

View file

@ -0,0 +1,931 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'HTML/QuickForm/Rule/Email.php';
/**
* Class CRM_Utils_Rule
*/
class CRM_Utils_Rule {
/**
* @param $str
* @param int $maxLength
*
* @return bool
*/
public static function title($str, $maxLength = 127) {
// check length etc
if (empty($str) || strlen($str) > $maxLength) {
return FALSE;
}
// Make sure it include valid characters, alpha numeric and underscores
if (!preg_match('/^\w[\w\s\'\&\,\$\#\-\.\"\?\!]+$/i', $str)) {
return FALSE;
}
return TRUE;
}
/**
* @param $str
*
* @return bool
*/
public static function longTitle($str) {
return self::title($str, 255);
}
/**
* @param $str
*
* @return bool
*/
public static function variable($str) {
// check length etc
if (empty($str) || strlen($str) > 31) {
return FALSE;
}
// make sure it includes valid characters, alpha numeric and underscores
if (!preg_match('/^[\w]+$/i', $str)) {
return FALSE;
}
return TRUE;
}
/**
* Validate that a string is a valid MySQL column name or alias.
*
* @param $str
*
* @return bool
*/
public static function mysqlColumnNameOrAlias($str) {
// Check not empty.
if (empty($str)) {
return FALSE;
}
// Ensure $str conforms to expected format. Not a complete expression of
// what MySQL permits; this should permit the formats CiviCRM generates.
//
// * Table name prefix is optional.
// * Table & column names & aliases:
// * Composed of alphanumeric chars, underscore and hyphens.
// * Maximum length of 64 chars.
// * Optionally surrounded by backticks, in which case spaces also OK.
if (!preg_match('/^((`[\w- ]{1,64}`|[\w-]{1,64})\.)?(`[\w- ]{1,64}`|[\w-]{1,64})$/i', $str)) {
return FALSE;
}
return TRUE;
}
/**
* Validate that a string is ASC or DESC.
*
* Empty string should be treated as invalid and ignored => default = ASC.
*
* @param $str
* @return bool
*/
public static function mysqlOrderByDirection($str) {
if (!preg_match('/^(asc|desc)$/i', $str)) {
return FALSE;
}
return TRUE;
}
/**
* Validate that a string is valid order by clause.
*
* @param $str
* @return bool
*/
public static function mysqlOrderBy($str) {
$matches = array();
// Using the field function in order by is valid.
// Look for a string like field(contribution_status_id,3,4,6).
// or field(civicrm_contribution.contribution_status_id,3,4,6)
if (preg_match('/field\([a-z_.]+,[0-9,]+\)/', $str, $matches)) {
// We have checked these. Remove them as they will fail the next lot.
// Our check currently only permits numbers & no back ticks. If we get a
// need for strings or backticks we can add.
$str = str_replace($matches, '', $str);
}
$str = trim($str);
if (!empty($matches) && empty($str)) {
// nothing left to check after the field check.
return TRUE;
}
// Making a regex for a comma separated list is quite hard and not readable
// at all, so we split and loop over.
$parts = explode(',', $str);
foreach ($parts as $part) {
if (!preg_match('/^((`[\w-]{1,64}`|[\w-]{1,64})\.)?(`[\w-]{1,64}`|[\w-]{1,64})( (asc|desc))?$/i', trim($part))) {
return FALSE;
}
}
return TRUE;
}
/**
* @param $str
*
* @return bool
*/
public static function qfVariable($str) {
// check length etc
//if ( empty( $str ) || strlen( $str ) > 31 ) {
if (strlen(trim($str)) == 0 || strlen($str) > 31) {
return FALSE;
}
// make sure it includes valid characters, alpha numeric and underscores
// added (. and ,) option (CRM-1336)
if (!preg_match('/^[\w\s\.\,]+$/i', $str)) {
return FALSE;
}
return TRUE;
}
/**
* @param $phone
*
* @return bool
*/
public static function phone($phone) {
// check length etc
if (empty($phone) || strlen($phone) > 16) {
return FALSE;
}
// make sure it includes valid characters, (, \s and numeric
if (preg_match('/^[\d\(\)\-\.\s]+$/', $phone)) {
return TRUE;
}
return FALSE;
}
/**
* @param $query
*
* @return bool
*/
public static function query($query) {
// check length etc
if (empty($query) || strlen($query) < 3 || strlen($query) > 127) {
return FALSE;
}
// make sure it includes valid characters, alpha numeric and underscores
if (!preg_match('/^[\w\s\%\'\&\,\$\#]+$/i', $query)) {
return FALSE;
}
return TRUE;
}
/**
* @param $url
*
* @return bool
*/
public static function url($url) {
if (preg_match('/^\//', $url)) {
// allow relative URL's (CRM-15598)
$url = 'http://' . $_SERVER['HTTP_HOST'] . $url;
}
return (bool) filter_var($url, FILTER_VALIDATE_URL);
}
/**
* @param $url
*
* @return bool
*/
public static function urlish($url) {
if (empty($url)) {
return TRUE;
}
$url = Civi::paths()->getUrl($url, 'absolute');
return (bool) filter_var($url, FILTER_VALIDATE_URL);
}
/**
* @param $string
*
* @return bool
*/
public static function wikiURL($string) {
$items = explode(' ', trim($string), 2);
return self::url($items[0]);
}
/**
* @param $domain
*
* @return bool
*/
public static function domain($domain) {
// not perfect, but better than the previous one; see CRM-1502
if (!preg_match('/^[A-Za-z0-9]([A-Za-z0-9\.\-]*[A-Za-z0-9])?$/', $domain)) {
return FALSE;
}
return TRUE;
}
/**
* @param $value
* @param null $default
*
* @return null
*/
public static function date($value, $default = NULL) {
if (is_string($value) &&
preg_match('/^\d\d\d\d-?\d\d-?\d\d$/', $value)
) {
return $value;
}
return $default;
}
/**
* @param $value
* @param null $default
*
* @return null|string
*/
public static function dateTime($value, $default = NULL) {
$result = $default;
if (is_string($value) &&
preg_match('/^\d\d\d\d-?\d\d-?\d\d(\s\d\d:\d\d(:\d\d)?|\d\d\d\d(\d\d)?)?$/', $value)
) {
$result = $value;
}
return $result;
}
/**
* Check the validity of the date (in qf format)
* note that only a year is valid, or a mon-year is
* also valid in addition to day-mon-year. The date
* specified has to be beyond today. (i.e today or later)
*
* @param array $date
* @param bool $monthRequired
* Check whether month is mandatory.
*
* @return bool
* true if valid date
*/
public static function currentDate($date, $monthRequired = TRUE) {
$config = CRM_Core_Config::singleton();
$d = CRM_Utils_Array::value('d', $date);
$m = CRM_Utils_Array::value('M', $date);
$y = CRM_Utils_Array::value('Y', $date);
if (!$d && !$m && !$y) {
return TRUE;
}
// CRM-9017 CiviContribute/CiviMember form with expiration date format 'm Y'
if (!$m && !empty($date['m'])) {
$m = CRM_Utils_Array::value('m', $date);
}
$day = $mon = 1;
$year = 0;
if ($d) {
$day = $d;
}
if ($m) {
$mon = $m;
}
if ($y) {
$year = $y;
}
// if we have day we need mon, and if we have mon we need year
if (($d && !$m) ||
($d && !$y) ||
($m && !$y)
) {
return FALSE;
}
$result = FALSE;
if (!empty($day) || !empty($mon) || !empty($year)) {
$result = checkdate($mon, $day, $year);
}
if (!$result) {
return FALSE;
}
// ensure we have month if required
if ($monthRequired && !$m) {
return FALSE;
}
// now make sure this date is greater that today
$currentDate = getdate();
if ($year > $currentDate['year']) {
return TRUE;
}
elseif ($year < $currentDate['year']) {
return FALSE;
}
if ($m) {
if ($mon > $currentDate['mon']) {
return TRUE;
}
elseif ($mon < $currentDate['mon']) {
return FALSE;
}
}
if ($d) {
if ($day > $currentDate['mday']) {
return TRUE;
}
elseif ($day < $currentDate['mday']) {
return FALSE;
}
}
return TRUE;
}
/**
* Check the validity of a date or datetime (timestamp)
* value which is in YYYYMMDD or YYYYMMDDHHMMSS format
*
* Uses PHP checkdate() - params are ( int $month, int $day, int $year )
*
* @param string $date
*
* @return bool
* true if valid date
*/
public static function mysqlDate($date) {
// allow date to be null
if ($date == NULL) {
return TRUE;
}
if (checkdate(substr($date, 4, 2), substr($date, 6, 2), substr($date, 0, 4))) {
return TRUE;
}
return FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function integer($value) {
if (is_int($value)) {
return TRUE;
}
// CRM-13460
// ensure number passed is always a string numeral
if (!is_numeric($value)) {
return FALSE;
}
// note that is_int matches only integer type
// and not strings which are only integers
// hence we do this here
if (preg_match('/^\d+$/', $value)) {
return TRUE;
}
if ($value < 0) {
$negValue = -1 * $value;
if (is_int($negValue)) {
return TRUE;
}
}
return FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function positiveInteger($value) {
if (is_int($value)) {
return ($value < 0) ? FALSE : TRUE;
}
// CRM-13460
// ensure number passed is always a string numeral
if (!is_numeric($value)) {
return FALSE;
}
if (preg_match('/^\d+$/', $value)) {
return TRUE;
}
return FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function numeric($value) {
// lets use a php gatekeeper to ensure this is numeric
if (!is_numeric($value)) {
return FALSE;
}
return preg_match('/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/', $value) ? TRUE : FALSE;
}
/**
* @param $value
* @param $noOfDigit
*
* @return bool
*/
public static function numberOfDigit($value, $noOfDigit) {
return preg_match('/^\d{' . $noOfDigit . '}$/', $value) ? TRUE : FALSE;
}
/**
* Strip thousand separator from a money string.
*
* Note that this should be done at the form layer. Once we are processing
* money at the BAO or processor layer we should be working with something that
* is already in a normalised format.
*
* @param string $value
*
* @return string
*/
public static function cleanMoney($value) {
// first remove all white space
$value = str_replace(array(' ', "\t", "\n"), '', $value);
$config = CRM_Core_Config::singleton();
//CRM-14868
$currencySymbols = CRM_Core_PseudoConstant::get(
'CRM_Contribute_DAO_Contribution',
'currency', array(
'keyColumn' => 'name',
'labelColumn' => 'symbol',
)
);
$value = str_replace($currencySymbols, '', $value);
if ($config->monetaryThousandSeparator) {
$mon_thousands_sep = $config->monetaryThousandSeparator;
}
else {
$mon_thousands_sep = ',';
}
// ugly fix for CRM-6391: do not drop the thousand separator if
// it looks like its separating decimal part (because a given
// value undergoes a second cleanMoney() call, for example)
// CRM-15835 - in case the amount/value contains 0 after decimal
// eg 150.5 the following if condition will pass
if ($mon_thousands_sep != '.' or (substr($value, -3, 1) != '.' && substr($value, -2, 1) != '.')) {
$value = str_replace($mon_thousands_sep, '', $value);
}
if ($config->monetaryDecimalPoint) {
$mon_decimal_point = $config->monetaryDecimalPoint;
}
else {
$mon_decimal_point = '.';
}
$value = str_replace($mon_decimal_point, '.', $value);
return $value;
}
/**
* @param $value
*
* @return bool
*/
public static function money($value) {
$config = CRM_Core_Config::singleton();
// only edge case when we have a decimal point in the input money
// field and not defined in the decimal Point in config settings
if ($config->monetaryDecimalPoint &&
$config->monetaryDecimalPoint != '.' &&
// CRM-7122 also check for Thousands Separator in config settings
$config->monetaryThousandSeparator != '.' &&
substr_count($value, '.')
) {
return FALSE;
}
$value = self::cleanMoney($value);
if (self::integer($value)) {
return TRUE;
}
// Allow values such as -0, 1.024555, -.1
// We need to support multiple decimal places here, not just the number allowed by locale
// otherwise tax calculations break when you want the inclusive amount to be a round number (eg. £10 inc. VAT requires 8.333333333 here).
return preg_match('/(^-?\d+\.?\d*$)|(^-?\.\d+$)/', $value) ? TRUE : FALSE;
}
/**
* @param $value
* @param int $maxLength
*
* @return bool
*/
public static function string($value, $maxLength = 0) {
if (is_string($value) &&
($maxLength === 0 || strlen($value) <= $maxLength)
) {
return TRUE;
}
return FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function boolean($value) {
return preg_match(
'/(^(1|0)$)|(^(Y(es)?|N(o)?)$)|(^(T(rue)?|F(alse)?)$)/i', $value
) ? TRUE : FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function email($value) {
return (bool) filter_var($value, FILTER_VALIDATE_EMAIL);
}
/**
* @param $list
*
* @return bool
*/
public static function emailList($list) {
$emails = explode(',', $list);
foreach ($emails as $email) {
$email = trim($email);
if (!self::email($email)) {
return FALSE;
}
}
return TRUE;
}
/**
* allow between 4-6 digits as postal code since india needs 6 and US needs 5 (or
* if u disregard the first 0, 4 (thanx excel!)
* FIXME: we need to figure out how to localize such rules
* @param $value
*
* @return bool
*/
public static function postalCode($value) {
if (preg_match('/^\d{4,6}(-\d{4})?$/', $value)) {
return TRUE;
}
return FALSE;
}
/**
* See how file rules are written in HTML/QuickForm/file.php
* Checks to make sure the uploaded file is ascii
*
* @param string $elementValue
*
* @return bool
* True if file has been uploaded, false otherwise
*/
public static function asciiFile($elementValue) {
if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
(!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
) {
return CRM_Utils_File::isAscii($elementValue['tmp_name']);
}
return FALSE;
}
/**
* Checks to make sure the uploaded file is in UTF-8, recodes if it's not
*
* @param array $elementValue
*
* @return bool
* Whether file has been uploaded properly and is now in UTF-8.
*/
public static function utf8File($elementValue) {
$success = FALSE;
if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
(!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
) {
$success = CRM_Utils_File::isAscii($elementValue['tmp_name']);
// if it's a file, but not UTF-8, let's try and recode it
// and then make sure it's an UTF-8 file in the end
if (!$success) {
$success = CRM_Utils_File::toUtf8($elementValue['tmp_name']);
if ($success) {
$success = CRM_Utils_File::isAscii($elementValue['tmp_name']);
}
}
}
return $success;
}
/**
* See how file rules are written in HTML/QuickForm/file.php
* Checks to make sure the uploaded file is html
*
* @param array $elementValue
*
* @return bool
* True if file has been uploaded, false otherwise
*/
public static function htmlFile($elementValue) {
if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
(!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
) {
return CRM_Utils_File::isHtmlFile($elementValue['tmp_name']);
}
return FALSE;
}
/**
* Check if there is a record with the same name in the db.
*
* @param string $value
* The value of the field we are checking.
* @param array $options
* The daoName, fieldName (optional) and DomainID (optional).
*
* @return bool
* true if object exists
*/
public static function objectExists($value, $options) {
$name = 'name';
if (isset($options[2])) {
$name = $options[2];
}
return CRM_Core_DAO::objectExists($value, CRM_Utils_Array::value(0, $options), CRM_Utils_Array::value(1, $options), CRM_Utils_Array::value(2, $options, $name), CRM_Utils_Array::value(3, $options));
}
/**
* @param $value
* @param $options
*
* @return bool
*/
public static function optionExists($value, $options) {
return CRM_Core_OptionValue::optionExists($value, $options[0], $options[1], $options[2], CRM_Utils_Array::value(3, $options, 'name'), CRM_Utils_Array::value(4, $options, FALSE));
}
/**
* @param $value
* @param $type
*
* @return bool
*/
public static function creditCardNumber($value, $type) {
return Validate_Finance_CreditCard::number($value, $type);
}
/**
* @param $value
* @param $type
*
* @return bool
*/
public static function cvv($value, $type) {
return Validate_Finance_CreditCard::cvv($value, $type);
}
/**
* @param $value
*
* @return bool
*/
public static function currencyCode($value) {
static $currencyCodes = NULL;
if (!$currencyCodes) {
$currencyCodes = CRM_Core_PseudoConstant::currencyCode();
}
if (in_array($value, $currencyCodes)) {
return TRUE;
}
return FALSE;
}
/**
* @param $value
*
* @return bool
*/
public static function xssString($value) {
if (is_string($value)) {
return preg_match('!<(vb)?script[^>]*>.*</(vb)?script.*>!ims',
$value
) ? FALSE : TRUE;
}
else {
return TRUE;
}
}
/**
* @param $path
*
* @return bool
*/
public static function fileExists($path) {
return file_exists($path);
}
/**
* Determine whether the value contains a valid reference to a directory.
*
* Paths stored in the setting system may be absolute -- or may be
* relative to the default data directory.
*
* @param string $path
* @return bool
*/
public static function settingPath($path) {
return is_dir(Civi::paths()->getPath($path));
}
/**
* @param $value
* @param null $actualElementValue
*
* @return bool
*/
public static function validContact($value, $actualElementValue = NULL) {
if ($actualElementValue) {
$value = $actualElementValue;
}
return CRM_Utils_Rule::positiveInteger($value);
}
/**
* Check the validity of the date (in qf format)
* note that only a year is valid, or a mon-year is
* also valid in addition to day-mon-year
*
* @param array $date
*
* @return bool
* true if valid date
*/
public static function qfDate($date) {
$config = CRM_Core_Config::singleton();
$d = CRM_Utils_Array::value('d', $date);
$m = CRM_Utils_Array::value('M', $date);
$y = CRM_Utils_Array::value('Y', $date);
if (isset($date['h']) ||
isset($date['g'])
) {
$m = CRM_Utils_Array::value('M', $date);
}
if (!$d && !$m && !$y) {
return TRUE;
}
$day = $mon = 1;
$year = 0;
if ($d) {
$day = $d;
}
if ($m) {
$mon = $m;
}
if ($y) {
$year = $y;
}
// if we have day we need mon, and if we have mon we need year
if (($d && !$m) ||
($d && !$y) ||
($m && !$y)
) {
return FALSE;
}
if (!empty($day) || !empty($mon) || !empty($year)) {
return checkdate($mon, $day, $year);
}
return FALSE;
}
/**
* @param $key
*
* @return bool
*/
public static function qfKey($key) {
return ($key) ? CRM_Core_Key::valid($key) : FALSE;
}
/**
* Check if the values in the date range are in correct chronological order.
*
* @param array $fields
* Fields of the form.
* @param $fieldName
* Name of date range field.
* @param $errors
* The error array.
* @param $title
* Title of the date range to be displayed in the error message.
*/
public static function validDateRange($fields, $fieldName, &$errors, $title) {
$lowDate = strtotime($fields[$fieldName . '_low']);
$highDate = strtotime($fields[$fieldName . '_high']);
if ($lowDate > $highDate) {
$errors[$fieldName . '_range_error'] = ts('%1: Please check that your date range is in correct chronological order.', array(1 => $title));
}
}
/**
* @param string $key Extension Key to check
* @return bool
*/
public static function checkExtesnionKeyIsValid($key = NULL) {
if (!empty($key) && !preg_match('/^[0-9a-zA-Z._-]+$/', $key)) {
return FALSE;
}
return TRUE;
}
}

View file

@ -0,0 +1,125 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Just another collection of static utils functions.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_SQL {
/**
* Helper function for adding the permissioned subquery from one entity onto another
*
* @param string $entity
* @param string $joinColumn
* @return array
*/
public static function mergeSubquery($entity, $joinColumn = 'id') {
require_once 'api/v3/utils.php';
$baoName = _civicrm_api3_get_BAO($entity);
$bao = new $baoName();
$clauses = $subclauses = array();
foreach ((array) $bao->addSelectWhereClause() as $field => $vals) {
if ($vals && $field == $joinColumn) {
$clauses = array_merge($clauses, (array) $vals);
}
elseif ($vals) {
$subclauses[] = "$field " . implode(" AND $field ", (array) $vals);
}
}
if ($subclauses) {
$clauses[] = "IN (SELECT `$joinColumn` FROM `" . $bao->tableName() . "` WHERE " . implode(' AND ', $subclauses) . ")";
}
return $clauses;
}
/**
* Get current sqlModes of the session
* @return array
*/
public static function getSqlModes() {
$sqlModes = explode(',', CRM_Core_DAO::singleValueQuery('SELECT @@sql_mode'));
return $sqlModes;
}
/**
* Checks if this system enforce the MYSQL mode ONLY_FULL_GROUP_BY.
* This function should be named supportsAnyValueAndEnforcesFullGroupBY(),
* but should be deprecated instead.
*
* @return mixed
* @deprecated
*/
public static function supportsFullGroupBy() {
// CRM-21455 MariaDB 10.2 does not support ANY_VALUE
$version = CRM_Core_DAO::singleValueQuery('SELECT VERSION()');
if (stripos($version, 'mariadb') !== FALSE) {
return FALSE;
}
return version_compare($version, '5.7', '>=');
}
/**
* Disable ONLY_FULL_GROUP_BY for MySQL versions lower then 5.7
*
* @return bool
*/
public static function disableFullGroupByMode() {
$sqlModes = self::getSqlModes();
// Disable only_full_group_by mode for lower sql versions.
if (!self::supportsFullGroupBy() || (!empty($sqlModes) && !in_array('ONLY_FULL_GROUP_BY', $sqlModes))) {
if ($key = array_search('ONLY_FULL_GROUP_BY', $sqlModes)) {
unset($sqlModes[$key]);
CRM_Core_DAO::executeQuery("SET SESSION sql_mode = '" . implode(',', $sqlModes) . "'");
}
return TRUE;
}
return FALSE;
}
/**
* CHeck if ONLY_FULL_GROUP_BY is in the global sql_modes
* @return bool
*/
public static function isGroupByModeInDefault() {
if (!self::supportsFullGroupBy()) {
return FALSE;
}
$sqlModes = explode(',', CRM_Core_DAO::singleValueQuery('SELECT @@global.sql_mode'));
if (!in_array('ONLY_FULL_GROUP_BY', $sqlModes)) {
return FALSE;
}
return TRUE;
}
}

View file

@ -0,0 +1,188 @@
<?php
/**
* Dear God Why Do I Have To Write This (Dumb SQL Builder)
*
* Usage:
* $insert = CRM_Utils_SQL_Insert::into('mytable')
* ->row(array('col1' => '1', 'col2' => '2' ))
* ->row(array('col1' => '2b', 'col2' => '1b'));
* echo $insert->toSQL();
*
* Note: In MySQL, numeric values may be escaped. Except for NULL values,
* it's reasonable for us to simply escape all values by default -- without
* any knowledge of the underlying schema.
*
* Design principles:
* - Portable
* - No knowledge of the underlying SQL API (except for escaping -- CRM_Core_DAO::escapeString)
* - No knowledge of the underlying data model
* - Single file
* - SQL clauses correspond to PHP functions ($select->where("foo_id=123"))
*/
class CRM_Utils_SQL_Insert {
private $verb = 'INSERT INTO';
/**
* @var string
*/
private $table;
/**
* @var array
*/
private $rows;
/**
* Array<string> list of column names
*/
private $columns;
/**
* Create a new INSERT query.
*
* @param string $table
* Table-name and optional alias.
* @return CRM_Utils_SQL_Insert
*/
public static function into($table) {
return new self($table);
}
/**
* Insert a record based on a DAO.
*
* @param \CRM_Core_DAO $dao
* @return \CRM_Utils_SQL_Insert
* @throws \CRM_Core_Exception
*/
public static function dao(CRM_Core_DAO $dao) {
$table = CRM_Core_DAO::getLocaleTableName($dao->getTableName());
$row = array();
foreach ((array) $dao as $key => $value) {
if ($value === 'null') {
$value = NULL; // Blerg!!!
}
// Skip '_foobar' and '{\u00}*_options' and 'N'.
if (preg_match('/[a-zA-Z]/', $key{0}) && $key !== 'N') {
$row[$key] = $value;
}
}
return self::into($table)->row($row);
}
/**
* Create a new SELECT query.
*
* @param string $table
* Table-name and optional alias.
*/
public function __construct($table) {
$this->table = $table;
$this->rows = array();
}
/**
* Get columns.
*
* @param array $columns
*
* @return CRM_Utils_SQL_Insert
* @throws \CRM_Core_Exception
*/
public function columns($columns) {
if ($this->columns !== NULL) {
throw new CRM_Core_Exception("Column order already specified.");
}
$this->columns = $columns;
return $this;
}
/**
* Get rows.
*
* @param array $rows
*
* @return CRM_Utils_SQL_Insert
*/
public function rows($rows) {
foreach ($rows as $row) {
$this->row($row);
}
return $this;
}
/**
* Get row.
*
* @param array $row
*
* @return CRM_Utils_SQL_Insert
* @throws CRM_Core_Exception
*/
public function row($row) {
$columns = array_keys($row);
if ($this->columns === NULL) {
sort($columns);
$this->columns = $columns;
}
elseif (array_diff($this->columns, $columns) !== array()) {
throw new CRM_Core_Exception("Inconsistent column names");
}
$escapedRow = array();
foreach ($this->columns as $column) {
$escapedRow[$column] = $this->escapeString($row[$column]);
}
$this->rows[] = $escapedRow;
return $this;
}
/**
* Use REPLACE INTO instead of INSERT INTO.
*
* @param bool $asReplace
*
* @return CRM_Utils_SQL_Insert
*/
public function usingReplace($asReplace = TRUE) {
$this->verb = $asReplace ? 'REPLACE INTO' : 'INSERT INTO';
return $this;
}
/**
* Escape string.
*
* @param string|NULL $value
*
* @return string
* SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes)
*/
protected function escapeString($value) {
return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"';
}
/**
* Convert to SQL.
*
* @return string
* SQL statement
*/
public function toSQL() {
$columns = "`" . implode('`,`', $this->columns) . "`";
$sql = "{$this->verb} {$this->table} ({$columns}) VALUES";
$nextDelim = '';
foreach ($this->rows as $row) {
$sql .= "{$nextDelim}\n(" . implode(',', $row) . ")";
$nextDelim = ',';
}
$sql .= "\n";
return $sql;
}
}

View file

@ -0,0 +1,689 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* Dear God Why Do I Have To Write This (Dumb SQL Builder)
*
* Usage:
* @code
* $select = CRM_Utils_SQL_Select::from('civicrm_activity act')
* ->join('absence', 'inner join civicrm_activity absence on absence.id = act.source_record_id')
* ->where('activity_type_id = #type', array('type' => 234))
* ->where('status_id IN (#statuses)', array('statuses' => array(1,2,3))
* ->where('subject like @subj', array('subj' => '%hello%'))
* ->where('!dynamicColumn = 1', array('dynamicColumn' => 'coalesce(is_active,0)'))
* ->where('!column = @value', array(
* 'column' => $customField->column_name,
* 'value' => $form['foo']
* ))
* echo $select->toSQL();
* @endcode
*
* Design principles:
* - Portable
* - No knowledge of the underlying SQL API (except for escaping -- CRM_Core_DAO::escapeString)
* - No knowledge of the underlying data model
* - Single file
* - SQL clauses correspond to PHP functions ($select->where("foo_id=123"))
* - Variable escaping is concise and controllable based on prefixes, eg
* - similar to Drupal's t()
* - use "@varname" to insert the escaped value
* - use "!varname" to insert raw (unescaped) values
* - use "#varname" to insert a numerical value (these are validated but not escaped)
* - to disable any preprocessing, simply omit the variable list
* - control characters (@!#) are mandatory in expressions but optional in arg-keys
* - Variables may be individual values or arrays; arrays are imploded with commas
* - Conditionals are AND'd; if you need OR's, do it yourself
* - Use classes/functions with documentation (rather than undocumented array-trees)
* - For any given string, interpolation is only performed once. After an interpolation,
* a string may never again be subjected to interpolation.
*
* The "interpolate-once" principle can be enforced by either interpolating on input
* xor output. The notations for input and output interpolation are a bit different,
* and they may not be mixed.
*
* @code
* // Interpolate on input. Set params when using them.
* $select->where('activity_type_id = #type', array(
* 'type' => 234,
* ));
*
* // Interpolate on output. Set params independently.
* $select
* ->where('activity_type_id = #type')
* ->param('type', 234),
* @endcode
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_SQL_Select implements ArrayAccess {
/**
* Interpolate values as soon as they are passed in (where(), join(), etc).
*
* Default.
*
* Pro: Every clause has its own unique namespace for parameters.
* Con: Probably slower.
* Advice: Use this when aggregating SQL fragments from agents who
* maintained by different parties.
*/
const INTERPOLATE_INPUT = 'in';
/**
* Interpolate values when rendering SQL output (toSQL()).
*
* Pro: Probably faster.
* Con: Must maintain an aggregated list of all parameters.
* Advice: Use this when you have control over the entire query.
*/
const INTERPOLATE_OUTPUT = 'out';
/**
* Determine mode automatically. When the first attempt is made
* to use input-interpolation (eg `where(..., array(...))`) or
* output-interpolation (eg `param(...)`), the mode will be
* set. Subsequent calls will be validated using the same mode.
*/
const INTERPOLATE_AUTO = 'auto';
private $mode = NULL;
private $insertInto = NULL;
private $insertIntoFields = array();
private $selects = array();
private $from;
private $joins = array();
private $wheres = array();
private $groupBys = array();
private $havings = array();
private $orderBys = array();
private $limit = NULL;
private $offset = NULL;
private $params = array();
private $distinct = NULL;
// Public to work-around PHP 5.3 limit.
public $strict = NULL;
/**
* Create a new SELECT query.
*
* @param string $from
* Table-name and optional alias.
* @param array $options
* @return CRM_Utils_SQL_Select
*/
public static function from($from, $options = array()) {
return new self($from, $options);
}
/**
* Create a partial SELECT query.
*
* @param array $options
* @return CRM_Utils_SQL_Select
*/
public static function fragment($options = array()) {
return new self(NULL, $options);
}
/**
* Create a new SELECT query.
*
* @param string $from
* Table-name and optional alias.
* @param array $options
*/
public function __construct($from, $options = array()) {
$this->from = $from;
$this->mode = isset($options['mode']) ? $options['mode'] : self::INTERPOLATE_AUTO;
}
/**
* Make a new copy of this query.
*
* @return CRM_Utils_SQL_Select
*/
public function copy() {
return clone $this;
}
/**
* Merge something or other.
*
* @param CRM_Utils_SQL_Select $other
* @param array|NULL $parts
* ex: 'joins', 'wheres'
* @return CRM_Utils_SQL_Select
*/
public function merge($other, $parts = NULL) {
if ($other === NULL) {
return $this;
}
if ($this->mode === self::INTERPOLATE_AUTO) {
$this->mode = $other->mode;
}
elseif ($other->mode === self::INTERPOLATE_AUTO) {
// Noop.
}
elseif ($this->mode !== $other->mode) {
// Mixing modes will lead to someone getting an expected substitution.
throw new RuntimeException("Cannot merge queries that use different interpolation modes ({$this->mode} vs {$other->mode}).");
}
$arrayFields = array('insertIntoFields', 'selects', 'joins', 'wheres', 'groupBys', 'havings', 'orderBys', 'params');
foreach ($arrayFields as $f) {
if ($parts === NULL || in_array($f, $parts)) {
$this->{$f} = array_merge($this->{$f}, $other->{$f});
}
}
$flatFields = array('insertInto', 'from', 'limit', 'offset');
foreach ($flatFields as $f) {
if ($parts === NULL || in_array($f, $parts)) {
if ($other->{$f} !== NULL) {
$this->{$f} = $other->{$f};
}
}
}
return $this;
}
/**
* Add a new JOIN clause.
*
* Note: To add multiple JOINs at once, use $name===NULL and
* pass an array of $exprs.
*
* @param string|NULL $name
* The effective alias of the joined table.
* @param string|array $exprs
* The complete join expression (eg "INNER JOIN mytable myalias ON mytable.id = maintable.foo_id").
* @param array|null $args
* @return CRM_Utils_SQL_Select
*/
public function join($name, $exprs, $args = NULL) {
if ($name !== NULL) {
$this->joins[$name] = $this->interpolate($exprs, $args);
}
else {
foreach ($exprs as $name => $expr) {
$this->joins[$name] = $this->interpolate($expr, $args);
}
return $this;
}
return $this;
}
/**
* Specify the column(s)/value(s) to return by adding to the SELECT clause
*
* @param string|array $exprs list of SQL expressions
* @param null|array $args use NULL to disable interpolation; use an array of variables to enable
* @return CRM_Utils_SQL_Select
*/
public function select($exprs, $args = NULL) {
$exprs = (array) $exprs;
foreach ($exprs as $expr) {
$this->selects[] = $this->interpolate($expr, $args);
}
return $this;
}
/**
* Return only distinct values
*
* @param bool $isDistinct allow DISTINCT select or not
* @return CRM_Utils_SQL_Select
*/
public function distinct($isDistinct = TRUE) {
if ($isDistinct) {
$this->distinct = 'DISTINCT ';
}
return $this;
}
/**
* Limit results by adding extra condition(s) to the WHERE clause
*
* @param string|array $exprs list of SQL expressions
* @param null|array $args use NULL to disable interpolation; use an array of variables to enable
* @return CRM_Utils_SQL_Select
*/
public function where($exprs, $args = NULL) {
$exprs = (array) $exprs;
foreach ($exprs as $expr) {
$evaluatedExpr = $this->interpolate($expr, $args);
$this->wheres[$evaluatedExpr] = $evaluatedExpr;
}
return $this;
}
/**
* Group results by adding extra items to the GROUP BY clause.
*
* @param string|array $exprs list of SQL expressions
* @param null|array $args use NULL to disable interpolation; use an array of variables to enable
* @return CRM_Utils_SQL_Select
*/
public function groupBy($exprs, $args = NULL) {
$exprs = (array) $exprs;
foreach ($exprs as $expr) {
$evaluatedExpr = $this->interpolate($expr, $args);
$this->groupBys[$evaluatedExpr] = $evaluatedExpr;
}
return $this;
}
/**
* Limit results by adding extra condition(s) to the HAVING clause
*
* @param string|array $exprs list of SQL expressions
* @param null|array $args use NULL to disable interpolation; use an array of variables to enable
* @return CRM_Utils_SQL_Select
*/
public function having($exprs, $args = NULL) {
$exprs = (array) $exprs;
foreach ($exprs as $expr) {
$evaluatedExpr = $this->interpolate($expr, $args);
$this->havings[$evaluatedExpr] = $evaluatedExpr;
}
return $this;
}
/**
* Sort results by adding extra items to the ORDER BY clause.
*
* @param string|array $exprs list of SQL expressions
* @param null|array $args use NULL to disable interpolation; use an array of variables to enable
* @param int $weight
* @return \CRM_Utils_SQL_Select
*/
public function orderBy($exprs, $args = NULL, $weight = 0) {
static $guid = 0;
$exprs = (array) $exprs;
foreach ($exprs as $expr) {
$evaluatedExpr = $this->interpolate($expr, $args);
$this->orderBys[$evaluatedExpr] = array('value' => $evaluatedExpr, 'weight' => $weight, 'guid' => $guid++);
}
return $this;
}
/**
* Set one (or multiple) parameters to interpolate into the query.
*
* @param array|string $keys
* Key name, or an array of key-value pairs.
* @param null|mixed $value
* The new value of the parameter.
* Values may be strings, ints, or arrays thereof -- provided that the
* SQL query uses appropriate prefix (e.g. "@", "!", "#").
* @return \CRM_Utils_SQL_Select
*/
public function param($keys, $value = NULL) {
if ($this->mode === self::INTERPOLATE_AUTO) {
$this->mode = self::INTERPOLATE_OUTPUT;
}
elseif ($this->mode !== self::INTERPOLATE_OUTPUT) {
throw new RuntimeException("Select::param() only makes sense when interpolating on output.");
}
if (is_array($keys)) {
foreach ($keys as $k => $v) {
$this->params[$k] = $v;
}
}
else {
$this->params[$keys] = $value;
}
return $this;
}
/**
* Set a limit on the number of records to return.
*
* @param int $limit
* @param int $offset
* @return CRM_Utils_SQL_Select
* @throws CRM_Core_Exception
*/
public function limit($limit, $offset = 0) {
if ($limit !== NULL && !is_numeric($limit)) {
throw new CRM_Core_Exception("Illegal limit");
}
if ($offset !== NULL && !is_numeric($offset)) {
throw new CRM_Core_Exception("Illegal offset");
}
$this->limit = $limit;
$this->offset = $offset;
return $this;
}
/**
* Insert the results of the SELECT query into another
* table.
*
* @param string $table
* The name of the other table (which receives new data).
* @param array $fields
* The fields to fill in the other table (in order).
* @return CRM_Utils_SQL_Select
* @see insertIntoField
*/
public function insertInto($table, $fields = array()) {
$this->insertInto = $table;
$this->insertIntoField($fields);
return $this;
}
/**
* @param array $fields
* The fields to fill in the other table (in order).
* @return CRM_Utils_SQL_Select
*/
public function insertIntoField($fields) {
$fields = (array) $fields;
foreach ($fields as $field) {
$this->insertIntoFields[] = $field;
}
return $this;
}
/**
* @param array|NULL $parts
* List of fields to check (e.g. 'selects', 'joins').
* Defaults to all.
* @return bool
*/
public function isEmpty($parts = NULL) {
$empty = TRUE;
$fields = array(
'insertInto',
'insertIntoFields',
'selects',
'from',
'joins',
'wheres',
'groupBys',
'havings',
'orderBys',
'limit',
'offset',
);
if ($parts !== NULL) {
$fields = array_intersect($fields, $parts);
}
foreach ($fields as $field) {
if (!empty($this->{$field})) {
$empty = FALSE;
}
}
return $empty;
}
/**
* Enable (or disable) strict mode.
*
* In strict mode, unknown variables will generate exceptions.
*
* @param bool $strict
* @return CRM_Utils_SQL_Select
*/
public function strict($strict = TRUE) {
$this->strict = $strict;
return $this;
}
/**
* Given a string like "field_name = @value", replace "@value" with an escaped SQL string
*
* @param string $expr SQL expression
* @param null|array $args a list of values to insert into the SQL expression; keys are prefix-coded:
* prefix '@' => escape SQL
* prefix '#' => literal number, skip escaping but do validation
* prefix '!' => literal, skip escaping and validation
* if a value is an array, then it will be imploded
*
* PHP NULL's will be treated as SQL NULL's. The PHP string "null" will be treated as a string.
*
* @param string $activeMode
*
* @return string
*/
public function interpolate($expr, $args, $activeMode = self::INTERPOLATE_INPUT) {
if ($args === NULL) {
return $expr;
}
else {
if ($this->mode === self::INTERPOLATE_AUTO) {
$this->mode = $activeMode;
}
elseif ($activeMode !== $this->mode) {
throw new RuntimeException("Cannot mix interpolation modes.");
}
$select = $this;
return preg_replace_callback('/([#!@])([a-zA-Z0-9_]+)/', function($m) use ($select, $args) {
if (isset($args[$m[2]])) {
$values = $args[$m[2]];
}
elseif (isset($args[$m[1] . $m[2]])) {
// Backward compat. Keys in $args look like "#myNumber" or "@myString".
$values = $args[$m[1] . $m[2]];
}
elseif ($select->strict) {
throw new CRM_Core_Exception('Cannot build query. Variable "' . $m[1] . $m[2] . '" is unknown.');
}
else {
// Unrecognized variables are ignored. Mitigate risk of accidents.
return $m[0];
}
$values = is_array($values) ? $values : array($values);
switch ($m[1]) {
case '@':
$parts = array_map(array($select, 'escapeString'), $values);
return implode(', ', $parts);
// TODO: ensure all uses of this un-escaped literal are safe
case '!':
return implode(', ', $values);
case '#':
foreach ($values as $valueKey => $value) {
if ($value === NULL) {
$values[$valueKey] = 'NULL';
}
elseif (!is_numeric($value)) {
//throw new API_Exception("Failed encoding non-numeric value" . var_export(array($m[0] => $values), TRUE));
throw new CRM_Core_Exception("Failed encoding non-numeric value (" . $m[0] . ")");
}
}
return implode(', ', $values);
default:
throw new CRM_Core_Exception("Unrecognized prefix");
}
}, $expr);
}
}
/**
* @param string|NULL $value
* @return string
* SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes)
*/
public function escapeString($value) {
return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"';
}
/**
* @return string
* SQL statement
*/
public function toSQL() {
$sql = '';
if ($this->insertInto) {
$sql .= 'INSERT INTO ' . $this->insertInto . ' (';
$sql .= implode(', ', $this->insertIntoFields);
$sql .= ")\n";
}
if ($this->selects) {
$sql .= 'SELECT ' . $this->distinct . implode(', ', $this->selects) . "\n";
}
else {
$sql .= 'SELECT *' . "\n";
}
if ($this->from !== NULL) {
$sql .= 'FROM ' . $this->from . "\n";
}
foreach ($this->joins as $join) {
$sql .= $join . "\n";
}
if ($this->wheres) {
$sql .= 'WHERE (' . implode(') AND (', $this->wheres) . ")\n";
}
if ($this->groupBys) {
$sql .= 'GROUP BY ' . implode(', ', $this->groupBys) . "\n";
}
if ($this->havings) {
$sql .= 'HAVING (' . implode(') AND (', $this->havings) . ")\n";
}
if ($this->orderBys) {
$orderBys = CRM_Utils_Array::crmArraySortByField($this->orderBys,
array('weight', 'guid'));
$orderBys = CRM_Utils_Array::collect('value', $orderBys);
$sql .= 'ORDER BY ' . implode(', ', $orderBys) . "\n";
}
if ($this->limit !== NULL) {
$sql .= 'LIMIT ' . $this->limit . "\n";
if ($this->offset !== NULL) {
$sql .= 'OFFSET ' . $this->offset . "\n";
}
}
if ($this->mode === self::INTERPOLATE_OUTPUT) {
$sql = $this->interpolate($sql, $this->params, self::INTERPOLATE_OUTPUT);
}
return $sql;
}
/**
* Execute the query.
*
* To examine the results, use a function like `fetch()`, `fetchAll()`,
* `fetchValue()`, or `fetchMap()`.
*
* @param string|NULL $daoName
* The return object should be an instance of this class.
* Ex: 'CRM_Contact_BAO_Contact'.
* @param bool $i18nRewrite
* If the system has multilingual features, should the field/table
* names be rewritten?
* @return CRM_Core_DAO
* @see CRM_Core_DAO::executeQuery
* @see CRM_Core_I18n_Schema::rewriteQuery
*/
public function execute($daoName = NULL, $i18nRewrite = TRUE) {
// Don't pass through $params. toSQL() handles interpolation.
$params = array();
// Don't pass through $abort, $trapException. Just use straight-up exceptions.
$abort = TRUE;
$trapException = FALSE;
$errorScope = CRM_Core_TemporaryErrorScope::useException();
// Don't pass through freeDAO. You can do it yourself.
$freeDAO = FALSE;
return CRM_Core_DAO::executeQuery($this->toSQL(), $params, $abort, $daoName,
$freeDAO, $i18nRewrite, $trapException);
}
/**
* Has an offset been set.
*
* @param string $offset
*
* @return bool
*/
public function offsetExists($offset) {
return isset($this->params[$offset]);
}
/**
* Get the value of a SQL parameter.
*
* @code
* $select['cid'] = 123;
* $select->where('contact.id = #cid');
* echo $select['cid'];
* @endCode
*
* @param string $offset
* @return mixed
* @see param()
* @see ArrayAccess::offsetGet
*/
public function offsetGet($offset) {
return $this->params[$offset];
}
/**
* Set the value of a SQL parameter.
*
* @code
* $select['cid'] = 123;
* $select->where('contact.id = #cid');
* echo $select['cid'];
* @endCode
*
* @param string $offset
* @param mixed $value
* The new value of the parameter.
* Values may be strings, ints, or arrays thereof -- provided that the
* SQL query uses appropriate prefix (e.g. "@", "!", "#").
* @see param()
* @see ArrayAccess::offsetSet
*/
public function offsetSet($offset, $value) {
$this->param($offset, $value);
}
/**
* Unset the value of a SQL parameter.
*
* @param string $offset
* @see param()
* @see ArrayAccess::offsetUnset
*/
public function offsetUnset($offset) {
unset($this->params[$offset]);
}
}

View file

@ -0,0 +1,139 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* A utility which signs and verifies a list of key-value pairs
*
* FIXME: Add TTL support?
*
* @code
* $signer = new CRM_Utils_Signer('myprivatekey', array('param1','param2'));
* $params = array(
* 'param1' => 'hello',
* 'param2' => 'world',
* );
* $token = $signer->sign($params);
* ...
* assertTrue($signer->validate($token, $params));
* @endcode
*/
class CRM_Utils_Signer {
/**
* Expected length of the salt
*
* @var int
*/
const SALT_LEN = 4;
/**
* Instantiate a signature-processor
*
* @param string $secret
* private.
* @param array $paramNames
* Array, fields which should be part of the signature.
*/
public function __construct($secret, $paramNames) {
sort($paramNames); // ensure consistent serialization of payloads
$this->secret = $secret;
$this->paramNames = $paramNames;
$this->signDelim = "_"; // chosen to be valid in URLs but not in salt or md5
$this->defaultSalt = CRM_Utils_String::createRandom(self::SALT_LEN, CRM_Utils_String::ALPHANUMERIC);
}
/**
* Generate a signature for a set of key-value pairs
*
* @param array $params
* Array, key-value pairs.
* @param string $salt
* the salt (if known) or NULL (for auto-generated).
* @return string, the full public token representing the signature
*/
public function sign($params, $salt = NULL) {
$message = array();
$message['secret'] = $this->secret;
$message['payload'] = array();
if (empty($salt)) {
$message['salt'] = $this->createSalt();
}
else {
$message['salt'] = $salt;
}
// recall: paramNames is pre-sorted for stability
foreach ($this->paramNames as $paramName) {
if (isset($params[$paramName])) {
if (is_numeric($params[$paramName])) {
$params[$paramName] = (string) $params[$paramName];
}
}
else {
// $paramName is not included or ===NULL
$params[$paramName] = '';
}
$message['payload'][$paramName] = $params[$paramName];
}
$token = $message['salt'] . $this->signDelim . md5(serialize($message));
return $token;
}
/**
* Determine whether a token represents a proper signature for $params
*
* @param string $token
* the full public token representing the signature.
* @param array $params
* Array, key-value pairs.
*
* @throws Exception
* @return bool, TRUE iff all $paramNames for the submitted validate($params) and the original sign($params)
*/
public function validate($token, $params) {
list ($salt, $signature) = explode($this->signDelim, $token);
if (strlen($salt) != self::SALT_LEN) {
throw new Exception("Token contains invalid salt [" . urlencode($token) . "]");
}
$newToken = $this->sign($params, $salt);
return ($token == $newToken);
}
/**
* @return string
*/
public function createSalt() {
// It would be more secure to generate a new value but liable to run this
// many times on certain admin pages; so instead we'll re-use the hash.
return $this->defaultSalt;
}
}

View file

@ -0,0 +1,345 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
* This class handles all SOAP client requests.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_SoapServer {
/**
* Number of seconds we should let a soap process idle
*/
static $soap_timeout = 0;
/**
* Cache the actual UF Class
*/
public $ufClass;
/**
* Class constructor. This caches the real user framework class locally,
* so we can use it for authentication and validation.
*
* @internal param string $uf The userframework class
*/
public function __construct() {
// any external program which call SoapServer is responsible for
// creating and attaching the session
$args = func_get_args();
$this->ufClass = array_shift($args);
}
/**
* Simple ping function to test for liveness.
*
* @param string $var
* The string to be echoed.
*
* @return string
*/
public function ping($var) {
$session = CRM_Core_Session::singleton();
$key = $session->get('key');
$session->set('key', $var);
return "PONG: $var ($key)";
}
/**
* Verify a SOAP key.
*
* @param string $key
* The soap key generated by authenticate().
*
* @throws SoapFault
*/
public function verify($key) {
$session = CRM_Core_Session::singleton();
$soap_key = $session->get('soap_key');
$t = time();
if ($key !== sha1($soap_key)) {
throw new SoapFault('Client', 'Invalid key');
}
if (self::$soap_timeout &&
$t > ($session->get('soap_time') + self::$soap_timeout)
) {
throw new SoapFault('Client', 'Expired key');
}
// otherwise, we're ok. update the timestamp
$session->set('soap_time', $t);
}
/**
* Authentication wrapper to the UF Class.
*
* @param string $name
* Login name.
* @param string $pass
* Password.
*
* @param bool $loadCMSBootstrap
*
* @throws SoapFault
* @return string
* The SOAP Client key
*/
public function authenticate($name, $pass, $loadCMSBootstrap = FALSE) {
require_once str_replace('_', DIRECTORY_SEPARATOR, $this->ufClass) . '.php';
if ($this->ufClass == 'CRM_Utils_System_Joomla'
|| $this->ufClass == 'CRM_Utils_System_WordPress') {
$loadCMSBootstrap = TRUE;
}
$result = CRM_Utils_System::authenticate($name, $pass, $loadCMSBootstrap);
if (empty($result)) {
throw new SoapFault('Client', 'Invalid login');
}
$session = CRM_Core_Session::singleton();
$session->set('soap_key', $result[2]);
$session->set('soap_time', time());
return sha1($result[2]);
}
/**
* MAILER API.
*
* @param string $key
* @param int $job
* @param int $queue
* @param string $hash
* @param string $body
*
* @return array|int
* @throws \SoapFault
*/
public function mailer_event_bounce($key, $job, $queue, $hash, $body) {
$this->verify($key);
$params = array(
'job_id' => $job,
'time_stamp' => date('YmdHis'),
'event_queue_id' => $queue,
'hash' => $hash,
'body' => $body,
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_bounce', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* Mailer event unsubscribe.
*
* @param string $key
* @param int $job
* @param int $queue
* @param string $hash
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_unsubscribe($key, $job, $queue, $hash) {
$this->verify($key);
$params = array(
'job_id' => $job,
'time_stamp' => date('YmdHis'),
'org_unsubscribe' => 0,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_unsubscribe', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $job
* @param $queue
* @param $hash
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_domain_unsubscribe($key, $job, $queue, $hash) {
$this->verify($key);
$params = array(
'job_id' => $job,
'time_stamp' => date('YmdHis'),
'org_unsubscribe' => 1,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_domain_unsubscribe', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $job
* @param $queue
* @param $hash
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_resubscribe($key, $job, $queue, $hash) {
$this->verify($key);
$params = array(
'job_id' => $job,
'time_stamp' => date('YmdHis'),
'org_unsubscribe' => 0,
'event_queue_id' => $queue,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_resubscribe', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $email
* @param $domain
* @param $group
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_subscribe($key, $email, $domain, $group) {
$this->verify($key);
$params = array(
'email' => $email,
'group_id' => $group,
'version' => 3,
);
$result = civicrm_api('MailingGroup', 'event_subscribe', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $contact
* @param $subscribe
* @param $hash
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_confirm($key, $contact, $subscribe, $hash) {
$this->verify($key);
$params = array(
'contact_id' => $contact,
'subscribe_id' => $subscribe,
'time_stamp' => date('YmdHis'),
'event_subscribe_id' => $subscribe,
'hash' => $hash,
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_confirm', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $job
* @param $queue
* @param $hash
* @param $bodyTxt
* @param $rt
* @param null $bodyHTML
* @param null $fullEmail
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_reply($key, $job, $queue, $hash, $bodyTxt, $rt, $bodyHTML = NULL, $fullEmail = NULL) {
$this->verify($key);
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'bodyTxt' => $bodyTxt,
'replyTo' => $rt,
'bodyHTML' => $bodyHTML,
'fullEmail' => $fullEmail,
'time_stamp' => date('YmdHis'),
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_reply', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param $job
* @param $queue
* @param $hash
* @param $email
*
* @return array|int
* @throws SoapFault
*/
public function mailer_event_forward($key, $job, $queue, $hash, $email) {
$this->verify($key);
$params = array(
'job_id' => $job,
'event_queue_id' => $queue,
'hash' => $hash,
'email' => $email,
'version' => 3,
);
$result = civicrm_api('Mailing', 'event_forward', $params);
return CRM_Utils_Array::encode_items($result);
}
/**
* @param $key
* @param array $params
*
* @return array|int
* @throws SoapFault
*/
public function get_contact($key, $params) {
$this->verify($key);
$params['version'] = 3;
$result = civicrm_api('contact', 'get', $params);
return CRM_Utils_Array::encode_items($result);
}
}

View file

@ -0,0 +1,293 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* Base class to provide generic sort functionality.
*
* Note that some ideas have been borrowed from the drupal tablesort.inc code.
*
* Also note that since the Pager and Sort class are similar, do match the function names
* if introducing additional functionality
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
class CRM_Utils_Sort {
/**
* Constants to determine what direction each variable
* is to be sorted
*
* @var int
*/
const ASCENDING = 1, DESCENDING = 2, DONTCARE = 4,
/**
* The name for the sort GET/POST param
*
* @var string
*/
SORT_ID = 'crmSID', SORT_DIRECTION = 'crmSortDirection', SORT_ORDER = 'crmSortOrder';
/**
* Name of the sort function. Used to isolate session variables
* @var string
*/
protected $_name;
/**
* Array of variables that influence the query
*
* @var array
*/
public $_vars;
/**
* The newly formulated base url to be used as links
* for various table elements
*
* @var string
*/
protected $_link;
/**
* What's the name of the sort variable in a REQUEST
*
* @var string
*/
protected $_urlVar;
/**
* What variable are we currently sorting on
*
* @var string
*/
protected $_currentSortID;
/**
* What direction are we sorting on
*
* @var string
*/
protected $_currentSortDirection;
/**
* The output generated for the current form
*
* @var array
*/
public $_response;
/**
* The constructor takes an assoc array
* key names of variable (which should be the same as the column name)
* value: ascending or descending
*
* @param mixed $vars
* Assoc array as described above.
* @param string $defaultSortOrder
* Order to sort.
*
* @return \CRM_Utils_Sort
*/
public function __construct(&$vars, $defaultSortOrder = NULL) {
$this->_vars = array();
$this->_response = array();
foreach ($vars as $weight => $value) {
$this->_vars[$weight] = array(
'name' => CRM_Utils_Type::validate($value['sort'], 'MysqlColumnNameOrAlias'),
'direction' => CRM_Utils_Array::value('direction', $value),
'title' => $value['name'],
);
}
$this->_currentSortID = 1;
if (isset($this->_vars[$this->_currentSortID])) {
$this->_currentSortDirection = $this->_vars[$this->_currentSortID]['direction'];
}
$this->_urlVar = self::SORT_ID;
$this->_link = CRM_Utils_System::makeURL($this->_urlVar, TRUE);
$this->initialize($defaultSortOrder);
}
/**
* Function returns the string for the order by clause.
*
* @return string
* the order by clause
*/
public function orderBy() {
if (empty($this->_vars[$this->_currentSortID])) {
return '';
}
if ($this->_vars[$this->_currentSortID]['direction'] == self::ASCENDING ||
$this->_vars[$this->_currentSortID]['direction'] == self::DONTCARE
) {
$this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' asc';
}
else {
$this->_vars[$this->_currentSortID]['name'] = str_replace(' ', '_', $this->_vars[$this->_currentSortID]['name']);
return CRM_Utils_Type::escape($this->_vars[$this->_currentSortID]['name'], 'MysqlColumnNameOrAlias') . ' desc';
}
}
/**
* Create the sortID string to be used in the GET param.
*
* @param int $index
* The field index.
* @param int $dir
* The direction of the sort.
*
* @return string
* the string to append to the url
*/
public static function sortIDValue($index, $dir) {
return ($dir == self::DESCENDING) ? $index . '_d' : $index . '_u';
}
/**
* Init the sort ID values in the object.
*
* @param string $defaultSortOrder
* The sort order to use by default.
*/
public function initSortID($defaultSortOrder) {
$url = CRM_Utils_Array::value(self::SORT_ID, $_GET, $defaultSortOrder);
if (empty($url)) {
return;
}
list($current, $direction) = explode('_', $url);
// if current is weird and does not exist in the vars array, skip
if (!array_key_exists($current, $this->_vars)) {
return;
}
if ($direction == 'u') {
$direction = self::ASCENDING;
}
elseif ($direction == 'd') {
$direction = self::DESCENDING;
}
else {
$direction = self::DONTCARE;
}
$this->_currentSortID = $current;
$this->_currentSortDirection = $direction;
$this->_vars[$current]['direction'] = $direction;
}
/**
* Init the object.
*
* @param string $defaultSortOrder
* The sort order to use by default.
*/
public function initialize($defaultSortOrder) {
$this->initSortID($defaultSortOrder);
$this->_response = array();
$current = $this->_currentSortID;
foreach ($this->_vars as $index => $item) {
$name = $item['name'];
$this->_response[$name] = array();
$newDirection = ($item['direction'] == self::ASCENDING) ? self::DESCENDING : self::ASCENDING;
if ($current == $index) {
if ($item['direction'] == self::ASCENDING) {
$class = 'sorting_asc';
}
else {
$class = 'sorting_desc';
}
}
else {
$class = 'sorting';
}
$this->_response[$name]['link'] = '<a href="' . $this->_link . $this->sortIDValue($index, $newDirection) . '" class="' . $class . '">' . $item['title'] . '</a>';
}
}
/**
* Getter for currentSortID.
*
* @return int
* returns of the current sort id
*/
public function getCurrentSortID() {
return $this->_currentSortID;
}
/**
* Getter for currentSortDirection.
*
* @return int
* returns of the current sort direction
*/
public function getCurrentSortDirection() {
return $this->_currentSortDirection;
}
/**
* Universal callback function for sorting by weight, id, title or name
*
* @param $a
* @param $b
*
* @return int
* (-1 or 1)
*/
public static function cmpFunc($a, $b) {
$cmp_order = array('weight', 'id', 'title', 'name');
foreach ($cmp_order as $attribute) {
if (isset($a[$attribute]) && isset($b[$attribute])) {
if ($a[$attribute] < $b[$attribute]) {
return -1;
}
elseif ($a[$attribute] > $b[$attribute]) {
return 1;
} // else: $a and $b are equal wrt to this attribute, try next...
}
}
// if we get here, $a and $b are equal for all we know
// however, as I understand we don't want equality here:
return -1;
}
}

View file

@ -0,0 +1,943 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'HTML/QuickForm/Rule/Email.php';
/**
* This class contains string functions.
*/
class CRM_Utils_String {
const COMMA = ",", SEMICOLON = ";", SPACE = " ", TAB = "\t", LINEFEED = "\n", CARRIAGELINE = "\r\n", LINECARRIAGE = "\n\r", CARRIAGERETURN = "\r";
/**
* List of all letters and numbers
*/
const ALPHANUMERIC = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
/**
* Convert a display name into a potential variable name.
*
* @param string $title title of the string
* @param int $maxLength
*
* @return string
* An equivalent variable name.
*/
public static function titleToVar($title, $maxLength = 31) {
$variable = self::munge($title, '_', $maxLength);
if (CRM_Utils_Rule::title($variable, $maxLength)) {
return $variable;
}
// if longer than the maxLength lets just return a substr of the
// md5 to prevent errors downstream
return substr(md5($title), 0, $maxLength);
}
/**
* Replace all non alpha numeric characters and spaces with the replacement character.
*
* @param string $name
* The name to be worked on.
* @param string $char
* The character to use for non-valid chars.
* @param int $len
* Length of valid variables.
*
* @return string
* returns the manipulated string
*/
public static function munge($name, $char = '_', $len = 63) {
// Replace all white space and non-alpha numeric with $char
// we only use the ascii character set since mysql does not create table names / field names otherwise
// CRM-11744
$name = preg_replace('/[^a-zA-Z0-9]+/', $char, trim($name));
//If there are no ascii characters present.
if ($name == $char) {
$name = self::createRandom($len, self::ALPHANUMERIC);
}
if ($len) {
// lets keep variable names short
return substr($name, 0, $len);
}
else {
return $name;
}
}
/**
* Convert possibly underscore separated words to camel case with special handling for 'UF'
* e.g membership_payment returns MembershipPayment
*
* @param string $string
*
* @return string
*/
public static function convertStringToCamel($string) {
$map = array(
'acl' => 'Acl',
'ACL' => 'Acl',
'im' => 'Im',
'IM' => 'Im',
);
if (isset($map[$string])) {
return $map[$string];
}
$fragments = explode('_', $string);
foreach ($fragments as & $fragment) {
$fragment = ucfirst($fragment);
}
// Special case: UFGroup, UFJoin, UFMatch, UFField
if ($fragments[0] === 'Uf') {
$fragments[0] = 'UF';
}
return implode('', $fragments);
}
/**
* Takes a variable name and munges it randomly into another variable name.
*
* @param string $name
* Initial Variable Name.
* @param int $len
* Length of valid variables.
*
* @return string
* Randomized Variable Name
*/
public static function rename($name, $len = 4) {
$rand = substr(uniqid(), 0, $len);
return substr_replace($name, $rand, -$len, $len);
}
/**
* Takes a string and returns the last tuple of the string.
*
* Useful while converting file names to class names etc
*
* @param string $string
* The input string.
* @param string $char
* Character used to demarcate the components
*
* @return string
* The last component
*/
public static function getClassName($string, $char = '_') {
$names = array();
if (!is_array($string)) {
$names = explode($char, $string);
}
if (!empty($names)) {
return array_pop($names);
}
}
/**
* Appends a name to a string and separated by delimiter.
*
* Does the right thing for an empty string
*
* @param string $str
* The string to be appended to.
* @param string $delim
* The delimiter to use.
* @param mixed $name
* The string (or array of strings) to append.
*/
public static function append(&$str, $delim, $name) {
if (empty($name)) {
return;
}
if (is_array($name)) {
foreach ($name as $n) {
if (empty($n)) {
continue;
}
if (empty($str)) {
$str = $n;
}
else {
$str .= $delim . $n;
}
}
}
else {
if (empty($str)) {
$str = $name;
}
else {
$str .= $delim . $name;
}
}
}
/**
* Determine if the string is composed only of ascii characters.
*
* @param string $str
* Input string.
* @param bool $utf8
* Attempt utf8 match on failure (default yes).
*
* @return bool
* true if string is ascii
*/
public static function isAscii($str, $utf8 = TRUE) {
if (!function_exists('mb_detect_encoding')) {
// eliminate all white space from the string
$str = preg_replace('/\s+/', '', $str);
// FIXME: This is a pretty brutal hack to make utf8 and 8859-1 work.
// match low- or high-ascii characters
if (preg_match('/[\x00-\x20]|[\x7F-\xFF]/', $str)) {
// || // low ascii characters
// high ascii characters
// preg_match( '/[\x7F-\xFF]/', $str ) ) {
if ($utf8) {
// if we did match, try for utf-8, or iso8859-1
return self::isUtf8($str);
}
else {
return FALSE;
}
}
return TRUE;
}
else {
$order = array('ASCII');
if ($utf8) {
$order[] = 'UTF-8';
}
$enc = mb_detect_encoding($str, $order, TRUE);
return ($enc == 'ASCII' || $enc == 'UTF-8');
}
}
/**
* Determine the string replacements for redaction.
* on the basis of the regular expressions
*
* @param string $str
* Input string.
* @param array $regexRules
* Regular expression to be matched w/ replacements.
*
* @return array
* array of strings w/ corresponding redacted outputs
*/
public static function regex($str, $regexRules) {
// redact the regular expressions
if (!empty($regexRules) && isset($str)) {
static $matches, $totalMatches, $match = array();
foreach ($regexRules as $pattern => $replacement) {
preg_match_all($pattern, $str, $matches);
if (!empty($matches[0])) {
if (empty($totalMatches)) {
$totalMatches = $matches[0];
}
else {
$totalMatches = array_merge($totalMatches, $matches[0]);
}
$match = array_flip($totalMatches);
}
}
}
if (!empty($match)) {
foreach ($match as $matchKey => & $dontCare) {
foreach ($regexRules as $pattern => $replacement) {
if (preg_match($pattern, $matchKey)) {
$dontCare = $replacement . substr(md5($matchKey), 0, 5);
break;
}
}
}
return $match;
}
return CRM_Core_DAO::$_nullArray;
}
/**
* @param $str
* @param $stringRules
*
* @return mixed
*/
public static function redaction($str, $stringRules) {
// redact the strings
if (!empty($stringRules)) {
foreach ($stringRules as $match => $replace) {
$str = str_ireplace($match, $replace, $str);
}
}
// return the redacted output
return $str;
}
/**
* Determine if a string is composed only of utf8 characters
*
* @param string $str
* Input string.
*
* @return bool
*/
public static function isUtf8($str) {
if (!function_exists(mb_detect_encoding)) {
// eliminate all white space from the string
$str = preg_replace('/\s+/', '', $str);
// pattern stolen from the php.net function documentation for
// utf8decode();
// comment by JF Sebastian, 30-Mar-2005
return preg_match('/^([\x00-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xec][\x80-\xbf]{2}|\xed[\x80-\x9f][\x80-\xbf]|[\xee-\xef][\x80-\xbf]{2}|f0[\x90-\xbf][\x80-\xbf]{2}|[\xf1-\xf3][\x80-\xbf]{3}|\xf4[\x80-\x8f][\x80-\xbf]{2})*$/', $str);
// ||
// iconv('ISO-8859-1', 'UTF-8', $str);
}
else {
$enc = mb_detect_encoding($str, array('UTF-8'), TRUE);
return ($enc !== FALSE);
}
}
/**
* Determine if two hrefs are equivalent (fuzzy match)
*
* @param string $url1
* The first url to be matched.
* @param string $url2
* The second url to be matched against.
*
* @return bool
* true if the urls match, else false
*/
public static function match($url1, $url2) {
$url1 = strtolower($url1);
$url2 = strtolower($url2);
$url1Str = parse_url($url1);
$url2Str = parse_url($url2);
if ($url1Str['path'] == $url2Str['path'] &&
self::extractURLVarValue(CRM_Utils_Array::value('query', $url1Str)) == self::extractURLVarValue(CRM_Utils_Array::value('query', $url2Str))
) {
return TRUE;
}
return FALSE;
}
/**
* Extract the civicrm path from the url.
*
* @param string $query
* A url string.
*
* @return string|null
* civicrm url (eg: civicrm/contact/search)
*/
public static function extractURLVarValue($query) {
$config = CRM_Core_Config::singleton();
$urlVar = $config->userFrameworkURLVar;
$params = explode('&', $query);
foreach ($params as $p) {
if (strpos($p, '=')) {
list($k, $v) = explode('=', $p);
if ($k == $urlVar) {
return $v;
}
}
}
return NULL;
}
/**
* Translate a true/false/yes/no string to a 0 or 1 value
*
* @param string $str
* The string to be translated.
*
* @return bool
*/
public static function strtobool($str) {
if (!is_scalar($str)) {
return FALSE;
}
if (preg_match('/^(y(es)?|t(rue)?|1)$/i', $str)) {
return TRUE;
}
return FALSE;
}
/**
* Returns string '1' for a true/yes/1 string, and '0' for no/false/0 else returns false
*
* @param string $str
* The string to be translated.
*
* @return bool
*/
public static function strtoboolstr($str) {
if (!is_scalar($str)) {
return FALSE;
}
if (preg_match('/^(y(es)?|t(rue)?|1)$/i', $str)) {
return '1';
}
elseif (preg_match('/^(n(o)?|f(alse)?|0)$/i', $str)) {
return '0';
}
else {
return FALSE;
}
}
/**
* Convert a HTML string into a text one using html2text
*
* @param string $html
* The string to be converted.
*
* @return string
* the converted string
*/
public static function htmlToText($html) {
require_once 'packages/html2text/rcube_html2text.php';
$token_html = preg_replace('!\{([a-z_.]+)\}!i', 'token:{$1}', $html);
$converter = new rcube_html2text($token_html);
$token_text = $converter->get_text();
$text = preg_replace('!token\:\{([a-z_.]+)\}!i', '{$1}', $token_text);
return $text;
}
/**
* @param $string
* @param array $params
*/
public static function extractName($string, &$params) {
$name = trim($string);
if (empty($name)) {
return;
}
// strip out quotes
$name = str_replace('"', '', $name);
$name = str_replace('\'', '', $name);
// check for comma in name
if (strpos($name, ',') !== FALSE) {
// name has a comma - assume lname, fname [mname]
$names = explode(',', $name);
if (count($names) > 1) {
$params['last_name'] = trim($names[0]);
// check for space delim
$fnames = explode(' ', trim($names[1]));
if (count($fnames) > 1) {
$params['first_name'] = trim($fnames[0]);
$params['middle_name'] = trim($fnames[1]);
}
else {
$params['first_name'] = trim($fnames[0]);
}
}
else {
$params['first_name'] = trim($names[0]);
}
}
else {
// name has no comma - assume fname [mname] fname
$names = explode(' ', $name);
if (count($names) == 1) {
$params['first_name'] = $names[0];
}
elseif (count($names) == 2) {
$params['first_name'] = $names[0];
$params['last_name'] = $names[1];
}
else {
$params['first_name'] = $names[0];
$params['middle_name'] = $names[1];
$params['last_name'] = $names[2];
}
}
}
/**
* @param $string
*
* @return array
*/
public static function &makeArray($string) {
$string = trim($string);
$values = explode("\n", $string);
$result = array();
foreach ($values as $value) {
list($n, $v) = CRM_Utils_System::explode('=', $value, 2);
if (!empty($v)) {
$result[trim($n)] = trim($v);
}
}
return $result;
}
/**
* Given an ezComponents-parsed representation of
* a text with alternatives return only the first one
*
* @param string $full
* All alternatives as a long string (or some other text).
*
* @return string
* only the first alternative found (or the text without alternatives)
*/
public static function stripAlternatives($full) {
$matches = array();
preg_match('/-ALTERNATIVE ITEM 0-(.*?)-ALTERNATIVE ITEM 1-.*-ALTERNATIVE END-/s', $full, $matches);
if (isset($matches[1]) &&
trim(strip_tags($matches[1])) != ''
) {
return $matches[1];
}
else {
return $full;
}
}
/**
* Strip leading, trailing, double spaces from string
* used for postal/greeting/addressee
*
* @param string $string
* Input string to be cleaned.
*
* @return string
* the cleaned string
*/
public static function stripSpaces($string) {
return (empty($string)) ? $string : preg_replace("/\s{2,}/", " ", trim($string));
}
/**
* clean the URL 'path' variable that we use
* to construct CiviCRM urls by removing characters from the path variable
*
* @param string $string
* The input string to be sanitized.
* @param array $search
* The characters to be sanitized.
* @param string $replace
* The character to replace it with.
*
* @return string
* the sanitized string
*/
public static function stripPathChars(
$string,
$search = NULL,
$replace = NULL
) {
static $_searchChars = NULL;
static $_replaceChar = NULL;
if (empty($string)) {
return $string;
}
if ($_searchChars == NULL) {
$_searchChars = array(
'&',
';',
',',
'=',
'$',
'"',
"'",
'\\',
'<',
'>',
'(',
')',
' ',
"\r",
"\r\n",
"\n",
"\t",
);
$_replaceChar = '_';
}
if ($search == NULL) {
$search = $_searchChars;
}
if ($replace == NULL) {
$replace = $_replaceChar;
}
return str_replace($search, $replace, $string);
}
/**
* Use HTMLPurifier to clean up a text string and remove any potential
* xss attacks. This is primarily used in public facing pages which
* accept html as the input string
*
* @param string $string
* The input string.
*
* @return string
* the cleaned up string
*/
public static function purifyHTML($string) {
static $_filter = NULL;
if (!$_filter) {
$config = HTMLPurifier_Config::createDefault();
$config->set('Core.Encoding', 'UTF-8');
// Disable the cache entirely
$config->set('Cache.DefinitionImpl', NULL);
$_filter = new HTMLPurifier($config);
}
return $_filter->purify($string);
}
/**
* Truncate $string; if $string exceeds $maxLen, place "..." at the end
*
* @param string $string
* @param int $maxLen
*
* @return string
*/
public static function ellipsify($string, $maxLen) {
$len = strlen($string);
if ($len <= $maxLen) {
return $string;
}
else {
$end = $maxLen - 3;
while (strlen($string) > $maxLen - 3) {
$string = mb_substr($string, 0, $end, 'UTF-8');
$end = $end - 1;
}
return $string . '...';
}
}
/**
* Generate a random string.
*
* @param $len
* @param $alphabet
* @return string
*/
public static function createRandom($len, $alphabet) {
$alphabetSize = strlen($alphabet);
$result = '';
for ($i = 0; $i < $len; $i++) {
$result .= $alphabet{rand(1, $alphabetSize) - 1};
}
return $result;
}
/**
* Examples:
* "admin foo" => array(NULL,"admin foo")
* "cms:admin foo" => array("cms", "admin foo")
*
* @param $delim
* @param string $string
* E.g. "view all contacts". Syntax: "[prefix:]name".
* @param null $defaultPrefix
*
* @return array
* (0 => string|NULL $prefix, 1 => string $value)
*/
public static function parsePrefix($delim, $string, $defaultPrefix = NULL) {
$pos = strpos($string, $delim);
if ($pos === FALSE) {
return array($defaultPrefix, $string);
}
else {
return array(substr($string, 0, $pos), substr($string, 1 + $pos));
}
}
/**
* This function will mask part of the the user portion of an Email address (everything before the @)
*
* @param string $email
* The email address to be masked.
* @param string $maskChar
* The character used for masking.
* @param int $percent
* The percentage of the user portion to be masked.
*
* @return string
* returns the masked Email address
*/
public static function maskEmail($email, $maskChar = '*', $percent = 50) {
list($user, $domain) = preg_split("/@/", $email);
$len = strlen($user);
$maskCount = floor($len * $percent / 100);
$offset = floor(($len - $maskCount) / 2);
$masked = substr($user, 0, $offset)
. str_repeat($maskChar, $maskCount)
. substr($user, $maskCount + $offset);
return ($masked . '@' . $domain);
}
/**
* This function compares two strings.
*
* @param string $strOne
* String one.
* @param string $strTwo
* String two.
* @param bool $case
* Boolean indicating whether you want the comparison to be case sensitive or not.
*
* @return bool
* TRUE (string are identical); FALSE (strings are not identical)
*/
public static function compareStr($strOne, $strTwo, $case) {
if ($case == TRUE) {
// Convert to lowercase and trim white spaces
if (strtolower(trim($strOne)) == strtolower(trim($strTwo))) {
// yes - they are identical
return TRUE;
}
else {
// not identical
return FALSE;
}
}
if ($case == FALSE) {
// Trim white spaces
if (trim($strOne) == trim($strTwo)) {
// yes - they are identical
return TRUE;
}
else {
// not identical
return FALSE;
}
}
}
/**
* Many parts of the codebase have a convention of internally passing around
* HTML-encoded URLs. This effectively means that "&" is replaced by "&amp;"
* (because most other odd characters are %-escaped in URLs; and %-escaped
* strings don't need any extra escaping in HTML).
*
* @param string $htmlUrl
* URL with HTML entities.
* @return string
* URL without HTML entities
*/
public static function unstupifyUrl($htmlUrl) {
return str_replace('&amp;', '&', $htmlUrl);
}
/**
* When a user supplies a URL (e.g. to an image), we'd like to:
* - Remove the protocol and domain name if the URL points to the current
* site.
* - Keep the domain name for remote URLs.
* - Optionally, force remote URLs to use https instead of http (which is
* useful for images)
*
* @param string $url
* The URL to simplify. Examples:
* "https://example.org/sites/default/files/coffee-mug.jpg"
* "sites/default/files/coffee-mug.jpg"
* "http://i.stack.imgur.com/9jb2ial01b.png"
* @param bool $forceHttps = FALSE
* If TRUE, ensure that remote URLs use https. If a URL with
* http is supplied, then we'll change it to https.
* This is useful for situations like showing a premium product on a
* contribution, because (as reported in CRM-14283) if the user gets a
* browser warning like "page contains insecure elements" on a contribution
* page, that's a very bad thing. Thus, even if changing http to https
* breaks the image, that's better than leaving http content in a
* contribution page.
*
* @return string
* The simplified URL. Examples:
* "/sites/default/files/coffee-mug.jpg"
* "https://i.stack.imgur.com/9jb2ial01b.png"
*/
public static function simplifyURL($url, $forceHttps = FALSE) {
$config = CRM_Core_Config::singleton();
$siteURLParts = self::simpleParseUrl($config->userFrameworkBaseURL);
$urlParts = self::simpleParseUrl($url);
// If the image is locally hosted, then only give the path to the image
$urlIsLocal
= ($urlParts['host+port'] == '')
| ($urlParts['host+port'] == $siteURLParts['host+port']);
if ($urlIsLocal) {
// and make sure it begins with one forward slash
return preg_replace('_^/*(?=.)_', '/', $urlParts['path+query']);
}
// If the URL is external, then keep the full URL as supplied
else {
return $forceHttps ? preg_replace('_^http://_', 'https://', $url) : $url;
}
}
/**
* A simplified version of PHP's parse_url() function.
*
* @param string $url
* e.g. "https://example.com:8000/foo/bar/?id=1#fragment"
*
* @return array
* Will always contain keys 'host+port' and 'path+query', even if they're
* empty strings. Example:
* [
* 'host+port' => "example.com:8000",
* 'path+query' => "/foo/bar/?id=1",
* ]
*/
public static function simpleParseUrl($url) {
$parts = parse_url($url);
$host = isset($parts['host']) ? $parts['host'] : '';
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
$path = isset($parts['path']) ? $parts['path'] : '';
$query = isset($parts['query']) ? '?' . $parts['query'] : '';
return array(
'host+port' => "$host$port",
'path+query' => "$path$query",
);
}
/**
* Formats a string of attributes for insertion in an html tag.
*
* @param array $attributes
*
* @return string
*/
public static function htmlAttributes($attributes) {
$output = '';
foreach ($attributes as $name => $vals) {
$output .= " $name=\"" . htmlspecialchars(implode(' ', (array) $vals)) . '"';
}
return ltrim($output);
}
/**
* Determine if $string starts with $fragment.
*
* @param string $string
* The long string.
* @param string $fragment
* The fragment to look for.
* @return bool
*/
public static function startsWith($string, $fragment) {
if ($fragment === '') {
return TRUE;
}
$len = strlen($fragment);
return substr($string, 0, $len) === $fragment;
}
/**
* Determine if $string ends with $fragment.
*
* @param string $string
* The long string.
* @param string $fragment
* The fragment to look for.
* @return bool
*/
public static function endsWith($string, $fragment) {
if ($fragment === '') {
return TRUE;
}
$len = strlen($fragment);
return substr($string, -1 * $len) === $fragment;
}
/**
* @param string|array $patterns
* @param array $allStrings
* @param bool $allowNew
* Whether to return new, unrecognized names.
* @return array
*/
public static function filterByWildcards($patterns, $allStrings, $allowNew = FALSE) {
$patterns = (array) $patterns;
$result = array();
foreach ($patterns as $pattern) {
if (!\CRM_Utils_String::endsWith($pattern, '*')) {
if ($allowNew || in_array($pattern, $allStrings)) {
$result[] = $pattern;
}
}
else {
$prefix = rtrim($pattern, '*');
foreach ($allStrings as $key) {
if (\CRM_Utils_String::startsWith($key, $prefix)) {
$result[] = $key;
}
}
}
}
return array_values(array_unique($result));
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,935 @@
<?php
/**
* Base class for UF system integrations
*/
abstract class CRM_Utils_System_Base {
/**
* 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
* functions and leave the codebase oblivious to the type of CMS
*
* @deprecated
* @var bool
* TRUE, if the CMS is Drupal.
*/
var $is_drupal = FALSE;
/**
* 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
* functions and leave the codebase oblivious to the type of CMS
*
* @deprecated
* @var bool
* TRUE, if the CMS is Joomla!.
*/
var $is_joomla = FALSE;
/**
* 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
* functions and leave the codebase oblivious to the type of CMS
*
* @deprecated
* @var bool
* TRUE, if the CMS is WordPress.
*/
var $is_wordpress = FALSE;
/**
* Does this CMS / UF support a CMS specific logging mechanism?
* @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
* @var bool
*/
var $supports_UF_Logging = FALSE;
/**
* @var bool
* TRUE, if the CMS allows CMS forms to be extended by hooks.
*/
var $supports_form_extensions = FALSE;
public function initialize() {
if (\CRM_Utils_System::isSSL()) {
$this->mapConfigToSSL();
}
}
/**
* Append an additional breadcrumb tag to the existing breadcrumb.
*
* @param array $breadCrumbs
*/
public function appendBreadCrumb($breadCrumbs) {
}
/**
* Reset an additional breadcrumb tag to the existing breadcrumb.
*/
public function resetBreadCrumb() {
}
/**
* Append a string to the head of the html file.
*
* @param string $head
* The new string to be appended.
*/
public function addHTMLHead($head) {
}
/**
* Rewrite various system urls to https.
*/
public function mapConfigToSSL() {
// dont need to do anything, let CMS handle their own switch to SSL
}
/**
* Figure out the post url for QuickForm.
*
* @param string $action
* The default url if one is pre-specified.
*
* @return string
* The url to post the form.
*/
public function postURL($action) {
$config = CRM_Core_Config::singleton();
if (!empty($action)) {
return $action;
}
return $this->url(CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET),
NULL, TRUE, NULL, FALSE
);
}
/**
* Generate the url string to a CiviCRM path.
*
* @param string $path
* The path being linked to, such as "civicrm/add".
* @param string $query
* A query string to append to the link.
* @param bool $absolute
* Whether to force the output to be an absolute link (beginning with http).
* Useful for links that will be displayed outside the site, such as in an RSS feed.
* @param string $fragment
* A fragment identifier (named anchor) to append to the link.
* @param bool $frontend
* This link should be to the CMS front end (applies to WP & Joomla).
* @param bool $forceBackend
* This link should be to the CMS back end (applies to WP & Joomla).
*
* @return string
*/
public function url(
$path = NULL,
$query = NULL,
$absolute = FALSE,
$fragment = NULL,
$frontend = FALSE,
$forceBackend = FALSE
) {
return NULL;
}
/**
* Authenticate the user against the CMS db.
*
* @param string $name
* The user name.
* @param string $password
* The password for the above user.
* @param bool $loadCMSBootstrap
* Load cms bootstrap?.
* @param string $realPath
* Filename of script
*
* @return array|bool
* [contactID, ufID, unique string] else false if no auth
*/
public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
return FALSE;
}
/**
* Set a message in the CMS to display to a user.
*
* @param string $message
* The message to set.
*/
public function setMessage($message) {
}
/**
* Load user into session.
*
* @param obj $user
*
* @return bool
*/
public function loadUser($user) {
return TRUE;
}
/**
* Immediately stop script execution and display a 401 "Access Denied" page.
*/
public function permissionDenied() {
CRM_Core_Error::fatal(ts('You do not have permission to access this page.'));
}
/**
* Immediately stop script execution, log out the user and redirect to the home page.
*
* @deprecated
* This function should be removed in favor of linking to the CMS's logout page
*/
public function logout() {
}
/**
* Clear CMS caches related to the user registration/profile forms.
* Used when updating/embedding profiles on CMS user forms.
* @see CRM-3600
*/
public function updateCategories() {
}
/**
* Get the locale set in the CMS.
*
* @return string|null
* Locale or null for none
*/
public function getUFLocale() {
return NULL;
}
/**
* If we are using a theming system, invoke theme, else just print the content.
*
* @param string $content
* The content that will be themed.
* @param bool $print
* Are we displaying to the screen or bypassing theming?.
* @param bool $maintenance
* For maintenance mode.
*
* @throws Exception
* @return string|null
* NULL, If $print is FALSE, and some other criteria match up.
* The themed string, otherwise.
*
* @todo The return value is inconsistent.
* @todo Better to always return, and never print.
*/
public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
$ret = FALSE;
// TODO: Split up; this was copied verbatim from CiviCRM 4.0's multi-UF theming function
// but the parts should be copied into cleaner subclass implementations
$config = CRM_Core_Config::singleton();
if (
$config->userSystem->is_drupal &&
function_exists('theme') &&
!$print
) {
if ($maintenance) {
drupal_set_breadcrumb('');
drupal_maintenance_theme();
if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
CRM_Utils_System::addHTMLHead($region->render(''));
}
print theme('maintenance_page', array('content' => $content));
exit();
}
// TODO: Figure out why D7 returns but everyone else prints
$ret = TRUE;
}
$out = $content;
$config = &CRM_Core_Config::singleton();
if (
!$print &&
$config->userFramework == 'WordPress'
) {
if (!function_exists('is_admin')) {
throw new \Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
}
if (!defined('ABSPATH')) {
throw new \Exception('Constant "ABSPATH" is not defined, even though WordPress is the user framework.');
}
if (is_admin()) {
require_once ABSPATH . 'wp-admin/admin-header.php';
}
else {
// FIXME: we need to figure out to replace civicrm content on the frontend pages
}
}
if ($ret) {
return $out;
}
else {
print $out;
return NULL;
}
}
/**
* @return string
*/
public function getDefaultBlockLocation() {
return 'left';
}
/**
* Get the absolute path to the site's base url.
*
* @return bool|mixed|string
*/
public function getAbsoluteBaseURL() {
if (!defined('CIVICRM_UF_BASEURL')) {
return FALSE;
}
$url = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
//format url for language negotiation, CRM-7803
$url = $this->languageNegotiationURL($url);
if (CRM_Utils_System::isSSL()) {
$url = str_replace('http://', 'https://', $url);
}
return $url;
}
/**
* Get the relative path to the sites base url.
*
* @return bool
*/
public function getRelativeBaseURL() {
$absoluteBaseURL = $this->getAbsoluteBaseURL();
if ($absoluteBaseURL === FALSE) {
return FALSE;
}
$parts = parse_url($absoluteBaseURL);
return $parts['path'];
//$this->useFrameworkRelativeBase = empty($base['path']) ? '/' : $base['path'];
}
/**
* Get CMS Version.
*
* @return string
*/
public function getVersion() {
return 'Unknown';
}
/**
* Format the url as per language Negotiation.
*
* @param string $url
* @param bool $addLanguagePart
* @param bool $removeLanguagePart
*
* @return string
* Formatted url.
*/
public function languageNegotiationURL(
$url,
$addLanguagePart = TRUE,
$removeLanguagePart = FALSE
) {
return $url;
}
/**
* Determine the location of the CMS root.
*
* @return string|null
* Local file system path to CMS root, or NULL if it cannot be determined
*/
public function cmsRootPath() {
return NULL;
}
/**
* Create a user in the CMS.
*
* @param array $params
* @param string $mail
* Email id for cms user.
*
* @return int|bool
* uid if user exists, false otherwise
*/
public function createUser(&$params, $mail) {
return FALSE;
}
/**
* Update a user's email address in the CMS.
*
* @param int $ufID
* User ID in CMS.
* @param string $email
* Primary contact email address.
*/
public function updateCMSName($ufID, $email) {
}
/**
* Check if user is logged in to the CMS.
*
* @return bool
*/
public function isUserLoggedIn() {
return FALSE;
}
/**
* Check if user registration is permitted.
*
* @return bool
*/
public function isUserRegistrationPermitted() {
return FALSE;
}
/**
* Check if user can create passwords or is initially assigned a system-generated one.
*
* @return bool
*/
public function isPasswordUserGenerated() {
return FALSE;
}
/**
* Get user login URL for hosting CMS (method declared in each CMS system class)
*
* @param string $destination
* If present, add destination to querystring (works for Drupal only).
*
* @return string
* loginURL for the current CMS
*/
public abstract function getLoginURL($destination = '');
/**
* Get the login destination string.
*
* When this is passed in the URL the user will be directed to it after filling in the CMS form.
*
* @param CRM_Core_Form $form
* Form object representing the 'current' form - to which the user will be returned.
*
* @return string|NULL
* destination value for URL
*/
public function getLoginDestination(&$form) {
return NULL;
}
/**
* Determine the native ID of the CMS user.
*
* @param string $username
*
* @throws CRM_Core_Exception
*/
public function getUfId($username) {
$className = get_class($this);
throw new CRM_Core_Exception("Not implemented: {$className}->getUfId");
}
/**
* Set the localisation from the user framework.
*
* @param string $civicrm_language
*
* @return bool
*/
public function setUFLocale($civicrm_language) {
return TRUE;
}
/**
* Set a init session with user object.
*
* @param array $data
* Array with user specific data
*/
public function setUserSession($data) {
list($userID, $ufID) = $data;
$session = CRM_Core_Session::singleton();
$session->set('ufID', $ufID);
$session->set('userID', $userID);
}
/**
* Reset any system caches that may be required for proper CiviCRM integration.
*/
public function flush() {
// nullop by default
}
/**
* Flush css/js caches.
*/
public function clearResourceCache() {
// nullop by default
}
/**
* Add a script file.
*
* Note: This function is not to be called directly
* @see CRM_Core_Region::render()
*
* @param string $url absolute path to file
* @param string $region
* location within the document: 'html-header', 'page-header', 'page-footer'.
*
* @return bool
* TRUE if we support this operation in this CMS, FALSE otherwise
*/
public function addScriptUrl($url, $region) {
return FALSE;
}
/**
* Add an inline script.
*
* Note: This function is not to be called directly
* @see CRM_Core_Region::render()
*
* @param string $code javascript code
* @param string $region
* location within the document: 'html-header', 'page-header', 'page-footer'.
*
* @return bool
* TRUE if we support this operation in this CMS, FALSE otherwise
*/
public function addScript($code, $region) {
return FALSE;
}
/**
* Add a css file.
*
* Note: This function is not to be called directly
* @see CRM_Core_Region::render()
*
* @param string $url absolute path to file
* @param string $region
* location within the document: 'html-header', 'page-header', 'page-footer'.
*
* @return bool
* TRUE if we support this operation in this CMS, FALSE otherwise
*/
public function addStyleUrl($url, $region) {
return FALSE;
}
/**
* Add an inline style.
*
* Note: This function is not to be called directly
* @see CRM_Core_Region::render()
*
* @param string $code css code
* @param string $region
* location within the document: 'html-header', 'page-header', 'page-footer'.
*
* @return bool
* TRUE if we support this operation in this CMS, FALSE otherwise
*/
public function addStyle($code, $region) {
return FALSE;
}
/**
* Sets the title of the page.
*
* @param string $title
* Title to set in html header
* @param string|null $pageTitle
* Title to set in html body (if different)
*/
public function setTitle($title, $pageTitle = NULL) {
}
/**
* Return default Site Settings.
*
* @param string $dir
*
* @return array
* - $url, (Joomla - non admin url)
* - $siteName,
* - $siteRoot
*/
public function getDefaultSiteSettings($dir) {
$config = CRM_Core_Config::singleton();
$url = $config->userFrameworkBaseURL;
return array($url, NULL, NULL);
}
/**
* Determine the default location for file storage.
*
* FIXME:
* 1. This was pulled out from a bigger function. It should be split
* into even smaller pieces and marked abstract.
* 2. This would be easier to compute by a calling a CMS API, but
* for whatever reason Civi gets it from config data.
*
* @return array
* - url: string. ex: "http://example.com/sites/foo.com/files/civicrm"
* - path: string. ex: "/var/www/sites/foo.com/files/civicrm"
*/
public function getDefaultFileStorage() {
global $civicrm_root;
$config = CRM_Core_Config::singleton();
$baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE);
$filesURL = NULL;
$filesPath = NULL;
if ($config->userFramework == 'Joomla') {
// gross hack
// we need to remove the administrator/ from the end
$tempURL = str_replace("/administrator/", "/", $baseURL);
$filesURL = $tempURL . "media/civicrm/";
}
elseif ($config->userFramework == 'UnitTests') {
$filesURL = $baseURL . "sites/default/files/civicrm/";
}
else {
throw new CRM_Core_Exception("Failed to locate default file storage ($config->userFramework)");
}
return array(
'url' => $filesURL,
'path' => CRM_Utils_File::baseFilePath(),
);
}
/**
* Determine the location of the CiviCRM source tree.
*
* FIXME:
* 1. This was pulled out from a bigger function. It should be split
* into even smaller pieces and marked abstract.
* 2. This would be easier to compute by a calling a CMS API, but
* for whatever reason we take the hard way.
*
* @return array
* - url: string. ex: "http://example.com/sites/all/modules/civicrm"
* - path: string. ex: "/var/www/sites/all/modules/civicrm"
*/
public function getCiviSourceStorage() {
global $civicrm_root;
$config = CRM_Core_Config::singleton();
// Don't use $config->userFrameworkBaseURL; it has garbage on it.
// More generally, w shouldn't be using $config here.
if (!defined('CIVICRM_UF_BASEURL')) {
throw new RuntimeException('Undefined constant: CIVICRM_UF_BASEURL');
}
$baseURL = CRM_Utils_File::addTrailingSlash(CIVICRM_UF_BASEURL, '/');
if (CRM_Utils_System::isSSL()) {
$baseURL = str_replace('http://', 'https://', $baseURL);
}
if ($config->userFramework == 'Joomla') {
$userFrameworkResourceURL = $baseURL . "components/com_civicrm/civicrm/";
}
elseif ($config->userFramework == 'WordPress') {
$userFrameworkResourceURL = CIVICRM_PLUGIN_URL . "civicrm/";
}
elseif ($this->is_drupal) {
// Drupal setting
// check and see if we are installed in sites/all (for D5 and above)
// we dont use checkURL since drupal generates an error page and throws
// the system for a loop on lobo's macosx box
// or in modules
$cmsPath = $config->userSystem->cmsRootPath();
$userFrameworkResourceURL = $baseURL . str_replace("$cmsPath/", '',
str_replace('\\', '/', $civicrm_root)
);
$siteName = $config->userSystem->parseDrupalSiteNameFromRoot($civicrm_root);
if ($siteName) {
$civicrmDirName = trim(basename($civicrm_root));
$userFrameworkResourceURL = $baseURL . "sites/$siteName/modules/$civicrmDirName/";
}
}
else {
$userFrameworkResourceURL = NULL;
}
return array(
'url' => CRM_Utils_File::addTrailingSlash($userFrameworkResourceURL),
'path' => CRM_Utils_File::addTrailingSlash($civicrm_root),
);
}
/**
* Perform any post login activities required by the CMS.
*
* e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
* calls hook_user op 'login' and generates a new session.
*
* @param array $params
*
* FIXME: Document values accepted/required by $params
*/
public function userLoginFinalize($params = array()) {
}
/**
* Set timezone in mysql so that timestamp fields show the correct time.
*/
public function setMySQLTimeZone() {
$timeZoneOffset = $this->getTimeZoneOffset();
if ($timeZoneOffset) {
$sql = "SET time_zone = '$timeZoneOffset'";
CRM_Core_DAO::executequery($sql);
}
}
/**
* Get timezone from CMS.
*
* @return string|false|null
*/
public function getTimeZoneOffset() {
$timezone = $this->getTimeZoneString();
if ($timezone) {
if ($timezone == 'UTC' || $timezone == 'Etc/UTC') {
// CRM-17072 Let's short-circuit all the zero handling & return it here!
return '+00:00';
}
$tzObj = new DateTimeZone($timezone);
$dateTime = new DateTime("now", $tzObj);
$tz = $tzObj->getOffset($dateTime);
if (empty($tz)) {
return FALSE;
}
$timeZoneOffset = sprintf("%02d:%02d", $tz / 3600, abs(($tz / 60) % 60));
if ($timeZoneOffset > 0) {
$timeZoneOffset = '+' . $timeZoneOffset;
}
return $timeZoneOffset;
}
return NULL;
}
/**
* Get timezone as a string.
* @return string
* Timezone string e.g. 'America/Los_Angeles'
*/
public function getTimeZoneString() {
return date_default_timezone_get();
}
/**
* Get Unique Identifier from UserFramework system (CMS).
*
* @param object $user
* Object as described by the User Framework.
*
* @return mixed
* Unique identifier from the user Framework system
*/
public function getUniqueIdentifierFromUserObject($user) {
return NULL;
}
/**
* Get User ID from UserFramework system (CMS).
*
* @param object $user
*
* Object as described by the User Framework.
* @return null|int
*/
public function getUserIDFromUserObject($user) {
return NULL;
}
/**
* Get an array of user details for a contact, containing at minimum the user ID & name.
*
* @param int $contactID
*
* @return array
* CMS user details including
* - id
* - name (ie the system user name.
*/
public function getUser($contactID) {
$ufMatch = civicrm_api3('UFMatch', 'getsingle', array(
'contact_id' => $contactID,
'domain_id' => CRM_Core_Config::domainID(),
));
return array(
'id' => $ufMatch['uf_id'],
'name' => $ufMatch['uf_name'],
);
}
/**
* Get currently logged in user uf id.
*
* @return int|null
* logged in user uf id.
*/
public function getLoggedInUfID() {
return NULL;
}
/**
* Get currently logged in user unique identifier - this tends to be the email address or user name.
*
* @return string|null
* logged in user unique identifier
*/
public function getLoggedInUniqueIdentifier() {
return NULL;
}
/**
* Return a UFID (user account ID from the UserFramework / CMS system.
*
* ID is based on the user object passed, defaulting to the logged in user if not passed.
*
* Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would
* seem to be resolving the user id before calling the function.
*
* Note there is already a function getUFId which takes $username as a param - we could add $user
* as a second param to it but it seems messy - just overloading it because the name is taken.
*
* @param object $user
*
* @return int
* User ID of UF System
*/
public function getBestUFID($user = NULL) {
if ($user) {
return $this->getUserIDFromUserObject($user);
}
return $this->getLoggedInUfID();
}
/**
* Return a unique identifier (usually an email address or username) from the UserFramework / CMS system.
*
* This is based on the user object passed, defaulting to the logged in user if not passed.
*
* Note that ambiguous situation occurs in CRM_Core_BAO_UFMatch::synchronize - a cleaner approach would seem to be
* resolving the unique identifier before calling the function.
*
* @param object $user
*
* @return string
* unique identifier from the UF System
*/
public function getBestUFUniqueIdentifier($user = NULL) {
if ($user) {
return $this->getUniqueIdentifierFromUserObject($user);
}
return $this->getLoggedInUniqueIdentifier();
}
/**
* List modules installed in the CMS, including enabled and disabled ones.
*
* @return array
* [CRM_Core_Module]
*/
public function getModules() {
return array();
}
/**
* Get Url to view user record.
*
* @param int $contactID
* Contact ID.
*
* @return string|null
*/
public function getUserRecordUrl($contactID) {
return NULL;
}
/**
* Is the current user permitted to add a user.
*
* @return bool
*/
public function checkPermissionAddUser() {
return FALSE;
}
/**
* Output code from error function.
*
* @param string $content
*/
public function outputError($content) {
echo CRM_Utils_System::theme($content);
}
/**
* Log error to CMS.
*
* @param string $message
*/
public function logger($message) {
}
/**
* Append to coreResourcesList.
*
* @param array $list
*/
public function appendCoreResources(&$list) {
}
/**
* @param string $name
* @param string $value
*/
public function setHttpHeader($name, $value) {
header("$name: $value");
}
/**
* Create CRM contacts for all existing CMS users
*
* @return array
* @throws \Exception
*/
public function synchronizeUsers() {
throw new Exception('CMS user creation not supported for this framework');
return array();
}
}

View file

@ -0,0 +1,851 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Drupal specific stuff goes here
*/
class CRM_Utils_System_Drupal extends CRM_Utils_System_DrupalBase {
/**
* @inheritDoc
*/
public function createUser(&$params, $mail) {
$form_state = form_state_defaults();
$form_state['input'] = array(
'name' => $params['cms_name'],
'mail' => $params[$mail],
'op' => 'Create new account',
);
$admin = user_access('administer users');
if (!variable_get('user_email_verification', TRUE) || $admin) {
$form_state['input']['pass'] = array('pass1' => $params['cms_pass'], 'pass2' => $params['cms_pass']);
}
if (!empty($params['notify'])) {
$form_state['input']['notify'] = $params['notify'];
}
$form_state['rebuild'] = FALSE;
$form_state['programmed'] = TRUE;
$form_state['complete form'] = FALSE;
$form_state['method'] = 'post';
$form_state['build_info']['args'] = array();
/*
* if we want to submit this form more than once in a process (e.g. create more than one user)
* we must force it to validate each time for this form. Otherwise it will not validate
* subsequent submissions and the manner in which the password is passed in will be invalid
*/
$form_state['must_validate'] = TRUE;
$config = CRM_Core_Config::singleton();
// we also need to redirect b
$config->inCiviCRM = TRUE;
$form = drupal_retrieve_form('user_register_form', $form_state);
$form_state['process_input'] = 1;
$form_state['submitted'] = 1;
$form['#array_parents'] = array();
$form['#tree'] = FALSE;
drupal_process_form('user_register_form', $form, $form_state);
$config->inCiviCRM = FALSE;
if (form_get_errors()) {
return FALSE;
}
return $form_state['user']->uid;
}
/**
* @inheritDoc
*/
public function updateCMSName($ufID, $ufName) {
// CRM-5555
if (function_exists('user_load')) {
$user = user_load($ufID);
if ($user->mail != $ufName) {
user_save($user, array('mail' => $ufName));
$user = user_load($ufID);
}
}
}
/**
* Check if username and email exists in the drupal db.
*
* @param array $params
* Array of name and mail values.
* @param array $errors
* Array of errors.
* @param string $emailName
* Field label for the 'email'.
*/
public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
$config = CRM_Core_Config::singleton();
$dao = new CRM_Core_DAO();
$name = $dao->escape(CRM_Utils_Array::value('name', $params));
$email = $dao->escape(CRM_Utils_Array::value('mail', $params));
$errors = form_get_errors();
if ($errors) {
// unset drupal messages to avoid twice display of errors
unset($_SESSION['messages']);
}
if (!empty($params['name'])) {
if ($nameError = user_validate_name($params['name'])) {
$errors['cms_name'] = $nameError;
}
else {
$uid = db_query(
"SELECT uid FROM {users} WHERE name = :name",
array(':name' => $params['name'])
)->fetchField();
if ((bool) $uid) {
$errors['cms_name'] = ts('The username %1 is already taken. Please select another username.', array(1 => $params['name']));
}
}
}
if (!empty($params['mail'])) {
if ($emailError = user_validate_mail($params['mail'])) {
$errors[$emailName] = $emailError;
}
else {
$uid = db_query(
"SELECT uid FROM {users} WHERE mail = :mail",
array(':mail' => $params['mail'])
)->fetchField();
if ((bool) $uid) {
$resetUrl = url('user/password');
$errors[$emailName] = ts('The email address %1 already has an account associated with it. <a href="%2">Have you forgotten your password?</a>',
array(1 => $params['mail'], 2 => $resetUrl)
);
}
}
}
}
/**
* @inheritDoc
*/
public function getLoginURL($destination = '') {
$query = $destination ? array('destination' => $destination) : NULL;
return CRM_Utils_System::url('user', $query, TRUE);
}
/**
* @inheritDoc
*/
public function setTitle($title, $pageTitle = NULL) {
if (arg(0) == 'civicrm') {
if (!$pageTitle) {
$pageTitle = $title;
}
drupal_set_title($pageTitle, PASS_THROUGH);
}
}
/**
* @inheritDoc
*/
public function appendBreadCrumb($breadCrumbs) {
$breadCrumb = drupal_get_breadcrumb();
if (is_array($breadCrumbs)) {
foreach ($breadCrumbs as $crumbs) {
if (stripos($crumbs['url'], 'id%%')) {
$args = array('cid', 'mid');
foreach ($args as $a) {
$val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject,
FALSE, NULL, $_GET
);
if ($val) {
$crumbs['url'] = str_ireplace("%%{$a}%%", $val, $crumbs['url']);
}
}
}
$breadCrumb[] = "<a href=\"{$crumbs['url']}\">{$crumbs['title']}</a>";
}
}
drupal_set_breadcrumb($breadCrumb);
}
/**
* @inheritDoc
*/
public function resetBreadCrumb() {
$bc = array();
drupal_set_breadcrumb($bc);
}
/**
* @inheritDoc
*/
public function addHTMLHead($header) {
static $count = 0;
if (!empty($header)) {
$key = 'civi_' . ++$count;
$data = array(
'#type' => 'markup',
'#markup' => $header,
);
drupal_add_html_head($data, $key);
}
}
/**
* @inheritDoc
*/
public function addScriptUrl($url, $region) {
$params = array('group' => JS_LIBRARY, 'weight' => 10);
switch ($region) {
case 'html-header':
case 'page-footer':
$params['scope'] = substr($region, 5);
break;
default:
return FALSE;
}
// If the path is within the drupal directory we can use the more efficient 'file' setting
$params['type'] = $this->formatResourceUrl($url) ? 'file' : 'external';
drupal_add_js($url, $params);
return TRUE;
}
/**
* @inheritDoc
*/
public function addScript($code, $region) {
$params = array('type' => 'inline', 'group' => JS_LIBRARY, 'weight' => 10);
switch ($region) {
case 'html-header':
case 'page-footer':
$params['scope'] = substr($region, 5);
break;
default:
return FALSE;
}
drupal_add_js($code, $params);
return TRUE;
}
/**
* @inheritDoc
*/
public function addStyleUrl($url, $region) {
if ($region != 'html-header') {
return FALSE;
}
$params = array();
// If the path is within the drupal directory we can use the more efficient 'file' setting
$params['type'] = $this->formatResourceUrl($url) ? 'file' : 'external';
drupal_add_css($url, $params);
return TRUE;
}
/**
* @inheritDoc
*/
public function addStyle($code, $region) {
if ($region != 'html-header') {
return FALSE;
}
$params = array('type' => 'inline');
drupal_add_css($code, $params);
return TRUE;
}
/**
* @inheritDoc
*/
public function mapConfigToSSL() {
global $base_url;
$base_url = str_replace('http://', 'https://', $base_url);
}
/**
* Get the name of the users table.
*
* @return string
*/
protected function getUsersTableName() {
$userFrameworkUsersTableName = Civi::settings()->get('userFrameworkUsersTableName');
if (empty($userFrameworkUsersTableName)) {
$userFrameworkUsersTableName = 'users';
}
return $userFrameworkUsersTableName;
}
/**
* @inheritDoc
*/
public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
require_once 'DB.php';
$config = CRM_Core_Config::singleton();
$dbDrupal = DB::connect($config->userFrameworkDSN);
if (DB::isError($dbDrupal)) {
CRM_Core_Error::fatal("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage());
}
$account = $userUid = $userMail = NULL;
if ($loadCMSBootstrap) {
$bootStrapParams = array();
if ($name && $password) {
$bootStrapParams = array(
'name' => $name,
'pass' => $password,
);
}
CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath);
global $user;
if ($user) {
$userUid = $user->uid;
$userMail = $user->mail;
}
}
else {
// CRM-8638
// SOAP cannot load drupal bootstrap and hence we do it the old way
// Contact CiviSMTP folks if we run into issues with this :)
$cmsPath = $config->userSystem->cmsRootPath($realPath);
require_once "$cmsPath/includes/bootstrap.inc";
require_once "$cmsPath/includes/password.inc";
$strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
$name = $dbDrupal->escapeSimple($strtolower($name));
$userFrameworkUsersTableName = $this->getUsersTableName();
// LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
$sql = "
SELECT u.*
FROM {$userFrameworkUsersTableName} u
WHERE LOWER(u.name) = '$name'
AND u.status = 1
";
$query = $dbDrupal->query($sql);
$row = $query->fetchRow(DB_FETCHMODE_ASSOC);
if ($row) {
$fakeDrupalAccount = drupal_anonymous_user();
$fakeDrupalAccount->name = $name;
$fakeDrupalAccount->pass = $row['pass'];
$passwordCheck = user_check_password($password, $fakeDrupalAccount);
if ($passwordCheck) {
$userUid = $row['uid'];
$userMail = $row['mail'];
}
}
}
if ($userUid && $userMail) {
CRM_Core_BAO_UFMatch::synchronizeUFMatch($account, $userUid, $userMail, 'Drupal');
$contactID = CRM_Core_BAO_UFMatch::getContactId($userUid);
if (!$contactID) {
return FALSE;
}
return array($contactID, $userUid, mt_rand());
}
return FALSE;
}
/**
* @inheritDoc
*/
public function loadUser($username) {
global $user;
$user = user_load_by_name($username);
if (empty($user->uid)) {
return FALSE;
}
$uid = $user->uid;
$contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
// lets store contact id and user id in session
$session = CRM_Core_Session::singleton();
$session->set('ufID', $uid);
$session->set('userID', $contact_id);
return TRUE;
}
/**
* Perform any post login activities required by the UF -
* e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
* calls hook_user op 'login' and generates a new session.
*
* @param array $params
*
* FIXME: Document values accepted/required by $params
*/
public function userLoginFinalize($params = array()) {
user_login_finalize($params);
}
/**
* Determine the native ID of the CMS user.
*
* @param string $username
* @return int|NULL
*/
public function getUfId($username) {
$user = user_load_by_name($username);
if (empty($user->uid)) {
return NULL;
}
return $user->uid;
}
/**
* @inheritDoc
*/
public function logout() {
module_load_include('inc', 'user', 'user.pages');
return user_logout();
}
/**
* Get the default location for CiviCRM blocks.
*
* @return string
*/
public function getDefaultBlockLocation() {
return 'sidebar_first';
}
/**
* Load drupal bootstrap.
*
* @param array $params
* Either uid, or name & pass.
* @param bool $loadUser
* Boolean Require CMS user load.
* @param bool $throwError
* If true, print error on failure and exit.
* @param bool|string $realPath path to script
*
* @return bool
*/
public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
//take the cms root path.
$cmsPath = $this->cmsRootPath($realPath);
if (!file_exists("$cmsPath/includes/bootstrap.inc")) {
if ($throwError) {
throw new Exception('Sorry, could not locate bootstrap.inc');
}
return FALSE;
}
// load drupal bootstrap
chdir($cmsPath);
define('DRUPAL_ROOT', $cmsPath);
// For drupal multi-site CRM-11313
if ($realPath && strpos($realPath, 'sites/all/modules/') === FALSE) {
preg_match('@sites/([^/]*)/modules@s', $realPath, $matches);
if (!empty($matches[1])) {
$_SERVER['HTTP_HOST'] = $matches[1];
}
}
require_once 'includes/bootstrap.inc';
// @ to suppress notices eg 'DRUPALFOO already defined'.
@drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// explicitly setting error reporting, since we cannot handle drupal related notices
// @todo 1 = E_ERROR, but more to the point setting error reporting deep in code
// causes grief with debugging scripts
error_reporting(1);
if (!function_exists('module_exists')) {
if ($throwError) {
throw new Exception('Sorry, could not load drupal bootstrap.');
}
return FALSE;
}
if (!module_exists('civicrm')) {
if ($throwError) {
throw new Exception('Sorry, drupal cannot find CiviCRM');
}
return FALSE;
}
// seems like we've bootstrapped drupal
$config = CRM_Core_Config::singleton();
// lets also fix the clean url setting
// CRM-6948
$config->cleanURL = (int) variable_get('clean_url', '0');
// we need to call the config hook again, since we now know
// all the modules that are listening on it, does not apply
// to J! and WP as yet
// CRM-8655
CRM_Utils_Hook::config($config);
if (!$loadUser) {
return TRUE;
}
$uid = CRM_Utils_Array::value('uid', $params);
if (!$uid) {
//load user, we need to check drupal permissions.
$name = CRM_Utils_Array::value('name', $params, FALSE) ? $params['name'] : trim(CRM_Utils_Array::value('name', $_REQUEST));
$pass = CRM_Utils_Array::value('pass', $params, FALSE) ? $params['pass'] : trim(CRM_Utils_Array::value('pass', $_REQUEST));
if ($name) {
$uid = user_authenticate($name, $pass);
if (!$uid) {
if ($throwError) {
throw new Exception('Sorry, unrecognized username or password.');
}
return FALSE;
}
}
}
if ($uid) {
$account = user_load($uid);
if ($account && $account->uid) {
global $user;
$user = $account;
return TRUE;
}
}
if ($throwError) {
throw new Exception('Sorry, can not load CMS user account.');
}
// CRM-6948: When using loadBootStrap, it's implicit that CiviCRM has already loaded its settings
// which means that define(CIVICRM_CLEANURL) was correctly set.
// So we correct it
$config = CRM_Core_Config::singleton();
$config->cleanURL = (int) variable_get('clean_url', '0');
// CRM-8655: Drupal wasn't available during bootstrap, so hook_civicrm_config never executes
CRM_Utils_Hook::config($config);
return FALSE;
}
/**
* Get CMS root path.
*
* @param string $scriptFilename
*
* @return null|string
*/
public function cmsRootPath($scriptFilename = NULL) {
$cmsRoot = $valid = NULL;
if (!is_null($scriptFilename)) {
$path = $scriptFilename;
}
else {
$path = $_SERVER['SCRIPT_FILENAME'];
}
if (function_exists('drush_get_context')) {
// drush anyway takes care of multisite install etc
return drush_get_context('DRUSH_DRUPAL_ROOT');
}
global $civicrm_paths;
if (!empty($civicrm_paths['cms.root']['path'])) {
return $civicrm_paths['cms.root']['path'];
}
// CRM-7582
$pathVars = explode('/',
str_replace('//', '/',
str_replace('\\', '/', $path)
)
);
//lets store first var,
//need to get back for windows.
$firstVar = array_shift($pathVars);
//lets remove sript name to reduce one iteration.
array_pop($pathVars);
// CRM-7429 -- do check for uppermost 'includes' dir, which would
// work for multisite installation.
do {
$cmsRoot = $firstVar . '/' . implode('/', $pathVars);
$cmsIncludePath = "$cmsRoot/includes";
// Stop if we find bootstrap.
if (file_exists("$cmsIncludePath/bootstrap.inc")) {
$valid = TRUE;
break;
}
//remove one directory level.
array_pop($pathVars);
} while (count($pathVars));
return ($valid) ? $cmsRoot : NULL;
}
/**
* @inheritDoc
*/
public function isUserLoggedIn() {
$isloggedIn = FALSE;
if (function_exists('user_is_logged_in')) {
$isloggedIn = user_is_logged_in();
}
return $isloggedIn;
}
/**
* @inheritDoc
*/
public function getLoggedInUfID() {
$ufID = NULL;
if (function_exists('user_is_logged_in') &&
user_is_logged_in() &&
function_exists('user_uid_optional_to_arg')
) {
$ufID = user_uid_optional_to_arg(array());
}
return $ufID;
}
/**
* @inheritDoc
*/
public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
if (empty($url)) {
return $url;
}
//CRM-7803 -from d7 onward.
$config = CRM_Core_Config::singleton();
if (function_exists('variable_get') &&
module_exists('locale') &&
function_exists('language_negotiation_get')
) {
global $language;
//does user configuration allow language
//support from the URL (Path prefix or domain)
if (language_negotiation_get('language') == 'locale-url') {
$urlType = variable_get('locale_language_negotiation_url_part');
//url prefix
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
if (isset($language->prefix) && $language->prefix) {
if ($addLanguagePart) {
$url .= $language->prefix . '/';
}
if ($removeLanguagePart) {
$url = str_replace("/{$language->prefix}/", '/', $url);
}
}
}
//domain
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN) {
if (isset($language->domain) && $language->domain) {
if ($addLanguagePart) {
$cleanedUrl = preg_replace('#^https?://#', '', $language->domain);
// drupal function base_path() adds a "/" to the beginning and end of the returned path
if (substr($cleanedUrl, -1) == '/') {
$cleanedUrl = substr($cleanedUrl, 0, -1);
}
$url = (CRM_Utils_System::isSSL() ? 'https' : 'http') . '://' . $cleanedUrl . base_path();
}
if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
$url = str_replace('\\', '/', $url);
$parseUrl = parse_url($url);
//kinda hackish but not sure how to do it right
//hope http_build_url() will help at some point.
if (is_array($parseUrl) && !empty($parseUrl)) {
$urlParts = explode('/', $url);
$hostKey = array_search($parseUrl['host'], $urlParts);
$ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
$urlParts[$hostKey] = $ufUrlParts['host'];
$url = implode('/', $urlParts);
}
}
}
}
}
}
return $url;
}
/**
* Find any users/roles/security-principals with the given permission
* and replace it with one or more permissions.
*
* @param string $oldPerm
* @param array $newPerms
* Array, strings.
*/
public function replacePermission($oldPerm, $newPerms) {
$roles = user_roles(FALSE, $oldPerm);
if (!empty($roles)) {
foreach (array_keys($roles) as $rid) {
user_role_revoke_permissions($rid, array($oldPerm));
user_role_grant_permissions($rid, $newPerms);
}
}
}
/**
* Wrapper for og_membership creation.
*
* @param int $ogID
* Organic Group ID.
* @param int $drupalID
* Drupal User ID.
*/
public function og_membership_create($ogID, $drupalID) {
if (function_exists('og_entity_query_alter')) {
// sort-of-randomly chose a function that only exists in the // 7.x-2.x branch
//
// @TODO Find more solid way to check - try system_get_info('module', 'og').
//
// Also, since we don't know how to get the entity type of the // group, we'll assume it's 'node'
og_group('node', $ogID, array('entity' => user_load($drupalID)));
}
else {
// Works for the OG 7.x-1.x branch
og_group($ogID, array('entity' => user_load($drupalID)));
}
}
/**
* Wrapper for og_membership deletion.
*
* @param int $ogID
* Organic Group ID.
* @param int $drupalID
* Drupal User ID.
*/
public function og_membership_delete($ogID, $drupalID) {
if (function_exists('og_entity_query_alter')) {
// sort-of-randomly chose a function that only exists in the 7.x-2.x branch
// TODO: Find a more solid way to make this test
// Also, since we don't know how to get the entity type of the group, we'll assume it's 'node'
og_ungroup('node', $ogID, 'user', user_load($drupalID));
}
else {
// Works for the OG 7.x-1.x branch
og_ungroup($ogID, 'user', user_load($drupalID));
}
}
/**
* @inheritDoc
*/
public function getTimeZoneString() {
global $user;
// Note that 0 is a valid timezone (GMT) so we use strlen not empty to check.
if (variable_get('configurable_timezones', 1) && $user->uid && isset($user->timezone) && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', NULL);
}
if (!$timezone) {
$timezone = parent::getTimeZoneString();
}
return $timezone;
}
/**
* @inheritDoc
*/
public function setHttpHeader($name, $value) {
drupal_add_http_header($name, $value);
}
/**
* @inheritDoc
*/
public function synchronizeUsers() {
$config = CRM_Core_Config::singleton();
if (PHP_SAPI != 'cli') {
set_time_limit(300);
}
$id = 'uid';
$mail = 'mail';
$name = 'name';
$result = db_query("SELECT uid, mail, name FROM {users} where mail != ''");
$user = new StdClass();
$uf = $config->userFramework;
$contactCount = 0;
$contactCreated = 0;
$contactMatching = 0;
foreach ($result as $row) {
$user->$id = $row->$id;
$user->$mail = $row->$mail;
$user->$name = $row->$name;
$contactCount++;
if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $row->$id, $row->$mail, $uf, 1, 'Individual', TRUE)) {
$contactCreated++;
}
else {
$contactMatching++;
}
if (is_object($match)) {
$match->free();
}
}
return array(
'contactCount' => $contactCount,
'contactMatching' => $contactMatching,
'contactCreated' => $contactCreated,
);
}
}

View file

@ -0,0 +1,821 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Drupal specific stuff goes here.
*/
class CRM_Utils_System_Drupal6 extends CRM_Utils_System_DrupalBase {
/**
* Theme output.
*
* If we are using a theming system, invoke theme, else just print the content.
*
* @param string $content
* The content that will be themed.
* @param bool $print
* Are we displaying to the screen or bypassing theming?.
* @param bool $maintenance
* For maintenance mode.
*
* @return null|string
* prints content on stdout
*/
public function theme(&$content, $print = FALSE, $maintenance = FALSE) {
// TODO: Simplify; this was copied verbatim from CiviCRM 3.4's multi-UF theming function, but that's more complex than necessary
if (function_exists('theme') && !$print) {
if ($maintenance) {
drupal_set_breadcrumb('');
drupal_maintenance_theme();
}
// Arg 3 for D6 theme() is "show_blocks". Previously, we passed
// through a badly named variable ("$args") which was almost always
// TRUE (except on fatal error screen). However, this feature is
// non-functional on D6 default themes, was purposefully removed from
// D7, has no analog in other our other CMS's, and clutters the code.
// Hard-wiring to TRUE should be OK.
$out = theme('page', $content, TRUE);
}
else {
$out = $content;
}
print $out;
return NULL;
}
/**
* Create user.
*
* @inheritDoc
*/
public function createUser(&$params, $mail) {
$form_state = array();
$form_state['values'] = array(
'name' => $params['cms_name'],
'mail' => $params[$mail],
'op' => 'Create new account',
);
$admin = user_access('administer users');
if (!variable_get('user_email_verification', TRUE) || $admin) {
$form_state['values']['pass']['pass1'] = $params['cms_pass'];
$form_state['values']['pass']['pass2'] = $params['cms_pass'];
}
$config = CRM_Core_Config::singleton();
// we also need to redirect b
$config->inCiviCRM = TRUE;
$form = drupal_retrieve_form('user_register', $form_state);
$form['#post'] = $form_state['values'];
drupal_prepare_form('user_register', $form, $form_state);
// remove the captcha element from the form prior to processing
unset($form['captcha']);
drupal_process_form('user_register', $form, $form_state);
$config->inCiviCRM = FALSE;
if (form_get_errors() || !isset($form_state['user'])) {
return FALSE;
}
return $form_state['user']->uid;
}
/**
* @inheritDoc
*/
public function updateCMSName($ufID, $ufName) {
// CRM-5555
if (function_exists('user_load')) {
$user = user_load(array('uid' => $ufID));
if ($user->mail != $ufName) {
user_save($user, array('mail' => $ufName));
$user = user_load(array('uid' => $ufID));
}
}
}
/**
* Check if username and email exists in the drupal db.
*
* @param array $params
* Array of name and mail values.
* @param array $errors
* Array of errors.
* @param string $emailName
* Field label for the 'email'.
*/
public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
$config = CRM_Core_Config::singleton();
$dao = new CRM_Core_DAO();
$name = $dao->escape(CRM_Utils_Array::value('name', $params));
$email = $dao->escape(CRM_Utils_Array::value('mail', $params));
_user_edit_validate(NULL, $params);
$errors = form_get_errors();
if ($errors) {
if (!empty($errors['name'])) {
$errors['cms_name'] = $errors['name'];
}
if (!empty($errors['mail'])) {
$errors[$emailName] = $errors['mail'];
}
// also unset drupal messages to avoid twice display of errors
unset($_SESSION['messages']);
}
// Do the name check manually.
$nameError = user_validate_name($params['name']);
if ($nameError) {
$errors['cms_name'] = $nameError;
}
// LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
$sql = "
SELECT name, mail
FROM {users}
WHERE (LOWER(name) = LOWER('$name')) OR (LOWER(mail) = LOWER('$email'))
";
$result = db_query($sql);
$row = db_fetch_array($result);
if (!$row) {
return;
}
$user = NULL;
if (!empty($row)) {
$dbName = CRM_Utils_Array::value('name', $row);
$dbEmail = CRM_Utils_Array::value('mail', $row);
if (strtolower($dbName) == strtolower($name)) {
$errors['cms_name'] = ts('The username %1 is already taken. Please select another username.',
array(1 => $name)
);
}
if (strtolower($dbEmail) == strtolower($email)) {
if (empty($email)) {
$errors[$emailName] = ts('You cannot create an email account for a contact with no email',
array(1 => $email)
);
}
else {
$errors[$emailName] = ts('This email %1 already has an account associated with it. Please select another email.',
array(1 => $email)
);
}
}
}
}
/**
* @inheritDoc
*/
public function setTitle($title, $pageTitle = NULL) {
if (!$pageTitle) {
$pageTitle = $title;
}
if (arg(0) == 'civicrm') {
//set drupal title
drupal_set_title($pageTitle);
}
}
/**
* @inheritDoc
*/
public function appendBreadCrumb($breadCrumbs) {
$breadCrumb = drupal_get_breadcrumb();
if (is_array($breadCrumbs)) {
foreach ($breadCrumbs as $crumbs) {
if (stripos($crumbs['url'], 'id%%')) {
$args = array('cid', 'mid');
foreach ($args as $a) {
$val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject,
FALSE, NULL, $_GET
);
if ($val) {
$crumbs['url'] = str_ireplace("%%{$a}%%", $val, $crumbs['url']);
}
}
}
$breadCrumb[] = "<a href=\"{$crumbs['url']}\">{$crumbs['title']}</a>";
}
}
drupal_set_breadcrumb($breadCrumb);
}
/**
* @inheritDoc
*/
public function resetBreadCrumb() {
$bc = array();
drupal_set_breadcrumb($bc);
}
/**
* Append a string to the head of the html file.
*
* @param string $head
* The new string to be appended.
*/
public function addHTMLHead($head) {
drupal_set_html_head($head);
}
/**
* Add a css file.
*
* @param $url : string, absolute path to file
* @param string $region
* location within the document: 'html-header', 'page-header', 'page-footer'.
*
* Note: This function is not to be called directly
* @see CRM_Core_Region::render()
*
* @return bool
* TRUE if we support this operation in this CMS, FALSE otherwise
*/
public function addStyleUrl($url, $region) {
if ($region != 'html-header' || !$this->formatResourceUrl($url)) {
return FALSE;
}
drupal_add_css($url);
return TRUE;
}
/**
* @inheritDoc
*/
public function mapConfigToSSL() {
global $base_url;
$base_url = str_replace('http://', 'https://', $base_url);
}
/**
* Get the name of the table that stores the user details.
*
* @return string
*/
protected function getUsersTableName() {
$userFrameworkUsersTableName = Civi::settings()->get('userFrameworkUsersTableName');
if (empty($userFrameworkUsersTableName)) {
$userFrameworkUsersTableName = 'users';
}
return $userFrameworkUsersTableName;
}
/**
* @inheritDoc
*/
public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
//@todo this 'PEAR-y' stuff is only required when bookstrap is not being loaded which is rare
// if ever now.
// probably if bootstrap is loaded this call
// CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath); would be
// sufficient to do what this fn does. It does exist as opposed to return which might need some hanky-panky to make
// safe in the unknown situation where authenticate might be called & it is important that
// false is returned
require_once 'DB.php';
$config = CRM_Core_Config::singleton();
$dbDrupal = DB::connect($config->userFrameworkDSN);
if (DB::isError($dbDrupal)) {
CRM_Core_Error::fatal("Cannot connect to drupal db via $config->userFrameworkDSN, " . $dbDrupal->getMessage());
}
$strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
$dbpassword = md5($password);
$name = $dbDrupal->escapeSimple($strtolower($name));
$userFrameworkUsersTableName = $this->getUsersTableName();
$sql = 'SELECT u.* FROM ' . $userFrameworkUsersTableName . " u WHERE LOWER(u.name) = '$name' AND u.pass = '$dbpassword' AND u.status = 1";
$query = $dbDrupal->query($sql);
$user = NULL;
// need to change this to make sure we matched only one row
while ($row = $query->fetchRow(DB_FETCHMODE_ASSOC)) {
CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $row['uid'], $row['mail'], 'Drupal');
$contactID = CRM_Core_BAO_UFMatch::getContactId($row['uid']);
if (!$contactID) {
return FALSE;
}
else {
//success
if ($loadCMSBootstrap) {
$bootStrapParams = array();
if ($name && $password) {
$bootStrapParams = array(
'name' => $name,
'pass' => $password,
);
}
CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, $realPath);
}
return array($contactID, $row['uid'], mt_rand());
}
}
return FALSE;
}
/**
* @inheritDoc
*/
public function loadUser($username) {
global $user;
$user = user_load(array('name' => $username));
if (empty($user->uid)) {
return FALSE;
}
$uid = $user->uid;
$contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
// lets store contact id and user id in session
$session = CRM_Core_Session::singleton();
$session->set('ufID', $uid);
$session->set('userID', $contact_id);
return TRUE;
}
/**
* Perform any post login activities required by the UF -
* e.g. for drupal : records a watchdog message about the new session,
* saves the login timestamp, calls hook_user op 'login' and generates a new session.
*
* @param array $params
*
* FIXME: Document values accepted/required by $params
*/
public function userLoginFinalize($params = array()) {
user_authenticate_finalize($params);
}
/**
* Determine the native ID of the CMS user.
*
* @param string $username
* @return int|NULL
*/
public function getUfId($username) {
$user = user_load(array('name' => $username));
if (empty($user->uid)) {
return NULL;
}
return $user->uid;
}
/**
* @inheritDoc
*/
public function logout() {
module_load_include('inc', 'user', 'user.pages');
return user_logout();
}
/**
* Load drupal bootstrap.
*
* @param array $params
* Either uid, or name & pass.
* @param bool $loadUser
* Boolean Require CMS user load.
* @param bool $throwError
* If true, print error on failure and exit.
* @param bool|string $realPath path to script
*
* @return bool
*/
public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
//take the cms root path.
$cmsPath = $this->cmsRootPath($realPath);
if (!file_exists("$cmsPath/includes/bootstrap.inc")) {
if ($throwError) {
echo '<br />Sorry, could not locate bootstrap.inc\n';
exit();
}
return FALSE;
}
// load drupal bootstrap
chdir($cmsPath);
define('DRUPAL_ROOT', $cmsPath);
// For drupal multi-site CRM-11313
if ($realPath && strpos($realPath, 'sites/all/modules/') === FALSE) {
preg_match('@sites/([^/]*)/modules@s', $realPath, $matches);
if (!empty($matches[1])) {
$_SERVER['HTTP_HOST'] = $matches[1];
}
}
require_once 'includes/bootstrap.inc';
// @ to suppress notices eg 'DRUPALFOO already defined'.
@drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// explicitly setting error reporting, since we cannot handle drupal related notices
error_reporting(1);
if (!function_exists('module_exists') || !module_exists('civicrm')) {
if ($throwError) {
echo '<br />Sorry, could not load drupal bootstrap.';
exit();
}
return FALSE;
}
// seems like we've bootstrapped drupal
$config = CRM_Core_Config::singleton();
// lets also fix the clean url setting
// CRM-6948
$config->cleanURL = (int) variable_get('clean_url', '0');
// we need to call the config hook again, since we now know
// all the modules that are listening on it, does not apply
// to J! and WP as yet
// CRM-8655
CRM_Utils_Hook::config($config);
if (!$loadUser) {
return TRUE;
}
global $user;
// If $uid is passed in, authentication has been done already.
$uid = CRM_Utils_Array::value('uid', $params);
if (!$uid) {
//load user, we need to check drupal permissions.
$name = CRM_Utils_Array::value('name', $params, FALSE) ? $params['name'] : trim(CRM_Utils_Array::value('name', $_REQUEST));
$pass = CRM_Utils_Array::value('pass', $params, FALSE) ? $params['pass'] : trim(CRM_Utils_Array::value('pass', $_REQUEST));
if ($name) {
$user = user_authenticate(array('name' => $name, 'pass' => $pass));
if (!$user->uid) {
if ($throwError) {
echo '<br />Sorry, unrecognized username or password.';
exit();
}
return FALSE;
}
else {
return TRUE;
}
}
}
if ($uid) {
$account = user_load($uid);
if ($account && $account->uid) {
$user = $account;
return TRUE;
}
}
if ($throwError) {
echo '<br />Sorry, can not load CMS user account.';
exit();
}
// CRM-6948: When using loadBootStrap, it's implicit that CiviCRM has already loaded its settings
// which means that define(CIVICRM_CLEANURL) was correctly set.
// So we correct it
$config = CRM_Core_Config::singleton();
$config->cleanURL = (int) variable_get('clean_url', '0');
// CRM-8655: Drupal wasn't available during bootstrap, so hook_civicrm_config never executes
CRM_Utils_Hook::config($config);
return FALSE;
}
/**
* Get CMS root path.
*
* @param string $scriptFilename
*
* @return null|string
*/
public function cmsRootPath($scriptFilename = NULL) {
$cmsRoot = $valid = NULL;
if (!is_null($scriptFilename)) {
$path = $scriptFilename;
}
else {
$path = $_SERVER['SCRIPT_FILENAME'];
}
if (function_exists('drush_get_context')) {
// drush anyway takes care of multisite install etc
return drush_get_context('DRUSH_DRUPAL_ROOT');
}
global $civicrm_paths;
if (!empty($civicrm_paths['cms.root']['path'])) {
return $civicrm_paths['cms.root']['path'];
}
// CRM-7582
$pathVars = explode('/',
str_replace('//', '/',
str_replace('\\', '/', $path)
)
);
//lets store first var,
//need to get back for windows.
$firstVar = array_shift($pathVars);
//lets remove sript name to reduce one iteration.
array_pop($pathVars);
//CRM-7429 --do check for upper most 'includes' dir,
//which would effectually work for multisite installation.
do {
$cmsRoot = $firstVar . '/' . implode('/', $pathVars);
$cmsIncludePath = "$cmsRoot/includes";
// Stop if we found bootstrap.
if (file_exists("$cmsIncludePath/bootstrap.inc")) {
$valid = TRUE;
break;
}
//remove one directory level.
array_pop($pathVars);
} while (count($pathVars));
return ($valid) ? $cmsRoot : NULL;
}
/**
* @inheritDoc
*/
public function isUserLoggedIn() {
$isloggedIn = FALSE;
if (function_exists('user_is_logged_in')) {
$isloggedIn = user_is_logged_in();
}
return $isloggedIn;
}
/**
* @inheritDoc
*/
public function getLoggedInUfID() {
$ufID = NULL;
if (function_exists('user_is_logged_in') &&
user_is_logged_in() &&
function_exists('user_uid_optional_to_arg')
) {
$ufID = user_uid_optional_to_arg(array());
}
return $ufID;
}
/**
* @inheritDoc
*/
public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
if (empty($url)) {
return $url;
}
//up to d6 only, already we have code in place for d7
$config = CRM_Core_Config::singleton();
if (function_exists('variable_get') &&
module_exists('locale')
) {
global $language;
//get the mode.
$mode = variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE);
//url prefix / path.
if (isset($language->prefix) &&
$language->prefix &&
in_array($mode, array(
LANGUAGE_NEGOTIATION_PATH,
LANGUAGE_NEGOTIATION_PATH_DEFAULT,
))
) {
if ($addLanguagePart) {
$url .= $language->prefix . '/';
}
if ($removeLanguagePart) {
$url = str_replace("/{$language->prefix}/", '/', $url);
}
}
if (isset($language->domain) &&
$language->domain &&
$mode == LANGUAGE_NEGOTIATION_DOMAIN
) {
if ($addLanguagePart) {
$url = CRM_Utils_File::addTrailingSlash($language->domain, '/');
}
if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
$url = str_replace('\\', '/', $url);
$parseUrl = parse_url($url);
//kinda hackish but not sure how to do it right
//hope http_build_url() will help at some point.
if (is_array($parseUrl) && !empty($parseUrl)) {
$urlParts = explode('/', $url);
$hostKey = array_search($parseUrl['host'], $urlParts);
$ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
$urlParts[$hostKey] = $ufUrlParts['host'];
$url = implode('/', $urlParts);
}
}
}
}
return $url;
}
/**
* Find any users/roles/security-principals with the given permission
* and replace it with one or more permissions.
*
* @param string $oldPerm
* @param array $newPerms
* Array, strings.
*/
public function replacePermission($oldPerm, $newPerms) {
$roles = user_roles(FALSE, $oldPerm);
foreach ($roles as $rid => $roleName) {
$permList = db_result(db_query('SELECT perm FROM {permission} WHERE rid = %d', $rid));
$perms = drupal_map_assoc(explode(', ', $permList));
unset($perms[$oldPerm]);
$perms = $perms + drupal_map_assoc($newPerms);
$permList = implode(', ', $perms);
db_query('UPDATE {permission} SET perm = "%s" WHERE rid = %d', $permList, $rid);
/* @codingStandardsIgnoreStart
if ( ! empty( $roles ) ) {
$rids = implode(',', array_keys($roles));
db_query( 'UPDATE {permission} SET perm = CONCAT( perm, \', edit all events\') WHERE rid IN (' . implode(',', array_keys($roles)) . ')' );
db_query( "UPDATE {permission} SET perm = REPLACE( perm, '%s', '%s' ) WHERE rid IN ($rids)",
$oldPerm, implode(', ', $newPerms) );
@codingStandardsIgnoreEnd */
}
}
/**
* @inheritDoc
*/
public function getModules() {
$result = array();
$q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1');
while ($row = db_fetch_object($q)) {
$result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE);
}
return $result;
}
/**
* @inheritDoc
*/
public function getLoginURL($destination = '') {
$config = CRM_Core_Config::singleton();
$loginURL = $config->userFrameworkBaseURL;
$loginURL .= 'user';
if (!empty($destination)) {
// append destination so user is returned to form they came from after login
$loginURL .= '?destination=' . urlencode($destination);
}
return $loginURL;
}
/**
* Wrapper for og_membership creation.
*
* @param int $ogID
* Organic Group ID.
* @param int $drupalID
* Drupal User ID.
*/
public function og_membership_create($ogID, $drupalID) {
og_save_subscription($ogID, $drupalID, array('is_active' => 1));
}
/**
* Wrapper for og_membership deletion.
*
* @param int $ogID
* Organic Group ID.
* @param int $drupalID
* Drupal User ID.
*/
public function og_membership_delete($ogID, $drupalID) {
og_delete_subscription($ogID, $drupalID);
}
/**
* @inheritDoc
*/
public function getTimeZoneString() {
global $user;
// Note that 0 is a valid timezone (GMT) so we use strlen not empty to check.
if (variable_get('configurable_timezones', 1) && $user->uid && isset($user->timezone) && strlen($user->timezone)) {
$timezone = $user->timezone;
}
else {
$timezone = variable_get('date_default_timezone', NULL);
}
if (!$timezone) {
$timezone = parent::getTimeZoneString();
}
return $timezone;
}
/**
* @inheritDoc
*/
public function setHttpHeader($name, $value) {
drupal_set_header("$name: $value");
}
/**
* @inheritDoc
*/
public function synchronizeUsers() {
$config = CRM_Core_Config::singleton();
if (PHP_SAPI != 'cli') {
set_time_limit(300);
}
$rows = array();
$id = 'uid';
$mail = 'mail';
$name = 'name';
$result = db_query("SELECT uid, mail, name FROM {users} where mail != ''");
while ($row = db_fetch_array($result)) {
$rows[] = $row;
}
$user = new StdClass();
$uf = $config->userFramework;
$contactCount = 0;
$contactCreated = 0;
$contactMatching = 0;
foreach ($rows as $row) {
$user->$id = $row[$id];
$user->$mail = $row[$mail];
$user->$name = $row[$name];
$contactCount++;
if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $row[$id], $row[$mail], $uf, 1, 'Individual', TRUE)) {
$contactCreated++;
}
else {
$contactMatching++;
}
if (is_object($match)) {
$match->free();
}
}
return array(
'contactCount' => $contactCount,
'contactMatching' => $contactMatching,
'contactCreated' => $contactCreated,
);
}
}

View file

@ -0,0 +1,662 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Drupal specific stuff goes here.
*/
class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase {
/**
* @inheritDoc
*/
public function createUser(&$params, $mail) {
$user = \Drupal::currentUser();
$user_register_conf = \Drupal::config('user.settings')->get('register');
$verify_mail_conf = \Drupal::config('user.settings')->get('verify_mail');
// Don't create user if we don't have permission to.
if (!$user->hasPermission('administer users') && $user_register_conf == 'admin_only') {
return FALSE;
}
$account = entity_create('user');
$account->setUsername($params['cms_name'])->setEmail($params[$mail]);
// Allow user to set password only if they are an admin or if
// the site settings don't require email verification.
if (!$verify_mail_conf || $user->hasPermission('administer users')) {
// @Todo: do we need to check that passwords match or assume this has already been done for us?
$account->setPassword($params['cms_pass']);
}
// Only activate account if we're admin or if anonymous users don't require
// approval to create accounts.
if ($user_register_conf != 'visitors' && !$user->hasPermission('administer users')) {
$account->block();
}
// Validate the user object
$violations = $account->validate();
if (count($violations)) {
return FALSE;
}
try {
$account->save();
}
catch (\Drupal\Core\Entity\EntityStorageException $e) {
return FALSE;
}
// Send off any emails as required.
// Possible values for $op:
// - 'register_admin_created': Welcome message for user created by the admin.
// - 'register_no_approval_required': Welcome message when user
// self-registers.
// - 'register_pending_approval': Welcome message, user pending admin
// approval.
// @Todo: Should we only send off emails if $params['notify'] is set?
switch (TRUE) {
case $user_register_conf == 'admin_only' || $user->isAuthenticated():
_user_mail_notify('register_admin_created', $account);
break;
case $user_register_conf == 'visitors':
_user_mail_notify('register_no_approval_required', $account);
break;
case 'visitors_admin_approval':
_user_mail_notify('register_pending_approval', $account);
break;
}
return $account->id();
}
/**
* @inheritDoc
*/
public function updateCMSName($ufID, $email) {
$user = entity_load('user', $ufID);
if ($user && $user->getEmail() != $email) {
$user->setEmail($email);
if (!count($user->validate())) {
$user->save();
}
}
}
/**
* Check if username and email exists in the drupal db.
*
* @param array $params
* Array of name and mail values.
* @param array $errors
* Errors.
* @param string $emailName
* Field label for the 'email'.
*/
public static function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
// If we are given a name, let's check to see if it already exists.
if (!empty($params['name'])) {
$name = $params['name'];
$user = entity_create('user');
$user->setUsername($name);
// This checks for both username uniqueness and validity.
$violations = iterator_to_array($user->validate());
// We only care about violations on the username field; discard the rest.
$violations = array_filter($violations, function ($v) {
return $v->getPropertyPath() == 'name.0.value';
});
if (count($violations) > 0) {
$errors['cms_name'] = $violations[0]->getMessage();
}
}
// And if we are given an email address, let's check to see if it already exists.
if (!empty($params[$emailName])) {
$mail = $params[$emailName];
$user = entity_create('user');
$user->setEmail($mail);
// This checks for both email uniqueness.
$violations = iterator_to_array($user->validate());
// We only care about violations on the email field; discard the rest.
$violations = array_filter($violations, function ($v) {
return $v->getPropertyPath() == 'mail.0.value';
});
if (count($violations) > 0) {
$errors[$emailName] = $violations[0]->getMessage();
}
}
}
/**
* @inheritDoc
*/
public function getLoginURL($destination = '') {
$query = $destination ? array('destination' => $destination) : array();
return \Drupal::url('user.page', array(), array('query' => $query));
}
/**
* @inheritDoc
*/
public function setTitle($title, $pageTitle = NULL) {
if (!$pageTitle) {
$pageTitle = $title;
}
\Drupal::service('civicrm.page_state')->setTitle($pageTitle);
}
/**
* @inheritDoc
*/
public function appendBreadCrumb($breadcrumbs) {
$civicrmPageState = \Drupal::service('civicrm.page_state');
foreach ($breadcrumbs as $breadcrumb) {
$civicrmPageState->addBreadcrumb($breadcrumb['title'], $breadcrumb['url']);
}
}
/**
* @inheritDoc
*/
public function resetBreadCrumb() {
\Drupal::service('civicrm.page_state')->resetBreadcrumbs();
}
/**
* @inheritDoc
*/
public function addHTMLHead($header) {
\Drupal::service('civicrm.page_state')->addHtmlHeader($header);
}
/**
* @inheritDoc
*/
public function addScriptUrl($url, $region) {
static $weight = 0;
switch ($region) {
case 'html-header':
break;
default:
return FALSE;
}
$script = array(
'#tag' => 'script',
'#attributes' => array(
'src' => $url,
),
'#weight' => $weight,
);
$weight++;
\Drupal::service('civicrm.page_state')->addJS($script);
return TRUE;
}
/**
* @inheritDoc
*/
public function addScript($code, $region) {
switch ($region) {
case 'html-header':
break;
default:
return FALSE;
}
$script = array(
'#tag' => 'script',
'#value' => $code,
);
\Drupal::service('civicrm.page_state')->addJS($script);
return TRUE;
}
/**
* @inheritDoc
*/
public function addStyleUrl($url, $region) {
if ($region != 'html-header') {
return FALSE;
}
$css = array(
'#tag' => 'link',
'#attributes' => array(
'href' => $url,
'rel' => 'stylesheet',
),
);
\Drupal::service('civicrm.page_state')->addCSS($css);
return TRUE;
}
/**
* @inheritDoc
*/
public function addStyle($code, $region) {
if ($region != 'html-header') {
return FALSE;
}
$css = array(
'#tag' => 'style',
'#value' => $code,
);
\Drupal::service('civicrm.page_state')->addCSS($css);
return TRUE;
}
/**
* Check if a resource url is within the drupal directory and format appropriately.
*
* This seems to be a legacy function. We assume all resources are within the drupal
* directory and always return TRUE. As well, we clean up the $url.
*
* FIXME: This is not a legacy function and the above is not a safe assumption.
* External urls are allowed by CRM_Core_Resources and this needs to return the correct value.
*
* @param $url
*
* @return bool
*/
public function formatResourceUrl(&$url) {
// Remove leading slash if present.
$url = ltrim($url, '/');
// Remove query string — presumably added to stop intermediary caching.
if (($pos = strpos($url, '?')) !== FALSE) {
$url = substr($url, 0, $pos);
}
// FIXME: Should not unconditionally return true
return TRUE;
}
/**
* This function does nothing in Drupal 8. Changes to the base_url should be made
* in settings.php directly.
*/
public function mapConfigToSSL() {
}
/**
* @inheritDoc
*/
public function url(
$path = '',
$query = '',
$absolute = FALSE,
$fragment = NULL,
$frontend = FALSE,
$forceBackend = FALSE
) {
$query = html_entity_decode($query);
$url = \Drupal\civicrm\CivicrmHelper::parseURL("{$path}?{$query}");
// Not all links that CiviCRM generates are Drupal routes, so we use the weaker ::fromUri method.
try {
$url = \Drupal\Core\Url::fromUri("base:{$url['path']}", array(
'query' => $url['query'],
'fragment' => $fragment,
'absolute' => $absolute,
))->toString();
}
catch (Exception $e) {
// @Todo: log to watchdog
$url = '';
}
// Special case: CiviCRM passes us "*path*?*query*" as a skeleton, but asterisks
// are invalid and Drupal will attempt to escape them. We unescape them here:
if ($path == '*path*') {
// First remove trailing equals sign that has been added since the key '?*query*' has no value.
$url = rtrim($url, '=');
$url = urldecode($url);
}
return $url;
}
/**
* @inheritDoc
*/
public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
$system = new CRM_Utils_System_Drupal8();
$system->loadBootStrap(array(), FALSE);
$uid = \Drupal::service('user.auth')->authenticate($name, $password);
$contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
return array($contact_id, $uid, mt_rand());
}
/**
* @inheritDoc
*/
public function loadUser($username) {
$user = user_load_by_name($username);
if (!$user) {
return FALSE;
}
// Set Drupal's current user to the loaded user.
\Drupal::currentUser()->setAccount($user);
$uid = $user->id();
$contact_id = CRM_Core_BAO_UFMatch::getContactId($uid);
// Store the contact id and user id in the session
$session = CRM_Core_Session::singleton();
$session->set('ufID', $uid);
$session->set('userID', $contact_id);
return TRUE;
}
/**
* Determine the native ID of the CMS user.
*
* @param string $username
* @return int|NULL
*/
public function getUfId($username) {
if ($id = user_load_by_name($username)->id()) {
return $id;
}
}
/**
* @inheritDoc
*/
public function permissionDenied() {
\Drupal::service('civicrm.page_state')->setAccessDenied();
}
/**
* In previous versions, this function was the controller for logging out. In Drupal 8, we rewrite the route
* to hand off logout to the standard Drupal logout controller. This function should therefore never be called.
*/
public function logout() {
// Pass
}
/**
* Load drupal bootstrap.
*
* @param array $params
* Either uid, or name & pass.
* @param bool $loadUser
* Boolean Require CMS user load.
* @param bool $throwError
* If true, print error on failure and exit.
* @param bool|string $realPath path to script
*
* @return bool
* @Todo Handle setting cleanurls configuration for CiviCRM?
*/
public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL) {
static $run_once = FALSE;
if ($run_once) {
return TRUE;
}
else {
$run_once = TRUE;
}
if (!($root = $this->cmsRootPath())) {
return FALSE;
}
chdir($root);
// Create a mock $request object
$autoloader = require_once $root . '/vendor/autoload.php';
if ($autoloader === TRUE) {
$autoloader = ComposerAutoloaderInitDrupal8::getLoader();
}
// @Todo: do we need to handle case where $_SERVER has no HTTP_HOST key, ie. when run via cli?
$request = new \Symfony\Component\HttpFoundation\Request(array(), array(), array(), array(), array(), $_SERVER);
// Create a kernel and boot it.
\Drupal\Core\DrupalKernel::createFromRequest($request, $autoloader, 'prod')->prepareLegacyRequest($request);
// Initialize Civicrm
\Drupal::service('civicrm');
// We need to call the config hook again, since we now know
// all the modules that are listening on it (CRM-8655).
CRM_Utils_Hook::config($config);
if ($loadUser) {
if (!empty($params['uid']) && $username = \Drupal\user\Entity\User::load($uid)->getUsername()) {
$this->loadUser($username);
}
elseif (!empty($params['name']) && !empty($params['pass']) && $this->authenticate($params['name'], $params['pass'])) {
$this->loadUser($params['name']);
}
}
return TRUE;
}
/**
* Determine the location of the CMS root.
*
* @param string $path
*
* @return NULL|string
*/
public function cmsRootPath($path = NULL) {
global $civicrm_paths;
if (!empty($civicrm_paths['cms.root']['path'])) {
return $civicrm_paths['cms.root']['path'];
}
if (defined('DRUPAL_ROOT')) {
return DRUPAL_ROOT;
}
// It looks like Drupal hasn't been bootstrapped.
// We're going to attempt to discover the root Drupal path
// by climbing out of the folder hierarchy and looking around to see
// if we've found the Drupal root directory.
if (!$path) {
$path = $_SERVER['SCRIPT_FILENAME'];
}
// Normalize and explode path into its component paths.
$paths = explode(DIRECTORY_SEPARATOR, realpath($path));
// Remove script filename from array of directories.
array_pop($paths);
while (count($paths)) {
$candidate = implode('/', $paths);
if (file_exists($candidate . "/core/includes/bootstrap.inc")) {
return $candidate;
}
array_pop($paths);
}
}
/**
* @inheritDoc
*/
public function isUserLoggedIn() {
return \Drupal::currentUser()->isAuthenticated();
}
/**
* @inheritDoc
*/
public function isUserRegistrationPermitted() {
if (\Drupal::config('user.settings')->get('register') == 'admin_only') {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function isPasswordUserGenerated() {
if (\Drupal::config('user.settings')->get('verify_mail') == TRUE) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function getLoggedInUfID() {
if ($id = \Drupal::currentUser()->id()) {
return $id;
}
}
/**
* @inheritDoc
*/
public function getDefaultBlockLocation() {
return 'sidebar_first';
}
/**
* @inheritDoc
*/
public function flush() {
// CiviCRM and Drupal both provide (different versions of) Symfony (and possibly share other classes too).
// If we call drupal_flush_all_caches(), Drupal will attempt to rediscover all of its classes, use Civicrm's
// alternatives instead and then die. Instead, we only clear cache bins and no more.
foreach (Drupal\Core\Cache\Cache::getBins() as $service_id => $cache_backend) {
$cache_backend->deleteAll();
}
}
/**
* @inheritDoc
*/
public function getModules() {
$modules = array();
$module_data = system_rebuild_module_data();
foreach ($module_data as $module_name => $extension) {
if (!isset($extension->info['hidden']) && $extension->origin != 'core') {
$extension->schema_version = drupal_get_installed_schema_version($module_name);
$modules[] = new CRM_Core_Module('drupal.' . $module_name, ($extension->status == 1 ? TRUE : FALSE));
}
}
return $modules;
}
/**
* @inheritDoc
*/
public function getUniqueIdentifierFromUserObject($user) {
return $user->get('mail')->value;
}
/**
* @inheritDoc
*/
public function getUserIDFromUserObject($user) {
return $user->get('uid')->value;
}
/**
* @inheritDoc
*/
public function synchronizeUsers() {
$config = CRM_Core_Config::singleton();
if (PHP_SAPI != 'cli') {
set_time_limit(300);
}
$users = array();
$users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties();
$uf = $config->userFramework;
$contactCount = 0;
$contactCreated = 0;
$contactMatching = 0;
foreach ($users as $user) {
$mail = $user->get('mail')->value;
if (empty($mail)) {
continue;
}
$uid = $user->get('uid')->value;
$contactCount++;
if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $uid, $mail, $uf, 1, 'Individual', TRUE)) {
$contactCreated++;
}
else {
$contactMatching++;
}
if (is_object($match)) {
$match->free();
}
}
return array(
'contactCount' => $contactCount,
'contactMatching' => $contactMatching,
'contactCreated' => $contactCreated,
);
}
/**
* Drupal 8 has a different function to get current path, hence
* overriding the postURL function
*
* @param string $action
*
* @return string
*/
public function postURL($action) {
if (!empty($action)) {
return $action;
}
$current_path = \Drupal::service('path.current')->getPath();
return $this->url($current_path);
}
}

View file

@ -0,0 +1,667 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
* $Id$
*
*/
/**
* Drupal specific stuff goes here
*/
abstract class CRM_Utils_System_DrupalBase extends CRM_Utils_System_Base {
/**
* Does this CMS / UF support a CMS specific logging mechanism?
* @todo - we should think about offering up logging mechanisms in a way that is also extensible by extensions
* @var bool
*/
var $supports_UF_Logging = TRUE;
/**
*/
public function __construct() {
/**
* 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
* functions and leave the codebase oblivious to the type of CMS
* @deprecated
* @var bool
*/
$this->is_drupal = TRUE;
$this->supports_form_extensions = TRUE;
}
/**
* @inheritdoc
*/
public function getDefaultFileStorage() {
$config = CRM_Core_Config::singleton();
$baseURL = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE);
$siteName = $this->parseDrupalSiteNameFromRequest('/files/civicrm');
if ($siteName) {
$filesURL = $baseURL . "sites/$siteName/files/civicrm/";
}
else {
$filesURL = $baseURL . "sites/default/files/civicrm/";
}
return array(
'url' => $filesURL,
'path' => CRM_Utils_File::baseFilePath(),
);
}
/**
* @inheritDoc
*/
public function getDefaultSiteSettings($dir) {
$config = CRM_Core_Config::singleton();
$siteName = $siteRoot = NULL;
$matches = array();
if (preg_match(
'|/sites/([\w\.\-\_]+)/|',
$config->templateCompileDir,
$matches
)) {
$siteName = $matches[1];
if ($siteName) {
$siteName = "/sites/$siteName/";
$siteNamePos = strpos($dir, $siteName);
if ($siteNamePos !== FALSE) {
$siteRoot = substr($dir, 0, $siteNamePos);
}
}
}
$url = $config->userFrameworkBaseURL;
return array($url, $siteName, $siteRoot);
}
/**
* Check if a resource url is within the drupal directory and format appropriately.
*
* @param $url (reference)
*
* @return bool
* TRUE for internal paths, FALSE for external. The drupal_add_js fn is able to add js more
* efficiently if it is known to be in the drupal site
*/
public function formatResourceUrl(&$url) {
$internal = FALSE;
$base = CRM_Core_Config::singleton()->resourceBase;
global $base_url;
// Handle absolute urls
// 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)
// to see if the url is within our drupal dir, if it is we are able to treated it as an internal url
if (strpos($url, $base_url) === 0) {
$file = trim(str_replace($base_url, '', $url), '/');
// CRM-18130: Custom CSS URL not working if aliased or rewritten
if (file_exists(DRUPAL_ROOT . $file)) {
$url = $file;
$internal = TRUE;
}
}
// Handle relative urls that are within the CiviCRM module directory
elseif (strpos($url, $base) === 0) {
$internal = TRUE;
$url = $this->appendCoreDirectoryToResourceBase(dirname(drupal_get_path('module', 'civicrm')) . '/') . trim(substr($url, strlen($base)), '/');
}
// Strip query string
$q = strpos($url, '?');
if ($q && $internal) {
$url = substr($url, 0, $q);
}
return $internal;
}
/**
* In instance where civicrm folder has a drupal folder & a civicrm core folder @ the same level append the
* civicrm folder name to the url
* See CRM-13737 for discussion of how this allows implementers to alter the folder structure
* @todo - this only provides a limited amount of flexiblity - it still expects a 'civicrm' folder with a 'drupal' folder
* and is only flexible as to the name of the civicrm folder.
*
* @param string $url
* Potential resource url based on standard folder assumptions.
* @return string
* with civicrm-core directory appended if not standard civi dir
*/
public function appendCoreDirectoryToResourceBase($url) {
global $civicrm_root;
$lastDirectory = basename($civicrm_root);
if ($lastDirectory != 'civicrm') {
return $url .= $lastDirectory . '/';
}
return $url;
}
/**
* Generate an internal CiviCRM URL (copied from DRUPAL/includes/common.inc#url)
*
* @inheritDoc
*/
public function url(
$path = NULL,
$query = NULL,
$absolute = FALSE,
$fragment = NULL,
$frontend = FALSE,
$forceBackend = FALSE
) {
$config = CRM_Core_Config::singleton();
$script = 'index.php';
$path = CRM_Utils_String::stripPathChars($path);
if (isset($fragment)) {
$fragment = '#' . $fragment;
}
$base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase;
$separator = '&';
if (!$config->cleanURL) {
if (isset($path)) {
if (isset($query)) {
return $base . $script . '?q=' . $path . $separator . $query . $fragment;
}
else {
return $base . $script . '?q=' . $path . $fragment;
}
}
else {
if (isset($query)) {
return $base . $script . '?' . $query . $fragment;
}
else {
return $base . $fragment;
}
}
}
else {
if (isset($path)) {
if (isset($query)) {
return $base . $path . '?' . $query . $fragment;
}
else {
return $base . $path . $fragment;
}
}
else {
if (isset($query)) {
return $base . $script . '?' . $query . $fragment;
}
else {
return $base . $fragment;
}
}
}
}
/**
* @inheritDoc
*/
public function getUserIDFromUserObject($user) {
return !empty($user->uid) ? $user->uid : NULL;
}
/**
* @inheritDoc
*/
public function setMessage($message) {
drupal_set_message($message);
}
/**
* @inheritDoc
*/
public function getUniqueIdentifierFromUserObject($user) {
return empty($user->mail) ? NULL : $user->mail;
}
/**
* @inheritDoc
*/
public function getLoggedInUniqueIdentifier() {
global $user;
return $this->getUniqueIdentifierFromUserObject($user);
}
/**
* @inheritDoc
*/
public function permissionDenied() {
drupal_access_denied();
}
/**
* @inheritDoc
*/
public function getUserRecordUrl($contactID) {
$uid = CRM_Core_BAO_UFMatch::getUFId($contactID);
if (CRM_Core_Session::singleton()
->get('userID') == $contactID || CRM_Core_Permission::checkAnyPerm(array(
'cms:administer users',
'cms:view user account',
))
) {
return $this->url('user/' . $uid);
};
}
/**
* @inheritDoc
*/
public function checkPermissionAddUser() {
return CRM_Core_Permission::check('administer users');
}
/**
* @inheritDoc
*/
public function logger($message) {
if (CRM_Core_Config::singleton()->userFrameworkLogging && function_exists('watchdog')) {
watchdog('civicrm', '%message', array('%message' => $message), NULL, WATCHDOG_DEBUG);
}
}
/**
* @inheritDoc
*/
public function clearResourceCache() {
_drupal_flush_css_js();
}
/**
* Append Drupal js to coreResourcesList.
*
* @param array $list
*/
public function appendCoreResources(&$list) {
$list[] = 'js/crm.drupal.js';
}
/**
* @inheritDoc
*/
public function flush() {
drupal_flush_all_caches();
}
/**
* @inheritDoc
*/
public function getModules() {
$result = array();
$q = db_query('SELECT name, status FROM {system} WHERE type = \'module\' AND schema_version <> -1');
foreach ($q as $row) {
$result[] = new CRM_Core_Module('drupal.' . $row->name, ($row->status == 1) ? TRUE : FALSE);
}
return $result;
}
/**
* Find any users/roles/security-principals with the given permission
* and replace it with one or more permissions.
*
* @param string $oldPerm
* @param array $newPerms
* Array, strings.
*
* @return void
*/
public function replacePermission($oldPerm, $newPerms) {
$roles = user_roles(FALSE, $oldPerm);
if (!empty($roles)) {
foreach (array_keys($roles) as $rid) {
user_role_revoke_permissions($rid, array($oldPerm));
user_role_grant_permissions($rid, $newPerms);
}
}
}
/**
* @inheritDoc
*/
public function languageNegotiationURL($url, $addLanguagePart = TRUE, $removeLanguagePart = FALSE) {
if (empty($url)) {
return $url;
}
//CRM-7803 -from d7 onward.
$config = CRM_Core_Config::singleton();
if (function_exists('variable_get') &&
module_exists('locale') &&
function_exists('language_negotiation_get')
) {
global $language;
//does user configuration allow language
//support from the URL (Path prefix or domain)
if (language_negotiation_get('language') == 'locale-url') {
$urlType = variable_get('locale_language_negotiation_url_part');
//url prefix
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
if (isset($language->prefix) && $language->prefix) {
if ($addLanguagePart) {
$url .= $language->prefix . '/';
}
if ($removeLanguagePart) {
$url = str_replace("/{$language->prefix}/", '/', $url);
}
}
}
//domain
if ($urlType == LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN) {
if (isset($language->domain) && $language->domain) {
if ($addLanguagePart) {
$url = (CRM_Utils_System::isSSL() ? 'https' : 'http') . '://' . $language->domain . base_path();
}
if ($removeLanguagePart && defined('CIVICRM_UF_BASEURL')) {
$url = str_replace('\\', '/', $url);
$parseUrl = parse_url($url);
//kinda hackish but not sure how to do it right
//hope http_build_url() will help at some point.
if (is_array($parseUrl) && !empty($parseUrl)) {
$urlParts = explode('/', $url);
$hostKey = array_search($parseUrl['host'], $urlParts);
$ufUrlParts = parse_url(CIVICRM_UF_BASEURL);
$urlParts[$hostKey] = $ufUrlParts['host'];
$url = implode('/', $urlParts);
}
}
}
}
}
}
return $url;
}
/**
* @inheritDoc
*/
public function getVersion() {
return defined('VERSION') ? VERSION : 'Unknown';
}
/**
* @inheritDoc
*/
public function isUserRegistrationPermitted() {
if (!variable_get('user_register', TRUE)) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function isPasswordUserGenerated() {
if (variable_get('user_email_verification', TRUE)) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function updateCategories() {
// copied this from profile.module. Seems a bit inefficient, but i don't know a better way
cache_clear_all();
menu_rebuild();
}
/**
* @inheritDoc
*/
public function getUFLocale() {
// return CiviCRMs xx_YY locale that either matches Drupals Chinese locale
// (for CRM-6281), Drupals xx_YY or is retrieved based on Drupals xx
// sometimes for CLI based on order called, this might not be set and/or empty
global $language;
if (empty($language)) {
return NULL;
}
if ($language->language == 'zh-hans') {
return 'zh_CN';
}
if ($language->language == 'zh-hant') {
return 'zh_TW';
}
if (preg_match('/^.._..$/', $language->language)) {
return $language->language;
}
return CRM_Core_I18n_PseudoConstant::longForShort(substr($language->language, 0, 2));
}
/**
* @inheritDoc
*/
public function setUFLocale($civicrm_language) {
global $language;
$langcode = substr($civicrm_language, 0, 2);
$languages = language_list();
if (isset($languages[$langcode])) {
$language = $languages[$langcode];
// Config must be re-initialized to reset the base URL
// otherwise links will have the wrong language prefix/domain.
$config = CRM_Core_Config::singleton();
$config->free();
return TRUE;
}
return FALSE;
}
/**
* Perform any post login activities required by the UF -
* e.g. for drupal: records a watchdog message about the new session, saves the login timestamp,
* calls hook_user op 'login' and generates a new session.
*
* @param array $params
*
* FIXME: Document values accepted/required by $params
*/
public function userLoginFinalize($params = array()) {
user_login_finalize($params);
}
/**
* @inheritDoc
*/
public function getLoginDestination(&$form) {
$args = NULL;
$id = $form->get('id');
if ($id) {
$args .= "&id=$id";
}
else {
$gid = $form->get('gid');
if ($gid) {
$args .= "&gid=$gid";
}
else {
// Setup Personal Campaign Page link uses pageId
$pageId = $form->get('pageId');
if ($pageId) {
$component = $form->get('component');
$args .= "&pageId=$pageId&component=$component&action=add";
}
}
}
$destination = NULL;
if ($args) {
// append destination so user is returned to form they came from after login
$destination = CRM_Utils_System::currentPath() . '?reset=1' . $args;
}
return $destination;
}
/**
* Fixme: Why are we overriding the parent function? Seems inconsistent.
* This version supplies slightly different params to $this->url (not absolute and html encoded) but why?
*
* @param string $action
*
* @return string
*/
public function postURL($action) {
if (!empty($action)) {
return $action;
}
return $this->url($_GET['q']);
}
/**
* Get an array of user details for a contact, containing at minimum the user ID & name.
*
* @param int $contactID
*
* @return array
* CMS user details including
* - id
* - name (ie the system user name.
*/
public function getUser($contactID) {
$userDetails = parent::getUser($contactID);
$user = $this->getUserObject($userDetails['id']);
$userDetails['name'] = $user->name;
$userDetails['email'] = $user->mail;
return $userDetails;
}
/**
* Load the user object.
*
* Note this function still works in drupal 6, 7 & 8 but is deprecated in Drupal 8.
*
* @param $userID
*
* @return object
*/
public function getUserObject($userID) {
return user_load($userID);
}
/**
* Parse the name of the drupal site.
*
* @param string $civicrm_root
*
* @return null|string
* @deprecated
*/
public function parseDrupalSiteNameFromRoot($civicrm_root) {
$siteName = NULL;
if (strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . 'all' . DIRECTORY_SEPARATOR . 'modules'
) === FALSE
) {
$startPos = strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR
);
$endPos = strpos($civicrm_root,
DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR
);
if ($startPos && $endPos) {
// if component is in sites/SITENAME/modules
$siteName = substr($civicrm_root,
$startPos + 7,
$endPos - $startPos - 7
);
}
}
return $siteName;
}
/**
* Determine if Drupal multi-site applies to the current request -- and,
* specifically, determine the name of the multisite folder.
*
* @param string $flagFile
* Check if $flagFile exists inside the site dir.
* @return null|string
* string, e.g. `bar.example.com` if using multisite.
* NULL if using the default site.
*/
private function parseDrupalSiteNameFromRequest($flagFile = '') {
$phpSelf = array_key_exists('PHP_SELF', $_SERVER) ? $_SERVER['PHP_SELF'] : '';
$httpHost = array_key_exists('HTTP_HOST', $_SERVER) ? $_SERVER['HTTP_HOST'] : '';
if (empty($httpHost)) {
$httpHost = parse_url(CIVICRM_UF_BASEURL, PHP_URL_HOST);
if (parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT)) {
$httpHost .= ':' . parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT);
}
}
$confdir = $this->cmsRootPath() . '/sites';
if (file_exists($confdir . "/sites.php")) {
include $confdir . "/sites.php";
}
else {
$sites = array();
}
$uri = explode('/', $phpSelf);
$server = explode('.', implode('.', array_reverse(explode(':', rtrim($httpHost, '.')))));
for ($i = count($uri) - 1; $i > 0; $i--) {
for ($j = count($server); $j > 0; $j--) {
$dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
if (file_exists("$confdir/$dir" . $flagFile)) {
\Civi::$statics[__CLASS__]['drupalSiteName'] = $dir;
return \Civi::$statics[__CLASS__]['drupalSiteName'];
}
// check for alias
if (isset($sites[$dir]) && file_exists("$confdir/{$sites[$dir]}" . $flagFile)) {
\Civi::$statics[__CLASS__]['drupalSiteName'] = $sites[$dir];
return \Civi::$statics[__CLASS__]['drupalSiteName'];
}
}
}
}
}

View file

@ -0,0 +1,891 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Joomla specific stuff goes here.
*/
class CRM_Utils_System_Joomla extends CRM_Utils_System_Base {
/**
* Class constructor.
*/
public function __construct() {
/**
* 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
* functions and leave the codebase oblivious to the type of CMS
* @deprecated
* @var bool
*/
$this->is_drupal = FALSE;
}
/**
* @inheritDoc
*/
public function createUser(&$params, $mail) {
$baseDir = JPATH_SITE;
require_once $baseDir . '/components/com_users/models/registration.php';
$userParams = JComponentHelper::getParams('com_users');
$model = new UsersModelRegistration();
$ufID = NULL;
// get the default usertype
$userType = $userParams->get('new_usertype');
if (!$userType) {
$userType = 2;
}
if (isset($params['name'])) {
$fullname = trim($params['name']);
}
elseif (isset($params['contactID'])) {
$fullname = trim(CRM_Contact_BAO_Contact::displayName($params['contactID']));
}
else {
$fullname = trim($params['cms_name']);
}
// Prepare the values for a new Joomla user.
$values = array();
$values['name'] = $fullname;
$values['username'] = trim($params['cms_name']);
$values['password1'] = $values['password2'] = $params['cms_pass'];
$values['email1'] = $values['email2'] = trim($params[$mail]);
$lang = JFactory::getLanguage();
$lang->load('com_users', $baseDir);
$register = $model->register($values);
$ufID = JUserHelper::getUserId($values['username']);
return $ufID;
}
/**
* @inheritDoc
*/
public function updateCMSName($ufID, $ufName) {
$ufID = CRM_Utils_Type::escape($ufID, 'Integer');
$ufName = CRM_Utils_Type::escape($ufName, 'String');
$values = array();
$user = JUser::getInstance($ufID);
$values['email'] = $ufName;
$user->bind($values);
$user->save();
}
/**
* Check if username and email exists in the Joomla db.
*
* @param array $params
* Array of name and mail values.
* @param array $errors
* Array of errors.
* @param string $emailName
* Field label for the 'email'.
*/
public function checkUserNameEmailExists(&$params, &$errors, $emailName = 'email') {
$config = CRM_Core_Config::singleton();
$dao = new CRM_Core_DAO();
$name = $dao->escape(CRM_Utils_Array::value('name', $params));
$email = $dao->escape(CRM_Utils_Array::value('mail', $params));
//don't allow the special characters and min. username length is two
//regex \\ to match a single backslash would become '/\\\\/'
$isNotValid = (bool) preg_match('/[\<|\>|\"|\'|\%|\;|\(|\)|\&|\\\\|\/]/im', $name);
if ($isNotValid || strlen($name) < 2) {
$errors['cms_name'] = ts('Your username contains invalid characters or is too short');
}
$JUserTable = &JTable::getInstance('User', 'JTable');
$db = $JUserTable->getDbo();
$query = $db->getQuery(TRUE);
$query->select('username, email');
$query->from($JUserTable->getTableName());
// LOWER in query below roughly translates to 'hurt my database without deriving any benefit' See CRM-19811.
$query->where('(LOWER(username) = LOWER(\'' . $name . '\')) OR (LOWER(email) = LOWER(\'' . $email . '\'))');
$db->setQuery($query, 0, 10);
$users = $db->loadAssocList();
$row = array();
if (count($users)) {
$row = $users[0];
}
if (!empty($row)) {
$dbName = CRM_Utils_Array::value('username', $row);
$dbEmail = CRM_Utils_Array::value('email', $row);
if (strtolower($dbName) == strtolower($name)) {
$errors['cms_name'] = ts('The username %1 is already taken. Please select another username.',
array(1 => $name)
);
}
if (strtolower($dbEmail) == strtolower($email)) {
$resetUrl = str_replace('administrator/', '', $config->userFrameworkBaseURL) . 'index.php?option=com_users&view=reset';
$errors[$emailName] = ts('The email address %1 already has an account associated with it. <a href="%2">Have you forgotten your password?</a>',
array(1 => $email, 2 => $resetUrl)
);
}
}
}
/**
* @inheritDoc
*/
public function setTitle($title, $pageTitle = NULL) {
if (!$pageTitle) {
$pageTitle = $title;
}
$template = CRM_Core_Smarty::singleton();
$template->assign('pageTitle', $pageTitle);
$document = JFactory::getDocument();
$document->setTitle($title);
}
/**
* @inheritDoc
*/
public function appendBreadCrumb($breadCrumbs) {
$template = CRM_Core_Smarty::singleton();
$bc = $template->get_template_vars('breadcrumb');
if (is_array($breadCrumbs)) {
foreach ($breadCrumbs as $crumbs) {
if (stripos($crumbs['url'], 'id%%')) {
$args = array('cid', 'mid');
foreach ($args as $a) {
$val = CRM_Utils_Request::retrieve($a, 'Positive', CRM_Core_DAO::$_nullObject,
FALSE, NULL, $_GET
);
if ($val) {
$crumbs['url'] = str_ireplace("%%{$a}%%", $val, $crumbs['url']);
}
}
}
$bc[] = $crumbs;
}
}
$template->assign_by_ref('breadcrumb', $bc);
}
/**
* @inheritDoc
*/
public function resetBreadCrumb() {
}
/**
* @inheritDoc
*/
public function addHTMLHead($string = NULL) {
if ($string) {
$document = JFactory::getDocument();
$document->addCustomTag($string);
}
}
/**
* @inheritDoc
*/
public function addStyleUrl($url, $region) {
if ($region == 'html-header') {
$document = JFactory::getDocument();
$document->addStyleSheet($url);
return TRUE;
}
return FALSE;
}
/**
* @inheritDoc
*/
public function addStyle($code, $region) {
if ($region == 'html-header') {
$document = JFactory::getDocument();
$document->addStyleDeclaration($code);
return TRUE;
}
return FALSE;
}
/**
* @inheritDoc
*/
public function url(
$path = NULL,
$query = NULL,
$absolute = FALSE,
$fragment = NULL,
$frontend = FALSE,
$forceBackend = FALSE
) {
$config = CRM_Core_Config::singleton();
$separator = '&';
$Itemid = '';
$script = '';
$path = CRM_Utils_String::stripPathChars($path);
if ($config->userFrameworkFrontend) {
$script = 'index.php';
if (JRequest::getVar("Itemid")) {
$Itemid = "{$separator}Itemid=" . JRequest::getVar("Itemid");
}
}
if (isset($fragment)) {
$fragment = '#' . $fragment;
}
$base = $absolute ? $config->userFrameworkBaseURL : $config->useFrameworkRelativeBase;
if (!empty($query)) {
$url = "{$base}{$script}?option=com_civicrm{$separator}task={$path}{$Itemid}{$separator}{$query}{$fragment}";
}
else {
$url = "{$base}{$script}?option=com_civicrm{$separator}task={$path}{$Itemid}{$fragment}";
}
// gross hack for joomla, we are in the backend and want to send a frontend url
if ($frontend && $config->userFramework == 'Joomla') {
// handle both joomla v1.5 and v1.6, CRM-7939
$url = str_replace('/administrator/index2.php', '/index.php', $url);
$url = str_replace('/administrator/index.php', '/index.php', $url);
// CRM-8215
$url = str_replace('/administrator/', '/index.php', $url);
}
elseif ($forceBackend) {
if (defined('JVERSION')) {
$joomlaVersion = JVERSION;
}
else {
$jversion = new JVersion();
$joomlaVersion = $jversion->getShortVersion();
}
if (version_compare($joomlaVersion, '1.6') >= 0) {
$url = str_replace('/index.php', '/administrator/index.php', $url);
}
}
return $url;
}
/**
* Set the email address of the user.
*
* @param object $user
* Handle to the user object.
*/
public function setEmail(&$user) {
global $database;
$query = $db->getQuery(TRUE);
$query->select($db->quoteName('email'))
->from($db->quoteName('#__users'))
->where($db->quoteName('id') . ' = ' . $user->id);
$database->setQuery($query);
$user->email = $database->loadResult();
}
/**
* @inheritDoc
*/
public function authenticate($name, $password, $loadCMSBootstrap = FALSE, $realPath = NULL) {
require_once 'DB.php';
$config = CRM_Core_Config::singleton();
$user = NULL;
if ($loadCMSBootstrap) {
$bootStrapParams = array();
if ($name && $password) {
$bootStrapParams = array(
'name' => $name,
'pass' => $password,
);
}
CRM_Utils_System::loadBootStrap($bootStrapParams, TRUE, TRUE, FALSE);
}
jimport('joomla.application.component.helper');
jimport('joomla.database.table');
jimport('joomla.user.helper');
$JUserTable = JTable::getInstance('User', 'JTable');
$db = $JUserTable->getDbo();
$query = $db->getQuery(TRUE);
$query->select('id, name, username, email, password');
$query->from($JUserTable->getTableName());
$query->where('(LOWER(username) = LOWER(\'' . $name . '\')) AND (block = 0)');
$db->setQuery($query, 0, 0);
$users = $db->loadObjectList();
$row = array();
if (count($users)) {
$row = $users[0];
}
$joomlaBase = self::getBasePath();
self::getJVersion($joomlaBase);
if (!empty($row)) {
$dbPassword = $row->password;
$dbId = $row->id;
$dbEmail = $row->email;
if (version_compare(JVERSION, '2.5.18', 'lt') ||
(version_compare(JVERSION, '3.0', 'ge') && version_compare(JVERSION, '3.2.1', 'lt'))
) {
// now check password
list($hash, $salt) = explode(':', $dbPassword);
$cryptpass = md5($password . $salt);
if ($hash != $cryptpass) {
return FALSE;
}
}
else {
if (!JUserHelper::verifyPassword($password, $dbPassword, $dbId)) {
return FALSE;
}
if (version_compare(JVERSION, '3.8.0', 'ge')) {
jimport('joomla.application.helper');
jimport('joomla.application.cms');
jimport('joomla.application.administrator');
}
//include additional files required by Joomla 3.2.1+
elseif (version_compare(JVERSION, '3.2.1', 'ge')) {
require_once $joomlaBase . '/libraries/cms/application/helper.php';
require_once $joomlaBase . '/libraries/cms/application/cms.php';
require_once $joomlaBase . '/libraries/cms/application/administrator.php';
}
}
CRM_Core_BAO_UFMatch::synchronizeUFMatch($row, $dbId, $dbEmail, 'Joomla');
$contactID = CRM_Core_BAO_UFMatch::getContactId($dbId);
if (!$contactID) {
return FALSE;
}
return array($contactID, $dbId, mt_rand());
}
return FALSE;
}
/**
* Set a init session with user object.
*
* @param array $data
* Array with user specific data.
*/
public function setUserSession($data) {
list($userID, $ufID) = $data;
$user = new JUser($ufID);
$session = JFactory::getSession();
$session->set('user', $user);
parent::setUserSession($data);
}
/**
* FIXME: Do something
*
* @param string $message
*/
public function setMessage($message) {
}
/**
* @param \string $username
* @param \string $password
*
* @return bool
*/
public function loadUser($username, $password = NULL) {
$uid = JUserHelper::getUserId($username);
if (empty($uid)) {
return FALSE;
}
$contactID = CRM_Core_BAO_UFMatch::getContactId($uid);
if (!empty($password)) {
$instance = JFactory::getApplication('site');
$params = array(
'username' => $username,
'password' => $password,
);
//perform the login action
$instance->login($params);
}
$session = CRM_Core_Session::singleton();
$session->set('ufID', $uid);
$session->set('userID', $contactID);
return TRUE;
}
/**
* FIXME: Use CMS-native approach
*/
public function permissionDenied() {
CRM_Core_Error::fatal(ts('You do not have permission to access this page.'));
}
/**
* @inheritDoc
*/
public function logout() {
session_destroy();
CRM_Utils_System::setHttpHeader("Location", "index.php");
}
/**
* @inheritDoc
*/
public function getUFLocale() {
if (defined('_JEXEC')) {
$conf = JFactory::getConfig();
$locale = $conf->get('language');
return str_replace('-', '_', $locale);
}
return NULL;
}
/**
* @inheritDoc
*/
public function setUFLocale($civicrm_language) {
// TODO
return TRUE;
}
/**
* @inheritDoc
*/
public function getVersion() {
if (class_exists('JVersion')) {
$version = new JVersion();
return $version->getShortVersion();
}
else {
return 'Unknown';
}
}
public function getJVersion($joomlaBase) {
// Files may be in different places depending on Joomla version
if (!defined('JVERSION')) {
// Joomla 3.8.0+
$versionPhp = $joomlaBase . '/libraries/src/Version.php';
if (!file_exists($versionPhp)) {
// Joomla < 3.8.0
$versionPhp = $joomlaBase . '/libraries/cms/version/version.php';
}
require $versionPhp;
$jversion = new JVersion();
define('JVERSION', $jversion->getShortVersion());
}
}
/**
* Setup the base path related constant.
* @return mixed
*/
public function getBasePath() {
global $civicrm_root;
$joomlaPath = explode('/administrator', $civicrm_root);
$joomlaBase = $joomlaPath[0];
return $joomlaBase;
}
/**
* Load joomla bootstrap.
*
* @param array $params
* with uid or name and password.
* @param bool $loadUser
* load cms user?.
* @param bool|\throw $throwError throw error on failure?
* @param null $realPath
* @param bool $loadDefines
*
* @return bool
*/
public function loadBootStrap($params = array(), $loadUser = TRUE, $throwError = TRUE, $realPath = NULL, $loadDefines = TRUE) {
$joomlaBase = self::getBasePath();
// load BootStrap here if needed
// We are a valid Joomla entry point.
if (!defined('_JEXEC') && $loadDefines) {
define('_JEXEC', 1);
define('DS', DIRECTORY_SEPARATOR);
define('JPATH_BASE', $joomlaBase . '/administrator');
require $joomlaBase . '/administrator/includes/defines.php';
require $joomlaBase . '/administrator/includes/framework.php';
}
// Get the framework.
if (file_exists($joomlaBase . '/libraries/import.legacy.php')) {
require $joomlaBase . '/libraries/import.legacy.php';
}
require $joomlaBase . '/libraries/cms.php';
require $joomlaBase . '/libraries/import.php';
require $joomlaBase . '/libraries/joomla/event/dispatcher.php';
require_once $joomlaBase . '/configuration.php';
self::getJVersion($joomlaBase);
if (version_compare(JVERSION, '3.0', 'lt')) {
require $joomlaBase . '/libraries/joomla/environment/uri.php';
require $joomlaBase . '/libraries/joomla/application/component/helper.php';
}
else {
jimport('joomla.environment.uri');
}
jimport('joomla.application.cli');
if (!defined('JDEBUG')) {
define('JDEBUG', FALSE);
}
// CRM-14281 Joomla wasn't available during bootstrap, so hook_civicrm_config never executes.
$config = CRM_Core_Config::singleton();
CRM_Utils_Hook::config($config);
return TRUE;
}
/**
* @inheritDoc
*/
public function isUserLoggedIn() {
$user = JFactory::getUser();
return ($user->guest) ? FALSE : TRUE;
}
/**
* @inheritDoc
*/
public function isUserRegistrationPermitted() {
$userParams = JComponentHelper::getParams('com_users');
if (!$userParams->get('allowUserRegistration')) {
return FALSE;
}
return TRUE;
}
/**
* @inheritDoc
*/
public function isPasswordUserGenerated() {
return TRUE;
}
/**
* @inheritDoc
*/
public function getLoggedInUfID() {
$user = JFactory::getUser();
return ($user->guest) ? NULL : $user->id;
}
/**
* @inheritDoc
*/
public function getLoggedInUniqueIdentifier() {
$user = JFactory::getUser();
return $this->getUniqueIdentifierFromUserObject($user);
}
/**
* @inheritDoc
*/
public function getUserIDFromUserObject($user) {
return !empty($user->id) ? $user->id : NULL;
}
/**
* @inheritDoc
*/
public function getUniqueIdentifierFromUserObject($user) {
return ($user->guest) ? NULL : $user->email;
}
/**
* @inheritDoc
*/
public function getTimeZoneString() {
$timezone = JFactory::getConfig()->get('offset');
return !$timezone ? date_default_timezone_get() : $timezone;
}
/**
* Get a list of all installed modules, including enabled and disabled ones
*
* @return array
* CRM_Core_Module
*/
public function getModules() {
$result = array();
$db = JFactory::getDbo();
$query = $db->getQuery(TRUE);
$query->select('type, folder, element, enabled')
->from('#__extensions')
->where('type =' . $db->Quote('plugin'));
$plugins = $db->setQuery($query)->loadAssocList();
foreach ($plugins as $plugin) {
// question: is the folder really a critical part of the plugin's name?
$name = implode('.', array('joomla', $plugin['type'], $plugin['folder'], $plugin['element']));
$result[] = new CRM_Core_Module($name, $plugin['enabled'] ? TRUE : FALSE);
}
return $result;
}
/**
* @inheritDoc
*/
public function getLoginURL($destination = '') {
$config = CRM_Core_Config::singleton();
$loginURL = $config->userFrameworkBaseURL;
$loginURL = str_replace('administrator/', '', $loginURL);
$loginURL .= 'index.php?option=com_users&view=login';
//CRM-14872 append destination
if (!empty($destination)) {
$loginURL .= '&return=' . urlencode(base64_encode($destination));
}
return $loginURL;
}
/**
* @inheritDoc
*/
public function getLoginDestination(&$form) {
$args = NULL;
$id = $form->get('id');
if ($id) {
$args .= "&id=$id";
}
else {
$gid = $form->get('gid');
if ($gid) {
$args .= "&gid=$gid";
}
else {
// Setup Personal Campaign Page link uses pageId
$pageId = $form->get('pageId');
if ($pageId) {
$component = $form->get('component');
$args .= "&pageId=$pageId&component=$component&action=add";
}
}
}
$destination = NULL;
if ($args) {
// append destination so user is returned to form they came from after login
$args = 'reset=1' . $args;
$destination = CRM_Utils_System::url(CRM_Utils_System::currentPath(), $args, TRUE, NULL, FALSE, TRUE);
}
return $destination;
}
/**
* Determine the location of the CMS root.
*
* @return string|NULL
* local file system path to CMS root, or NULL if it cannot be determined
*/
public function cmsRootPath() {
global $civicrm_paths;
if (!empty($civicrm_paths['cms.root']['path'])) {
return $civicrm_paths['cms.root']['path'];
}
list($url, $siteName, $siteRoot) = $this->getDefaultSiteSettings();
if (file_exists("$siteRoot/administrator/index.php")) {
return $siteRoot;
}
return NULL;
}
/**
* @inheritDoc
*/
public function getDefaultSiteSettings($dir = NULL) {
$config = CRM_Core_Config::singleton();
$url = preg_replace(
'|/administrator|',
'',
$config->userFrameworkBaseURL
);
// CRM-19453 revisited. Under Windows, the pattern wasn't recognised.
// This is the original pattern, but it doesn't work under Windows.
// By setting the pattern to the one used before the change first and only
// changing it means that the change code only affects Windows users.
$pattern = '|/media/civicrm/.*$|';
if (DIRECTORY_SEPARATOR == '\\') {
// This regular expression will handle Windows as well as Linux
// and any combination of forward and back slashes in directory
// separators. We only apply it if the directory separator is the one
// used by Windows.
$pattern = '|[\\\\/]media[\\\\/]civicrm[\\\\/].*$|';
}
$siteRoot = preg_replace(
$pattern,
'',
$config->imageUploadDir
);
return array($url, NULL, $siteRoot);
}
/**
* @inheritDoc
*/
public function getUserRecordUrl($contactID) {
$uid = CRM_Core_BAO_UFMatch::getUFId($contactID);
$userRecordUrl = NULL;
// if logged in user has user edit access, then allow link to other users joomla profile
if (JFactory::getUser()->authorise('core.edit', 'com_users')) {
return CRM_Core_Config::singleton()->userFrameworkBaseURL . "index.php?option=com_users&view=user&task=user.edit&id=" . $uid;
}
elseif (CRM_Core_Session::singleton()->get('userID') == $contactID) {
return CRM_Core_Config::singleton()->userFrameworkBaseURL . "index.php?option=com_admin&view=profile&layout=edit&id=" . $uid;
}
}
/**
* @inheritDoc
*/
public function checkPermissionAddUser() {
if (JFactory::getUser()->authorise('core.create', 'com_users')) {
return TRUE;
}
}
/**
* Output code from error function.
* @param string $content
*/
public function outputError($content) {
if (class_exists('JErrorPage')) {
$error = new Exception($content);
JErrorPage::render($error);
}
elseif (class_exists('JError')) {
JError::raiseError('CiviCRM-001', $content);
}
else {
parent::outputError($content);
}
}
/**
* Append Joomla js to coreResourcesList.
*
* @param array $list
*/
public function appendCoreResources(&$list) {
$list[] = 'js/crm.joomla.js';
}
/**
* @inheritDoc
*/
public function synchronizeUsers() {
$config = CRM_Core_Config::singleton();
if (PHP_SAPI != 'cli') {
set_time_limit(300);
}
$id = 'id';
$mail = 'email';
$name = 'name';
$JUserTable = &JTable::getInstance('User', 'JTable');
$db = $JUserTable->getDbo();
$query = $db->getQuery(TRUE);
$query->select($id . ', ' . $mail . ', ' . $name);
$query->from($JUserTable->getTableName());
$query->where($mail != '');
$db->setQuery($query);
$users = $db->loadObjectList();
$user = new StdClass();
$uf = $config->userFramework;
$contactCount = 0;
$contactCreated = 0;
$contactMatching = 0;
for ($i = 0; $i < count($users); $i++) {
$user->$id = $users[$i]->$id;
$user->$mail = $users[$i]->$mail;
$user->$name = $users[$i]->$name;
$contactCount++;
if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user,
$users[$i]->$id,
$users[$i]->$mail,
$uf,
1,
'Individual',
TRUE
)
) {
$contactCreated++;
}
else {
$contactMatching++;
}
if (is_object($match)) {
$match->free();
}
}
return array(
'contactCount' => $contactCount,
'contactMatching' => $contactMatching,
'contactCreated' => $contactCreated,
);
}
}

View file

@ -0,0 +1,127 @@
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2017 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
/**
* Soap specific stuff goes here.
*/
class CRM_Utils_System_Soap extends CRM_Utils_System_Base {
/**
* UF container variables.
*/
static $uf = NULL;
static $ufClass = NULL;
/**
* Given a permission string, check for access requirements
*
* @param string $str
* The permission to check.
*
* @return bool
* true if yes, else false
*/
public function checkPermission($str) {
return TRUE;
}
/**
* @inheritDoc
*/
public function url($path = NULL, $query = NULL, $absolute = TRUE, $fragment = NULL) {
if (isset(self::$ufClass)) {
$className = self::$ufClass;
$url = $className::url($path, $query, $absolute, $fragment);
return $url;
}
else {
return NULL;
}
}
/**
* FIXME: Can this override be removed in favor of the parent?
* @inheritDoc
*/
public function postURL($action) {
return NULL;
}
/**
* Set the email address of the user.
*
* @param object $user
* Handle to the user object.
*/
public function setEmail(&$user) {
}
/**
* @inheritDoc
*/
public function authenticate($name, $pass) {
if (isset(self::$ufClass)) {
$className = self::$ufClass;
$result =& $className::authenticate($name, $pass);
return $result;
}
else {
return NULL;
}
}
/**
* Swap the current UF for soap.
*/
public function swapUF() {
$config = CRM_Core_Config::singleton();
self::$uf = $config->userFramework;
$config->userFramework = 'Soap';
self::$ufClass = $config->userFrameworkClass;
$config->userFrameworkClass = 'CRM_Utils_System_Soap';
}
/**
* Get user login URL for hosting CMS (method declared in each CMS system class)
*
* @param string $destination
*
* @throws Exception
*/
public function getLoginURL($destination = '') {
throw new Exception("Method not implemented: getLoginURL");
}
}

Some files were not shown because too many files have changed in this diff Show more