From: Eileen McNaughton Date: Wed, 5 Aug 2020 12:18:52 +0000 (+1200) Subject: Merge pull request #17981 from eileenmcnaughton/merge_form X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=0524386078c90467b18b8c604fbfe339055cc9ca;hp=c813e8f4b128cb2e03a7f81ceaa7ebb4c01bbc3e;p=civicrm-core.git Merge pull request #17981 from eileenmcnaughton/merge_form [REF] Move handling of form elements back to the Form --- diff --git a/CRM/Activity/BAO/Activity.php b/CRM/Activity/BAO/Activity.php index a4065942ca..3c8a0eb155 100644 --- a/CRM/Activity/BAO/Activity.php +++ b/CRM/Activity/BAO/Activity.php @@ -1783,16 +1783,14 @@ WHERE activity.id IN ($activityIds)"; * particular component object. * * @return string + * @throws \CRM_Core_Exception */ public static function getActivitySubject($entityObj) { + // @todo determine the subject on the appropriate entity rather than from the activity. switch ($entityObj->__table) { case 'civicrm_membership': - $membershipType = CRM_Member_PseudoConstant::membershipType($entityObj->membership_type_id); - $subject = $membershipType ? $membershipType : ts('Membership'); - - if (is_array($subject)) { - $subject = implode(", ", $subject); - } + $membershipType = CRM_Core_PseudoConstant::getLabel('CRM_Member_BAO_Membership', 'membership_type_id', $entityObj->membership_type_id); + $subject = $membershipType ?: ts('Membership'); if (!CRM_Utils_System::isNull($entityObj->source)) { $subject .= " - {$entityObj->source}"; @@ -1803,7 +1801,7 @@ WHERE activity.id IN ($activityIds)"; $subject .= sprintf(' (by %s)', $displayName); } - $subject .= " - Status: " . CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipStatus', $entityObj->status_id, 'label'); + $subject .= ' - Status: ' . CRM_Core_PseudoConstant::getLabel('CRM_Member_BAO_Membership', 'status_id', $entityObj->status_id); return $subject; case 'civicrm_participant': @@ -2132,91 +2130,75 @@ AND cl.modified_id = c.id self::$_exportableFields[$name] = []; // TODO: ideally we should retrieve all fields from xml, in this case since activity processing is done - // my case hence we have defined fields as case_* - if ($name === 'Activity') { - $exportableFields = CRM_Activity_DAO_Activity::export(); - $exportableFields['source_contact_id'] = [ - 'title' => ts('Source Contact ID'), - 'type' => CRM_Utils_Type::T_INT, - ]; - $exportableFields['source_contact'] = [ - 'title' => ts('Source Contact'), + $exportableFields = CRM_Activity_DAO_Activity::export(); + $exportableFields['source_contact_id'] = [ + 'title' => ts('Source Contact ID'), + 'type' => CRM_Utils_Type::T_INT, + ]; + $exportableFields['source_contact'] = [ + 'title' => ts('Source Contact'), + 'type' => CRM_Utils_Type::T_STRING, + ]; + + // @todo - remove these - they are added by CRM_Core_DAO::appendPseudoConstantsToFields + // below. That search label stuff is referenced in search builder but is likely just + // a hack that duplicates, maybe differently, other functionality. + $activityFields = [ + 'activity_type' => [ + 'title' => ts('Activity Type'), + 'name' => 'activity_type', 'type' => CRM_Utils_Type::T_STRING, - ]; + 'searchByLabel' => TRUE, + ], + 'activity_status' => [ + 'title' => ts('Activity Status'), + 'name' => 'activity_status', + 'type' => CRM_Utils_Type::T_STRING, + 'searchByLabel' => TRUE, + ], + 'activity_priority' => [ + 'title' => ts('Activity Priority'), + 'name' => 'activity_priority', + 'type' => CRM_Utils_Type::T_STRING, + 'searchByLabel' => TRUE, + ], + ]; + $fields = array_merge($activityFields, $exportableFields); + $fields['activity_type_id']['title'] = ts('Activity Type ID'); + $fields['activity_priority_id'] = $fields['priority_id']; - // @todo - remove these - they are added by CRM_Core_DAO::appendPseudoConstantsToFields - // below. That search label stuff is referenced in search builder but is likely just - // a hack that duplicates, maybe differently, other functionality. - $Activityfields = [ - 'activity_type' => [ - 'title' => ts('Activity Type'), - 'name' => 'activity_type', - 'type' => CRM_Utils_Type::T_STRING, - 'searchByLabel' => TRUE, - ], - 'activity_status' => [ - 'title' => ts('Activity Status'), - 'name' => 'activity_status', - 'type' => CRM_Utils_Type::T_STRING, - 'searchByLabel' => TRUE, - ], - 'activity_priority' => [ - 'title' => ts('Activity Priority'), - 'name' => 'activity_priority', - 'type' => CRM_Utils_Type::T_STRING, - 'searchByLabel' => TRUE, - ], - ]; - $fields = array_merge($Activityfields, $exportableFields); - $fields['activity_type_id']['title'] = ts('Activity Type ID'); - } - else { + if ($name === 'Case') { + // Now add "case_activity" fields // Set title to activity fields. - $fields = [ - 'case_activity_subject' => [ - 'title' => ts('Activity Subject'), - 'type' => CRM_Utils_Type::T_STRING, - ], + $caseActivityFields = [ 'case_source_contact_id' => [ 'title' => ts('Activity Reporter'), 'type' => CRM_Utils_Type::T_STRING, ], - 'case_recent_activity_date' => [ - 'title' => ts('Activity Actual Date'), + 'case_activity_date_time' => [ + 'title' => ts('Activity Date'), 'type' => CRM_Utils_Type::T_DATE, ], - 'case_scheduled_activity_date' => [ - 'title' => ts('Activity Scheduled Date'), - 'type' => CRM_Utils_Type::T_DATE, - ], - 'case_recent_activity_type' => [ + 'case_activity_type' => [ 'title' => ts('Activity Type'), 'type' => CRM_Utils_Type::T_STRING, ], - 'case_activity_status' => [ - 'title' => ts('Activity Status'), - 'type' => CRM_Utils_Type::T_STRING, - ], - 'case_activity_duration' => [ - 'title' => ts('Activity Duration'), - 'type' => CRM_Utils_Type::T_INT, - ], 'case_activity_medium_id' => [ 'title' => ts('Activity Medium'), 'type' => CRM_Utils_Type::T_INT, ], - 'case_activity_details' => [ - 'title' => ts('Activity Details'), - 'type' => CRM_Utils_Type::T_TEXT, - ], 'case_activity_is_auto' => [ 'title' => ts('Activity Auto-generated?'), 'type' => CRM_Utils_Type::T_BOOLEAN, ], ]; + $caseStandardFields = ['activity_subject', 'activity_status', 'activity_duration', 'activity_details']; + foreach ($caseStandardFields as $key) { + $caseActivityFields['case_' . $key] = $fields[$key]; + } + $fields = $caseActivityFields; } - - // add custom data for case activities + // Add custom data $fields = array_merge($fields, CRM_Core_BAO_CustomField::getFieldsForImport('Activity')); CRM_Core_DAO::appendPseudoConstantsToFields($fields); self::$_exportableFields[$name] = $fields; diff --git a/CRM/Admin/Form/Setting/Localization.php b/CRM/Admin/Form/Setting/Localization.php index 83194effce..d33ebb369d 100644 --- a/CRM/Admin/Form/Setting/Localization.php +++ b/CRM/Admin/Form/Setting/Localization.php @@ -325,8 +325,6 @@ class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting { $ufm = new CRM_Core_DAO_UFMatch(); $ufm->contact_id = $session->get('userID'); if ($newLocale && $ufm->find(TRUE)) { - $ufm->language = $newLocale; - $ufm->save(); $session->set('lcMessages', $newLocale); } } diff --git a/CRM/Admin/Page/Job.php b/CRM/Admin/Page/Job.php index e8ba06d9d5..2164d2b7ce 100644 --- a/CRM/Admin/Page/Job.php +++ b/CRM/Admin/Page/Job.php @@ -142,6 +142,17 @@ class CRM_Admin_Page_Job extends CRM_Core_Page_Basic { if (CRM_Core_Config::environment() != 'Production') { CRM_Core_Session::setStatus(ts('Execution of scheduled jobs has been turned off by default since this is a non-production environment. You can override this for particular jobs by adding runInNonProductionEnvironment=TRUE as a parameter.'), ts("Non-production Environment"), "warning", array('expires' => 0)); } + else { + $cronError = Civi\Api4\System::check(FALSE) + ->addWhere('name', '=', 'checkLastCron') + ->addWhere('severity_id', '>', 1) + ->setIncludeDisabled(TRUE) + ->execute() + ->first(); + if ($cronError) { + CRM_Core_Session::setStatus($cronError['message'], $cronError['title'], 'alert', ['expires' => 0]); + } + } $sj = new CRM_Core_JobManager(); $rows = $temp = []; diff --git a/CRM/Case/BAO/Query.php b/CRM/Case/BAO/Query.php index 71a5e0f147..6958dc6674 100644 --- a/CRM/Case/BAO/Query.php +++ b/CRM/Case/BAO/Query.php @@ -100,9 +100,9 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_tables['case_relation_type'] = $query->_whereTables['case_relation_type'] = 1; } - if (!empty($query->_returnProperties['case_recent_activity_date'])) { - $query->_select['case_recent_activity_date'] = "case_activity.activity_date_time as case_recent_activity_date"; - $query->_element['case_recent_activity_date'] = 1; + if (!empty($query->_returnProperties['case_activity_date_time'])) { + $query->_select['case_activity_date_time'] = "case_activity.activity_date_time as case_activity_date_time"; + $query->_element['case_activity_date_time'] = 1; $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; } @@ -121,6 +121,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_tables['civicrm_case'] = 1; } + // @todo switch to a more standard case_source_contact as the key where we want the name not the id. if (!empty($query->_returnProperties['case_source_contact_id'])) { $query->_select['case_source_contact_id'] = "civicrm_case_reporter.sort_name as case_source_contact_id"; $query->_element['case_source_contact_id'] = 1; @@ -134,7 +135,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_select['case_activity_status_id'] = "rec_activity_status.id as case_activity_status_id"; $query->_element['case_activity_status_id'] = 1; $query->_tables['case_activity'] = 1; - $query->_tables['recent_activity_status'] = 1; + $query->_tables['case_activity_status'] = 1; $query->_tables['civicrm_case_contact'] = 1; $query->_tables['civicrm_case'] = 1; } @@ -143,7 +144,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_select['case_activity_status'] = "rec_activity_status.label as case_activity_status"; $query->_element['case_activity_status'] = 1; $query->_tables['case_activity'] = 1; - $query->_tables['recent_activity_status'] = 1; + $query->_tables['case_activity_status'] = 1; $query->_tables['civicrm_case_contact'] = 1; $query->_tables['civicrm_case'] = 1; } @@ -157,7 +158,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { } if (!empty($query->_returnProperties['case_activity_medium_id'])) { - $query->_select['case_activity_medium_id'] = "recent_activity_medium.label as case_activity_medium_id"; + $query->_select['case_activity_medium_id'] = "case_activity_medium.label as case_activity_medium_id"; $query->_element['case_activity_medium_id'] = 1; $query->_tables['case_activity'] = 1; $query->_tables['case_activity_medium'] = 1; @@ -181,16 +182,16 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_tables['civicrm_case'] = 1; } - if (!empty($query->_returnProperties['case_scheduled_activity_date'])) { - $query->_select['case_scheduled_activity_date'] = "case_activity.activity_date_time as case_scheduled_activity_date"; - $query->_element['case_scheduled_activity_date'] = 1; + if (!empty($query->_returnProperties['case_activity_date_time'])) { + $query->_select['case_activity_date_time'] = "case_activity.activity_date_time as case_activity_date_time"; + $query->_element['case_activity_date_time'] = 1; $query->_tables['case_activity'] = 1; $query->_tables['civicrm_case_contact'] = 1; $query->_tables['civicrm_case'] = 1; } - if (!empty($query->_returnProperties['case_recent_activity_type'])) { - $query->_select['case_recent_activity_type'] = "rec_activity_type.label as case_recent_activity_type"; - $query->_element['case_recent_activity_type'] = 1; + if (!empty($query->_returnProperties['case_activity_type'])) { + $query->_select['case_activity_type'] = "rec_activity_type.label as case_activity_type"; + $query->_element['case_activity_type'] = 1; $query->_tables['case_activity'] = 1; $query->_tables['case_activity_type'] = 1; $query->_tables['civicrm_case_contact'] = 1; @@ -320,6 +321,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->handleWhereFromMetadata($fieldSpec, $name, $value, $op); return; + // @todo switch to a more standard case_source_contact as the key where we want the name not the id. case 'case_source_contact_id': $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_case_reporter.sort_name", $op, $value, 'String'); $query->_qill[$grouping][] = ts("Activity Reporter %1 '%2'", [1 => $op, 2 => $value]); @@ -329,31 +331,19 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; return; - case 'case_recent_activity_date': + case 'case_activity_date_time': $date = CRM_Utils_Date::format($value); $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.activity_date_time", $op, $date, 'Date'); if ($date) { $date = CRM_Utils_Date::customFormat($date); - $query->_qill[$grouping][] = ts("Activity Actual Date %1 %2", [1 => $op, 2 => $date]); + $query->_qill[$grouping][] = ts("Activity Date %1 %2", [1 => $op, 2 => $date]); } $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; return; - case 'case_scheduled_activity_date': - $date = CRM_Utils_Date::format($value); - $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.activity_date_time", $op, $date, 'Date'); - if ($date) { - $date = CRM_Utils_Date::customFormat($date); - $query->_qill[$grouping][] = ts("Activity Schedule Date %1 %2", [1 => $op, 2 => $date]); - } - $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; - $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; - $query->_tables['civicrm_case_contact'] = $query->_whereTables['civicrm_case_contact'] = 1; - return; - - case 'case_recent_activity_type': + case 'case_activity_type': $names = $value; if (($activityType = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_type_id', $value)) != FALSE) { $names = $activityType; @@ -374,7 +364,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { } $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("case_activity.status_id", $op, $value, 'Int'); - $query->_qill[$grouping][] = ts("Activity Type %1 %2", [1 => $op, 2 => $names]); + $query->_qill[$grouping][] = ts("Activity Status %1 %2", [1 => $op, 2 => $names]); $query->_tables['case_activity'] = $query->_whereTables['case_activity'] = 1; $query->_tables['civicrm_case'] = $query->_whereTables['civicrm_case'] = 1; $query->_tables['case_activity_status'] = 1; @@ -537,7 +527,7 @@ class CRM_Case_BAO_Query extends CRM_Core_BAO_Query { $from .= " $side JOIN civicrm_option_value rec_activity_type ON (case_activity.activity_type_id = rec_activity_type.value AND option_group_activity_type.id = rec_activity_type.option_group_id ) "; break; - case 'recent_activity_status': + case 'case_activity_status': $from .= " $side JOIN civicrm_option_group option_group_activity_status ON (option_group_activity_status.name = 'activity_status')"; $from .= " $side JOIN civicrm_option_value rec_activity_status ON (case_activity.status_id = rec_activity_status.value AND option_group_activity_status.id = rec_activity_status.option_group_id ) "; break; @@ -555,7 +545,7 @@ case_relation_type.id = case_relationship.relationship_type_id )"; case 'case_activity_medium': $from .= " $side JOIN civicrm_option_group option_group_activity_medium ON (option_group_activity_medium.name = 'encounter_medium')"; - $from .= " $side JOIN civicrm_option_value recent_activity_medium ON (case_activity.medium_id = recent_activity_medium.value AND option_group_activity_medium.id = recent_activity_medium.option_group_id ) "; + $from .= " $side JOIN civicrm_option_value case_activity_medium ON (case_activity.medium_id = case_activity_medium.value AND option_group_activity_medium.id = case_activity_medium.option_group_id ) "; break; case 'case_activity': @@ -607,11 +597,9 @@ case_relation_type.id = case_relationship.relationship_type_id )"; 'case_type' => 1, 'case_role' => 1, 'case_deleted' => 1, - 'case_recent_activity_date' => 1, - 'case_recent_activity_type' => 1, - 'case_scheduled_activity_date' => 1, + 'case_activity_date_time' => 1, + 'case_activity_type' => 1, 'phone' => 1, - // 'case_scheduled_activity_type'=> 1 ]; if ($includeCustomFields) { @@ -643,6 +631,7 @@ case_relation_type.id = case_relationship.relationship_type_id )"; 'case_start_date' => 1, 'case_end_date' => 1, 'case_subject' => 1, + // @todo switch to a more standard case_source_contact as the key where we want the name not the id. 'case_source_contact_id' => 1, 'case_activity_status' => 1, 'case_activity_duration' => 1, diff --git a/CRM/Contact/BAO/Contact/Permission.php b/CRM/Contact/BAO/Contact/Permission.php index 964fc47295..1aae3cea9a 100644 --- a/CRM/Contact/BAO/Contact/Permission.php +++ b/CRM/Contact/BAO/Contact/Permission.php @@ -307,17 +307,16 @@ AND $operationClause if (CRM_Core_Permission::check('view all contacts') || CRM_Core_Permission::check('edit all contacts') ) { - if (is_array($contactAlias)) { + if (!CRM_Core_Permission::check('access deleted contacts')) { $wheres = []; - foreach ($contactAlias as $alias) { + foreach ((array) $contactAlias as $alias) { // CRM-6181 $wheres[] = "$alias.is_deleted = 0"; } return [NULL, '(' . implode(' AND ', $wheres) . ')']; } else { - // CRM-6181 - return [NULL, "$contactAlias.is_deleted = 0"]; + return [NULL, '( 1 )']; } } @@ -332,14 +331,17 @@ AND $operationClause } $fromClause = implode(" ", $clauses); - $whereClase = NULL; + $whereClause = NULL; } else { $fromClause = " INNER JOIN civicrm_acl_contact_cache aclContactCache ON {$contactAlias}.id = aclContactCache.contact_id "; - $whereClase = " aclContactCache.user_id = $contactID AND $contactAlias.is_deleted = 0"; + $whereClause = " aclContactCache.user_id = $contactID"; + if (!CRM_Core_Permission::check('access deleted contacts')) { + $whereClause .= " AND $contactAlias.is_deleted = 0"; + } } - return [$fromClause, $whereClase]; + return [$fromClause, $whereClause]; } /** diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 648fd5d957..a6b2545d8b 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -517,6 +517,7 @@ class CRM_Contact_BAO_Query { // add activity fields $this->_fields = array_merge($this->_fields, CRM_Activity_BAO_Activity::exportableFields()); + $this->_fields = array_merge($this->_fields, CRM_Activity_BAO_Activity::exportableFields('Case')); // Add hack as no unique name is defined for the field but the search form is in denial. $this->_fields['activity_priority_id'] = $this->_fields['priority_id']; @@ -1487,6 +1488,9 @@ class CRM_Contact_BAO_Query { } if (!empty($this->_permissionWhereClause) && empty($this->_displayRelationshipType)) { + if (!empty($this->_permissionFromClause)) { + $from .= " $this->_permissionFromClause"; + } if (empty($where)) { $where = "WHERE $this->_permissionWhereClause"; } @@ -4603,6 +4607,9 @@ civicrm_relationship.start_date > {$today} $options = $query->_options; if (!empty($query->_permissionWhereClause)) { + if (!empty($query->_permissionFromClause) && !stripos($from, 'aclContactCache')) { + $from .= " $query->_permissionFromClause"; + } if (empty($where)) { $where = "WHERE $query->_permissionWhereClause"; } @@ -4982,10 +4989,7 @@ civicrm_relationship.start_date > {$today} $sqlParts = $this->getSearchSQLParts(NULL, NULL, NULL, FALSE, FALSE, TRUE); $query = "SELECT DISTINCT LEFT(contact_a.sort_name, 1) as sort_name {$sqlParts['from']} - {$sqlParts['where']} - {$sqlParts['having']} - GROUP BY sort_name - ORDER BY sort_name asc"; + {$sqlParts['where']}"; $dao = CRM_Core_DAO::executeQuery($query); return $dao; } @@ -5051,17 +5055,22 @@ civicrm_relationship.start_date > {$today} */ public function generatePermissionClause($onlyDeleted = FALSE, $count = FALSE) { if (!$this->_skipPermission) { - $this->_permissionWhereClause = CRM_ACL_API::whereClause( - CRM_Core_Permission::VIEW, - $this->_tables, - $this->_whereTables, - NULL, - $onlyDeleted, - $this->_skipDeleteClause - ); + $permissionClauses = CRM_Contact_BAO_Contact_Permission::cacheClause(); + $this->_permissionWhereClause = $permissionClauses[1]; + $this->_permissionFromClause = $permissionClauses[0]; - if (!$onlyDeleted && CRM_Core_Permission::check('access deleted contacts')) { - $this->_permissionWhereClause = str_replace(' ( 1 ) ', '(contact_a.is_deleted = 0)', $this->_permissionWhereClause); + if (CRM_Core_Permission::check('access deleted contacts')) { + if (!$onlyDeleted) { + $this->_permissionWhereClause = str_replace('( 1 )', '(contact_a.is_deleted = 0)', $this->_permissionWhereClause); + } + else { + if ($this->_permissionWhereClause === '( 1 )') { + $this->_permissionWhereClause = str_replace('( 1 )', '(contact_a.is_deleted)', $this->_permissionWhereClause); + } + else { + $this->_permissionWhereClause .= " AND (contact_a.is_deleted) "; + } + } } if (isset($this->_tables['civicrm_activity'])) { @@ -5085,19 +5094,6 @@ civicrm_relationship.start_date > {$today} $this->_permissionWhereClause .= '(' . implode(' AND ', $clauses) . ')'; } } - - // regenerate fromClause since permission might have added tables - if ($this->_permissionWhereClause) { - //fix for row count in qill (in contribute/membership find) - if (!$count) { - $this->_useDistinct = TRUE; - } - //CRM-15231 - $this->_fromClause = self::fromClause($this->_tables, NULL, NULL, $this->_primaryLocation, $this->_mode); - $this->_simpleFromClause = self::fromClause($this->_whereTables, NULL, NULL, $this->_primaryLocation, $this->_mode); - // note : this modifies _fromClause and _simpleFromClause - $this->includePseudoFieldsJoin($this->_sort); - } } else { // add delete clause if needed even if we are skipping permission @@ -5129,6 +5125,9 @@ civicrm_relationship.start_date > {$today} */ public function summaryContribution($context = NULL) { list($innerselect, $from, $where, $having) = $this->query(TRUE); + if (!empty($this->_permissionFromClause) && !stripos($from, 'aclContactCache')) { + $from .= " $this->_permissionFromClause"; + } if ($this->_permissionWhereClause) { $where .= " AND " . $this->_permissionWhereClause; } @@ -5832,6 +5831,9 @@ AND displayRelType.is_active = 1 $from = str_replace("INNER JOIN", "LEFT JOIN", $from); $from .= $qcache['from']; $where = $qcache['where']; + if (!empty($this->_permissionFromClause) && !stripos($from, 'aclContactCache')) { + $from .= " $this->_permissionFromClause"; + } if (!empty($this->_permissionWhereClause)) { $where .= "AND $this->_permissionWhereClause"; } diff --git a/CRM/Contact/Form/Search/Advanced.php b/CRM/Contact/Form/Search/Advanced.php index f517389c4d..e579c74331 100644 --- a/CRM/Contact/Form/Search/Advanced.php +++ b/CRM/Contact/Form/Search/Advanced.php @@ -189,7 +189,7 @@ class CRM_Contact_Form_Search_Advanced extends CRM_Contact_Form_Search { 'privacy_toggle' => 1, 'operator' => 'AND', ], $defaults); - $this->normalizeDefaultValues($defaults); + $defaults = $this->normalizeDefaultValues($defaults); //991/Subtypes not respected when editing smart group criteria if (!empty($defaults['contact_type']) && !empty($this->_formValues['contact_sub_type'])) { diff --git a/CRM/Contact/Form/Task/Useradd.php b/CRM/Contact/Form/Task/Useradd.php index b9a8056721..385405c6a2 100644 --- a/CRM/Contact/Form/Task/Useradd.php +++ b/CRM/Contact/Form/Task/Useradd.php @@ -73,6 +73,7 @@ class CRM_Contact_Form_Task_Useradd extends CRM_Core_Form { $this->addRule('cms_pass', 'Password is required', 'required'); $this->addRule(['cms_pass', 'cms_confirm_pass'], 'ERROR: Password mismatch', 'compare'); $this->add('text', 'email', ts('Email:'), ['class' => 'huge'])->freeze(); + $this->addRule('email', 'Email is required', 'required'); $this->add('hidden', 'contactID'); //add a rule to check username uniqueness @@ -101,8 +102,12 @@ class CRM_Contact_Form_Task_Useradd extends CRM_Core_Form { // store the submitted values in an array $params = $this->exportValues(); - CRM_Core_BAO_CMSUser::create($params, 'email'); - CRM_Core_Session::setStatus('', ts('User Added'), 'success'); + if (CRM_Core_BAO_CMSUser::create($params, 'email') === FALSE) { + CRM_Core_Error::statusBounce(ts('Error creating CMS user account.')); + } + else { + CRM_Core_Session::setStatus(ts('User Added'), '', 'success'); + } } /** diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 6c5df3c357..e5d83dd6cf 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -10,6 +10,8 @@ */ use Civi\Api4\Activity; +use Civi\Api4\ContributionPage; +use Civi\Api4\ContributionRecur; /** * @@ -1345,6 +1347,38 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution { return $rows; } + /** + * Should an email receipt be sent for this contribution on completion. + * + * @param array $input + * @param int $contributionPageID + * @param int $recurringContributionID + * + * @return bool + * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException + */ + protected static function isEmailReceipt(array $input, $contributionPageID, $recurringContributionID): bool { + if (isset($input['is_email_receipt'])) { + return (bool) $input['is_email_receipt']; + } + if ($recurringContributionID) { + //CRM-13273 - is_email_receipt setting on recurring contribution should take precedence over contribution page setting + // but CRM-16124 if $input['is_email_receipt'] is set then that should not be overridden. + // dev/core#1245 this maybe not the desired effect because the default value for is_email_receipt is set to 0 rather than 1 in + // Instance that had the table added via an upgrade in 4.1 + // see also https://github.com/civicrm/civicrm-svn/commit/7f39befd60bc735408d7866b02b3ac7fff1d4eea#diff-9ad8e290180451a2d6eacbd3d1ca7966R354 + // https://lab.civicrm.org/dev/core/issues/1245 + return (bool) ContributionRecur::get(FALSE)->addWhere('id', '=', $recurringContributionID)->addSelect('is_email_receipt')->execute()->first()['is_email_receipt']; + } + if ($contributionPageID) { + return (bool) ContributionPage::get(FALSE)->addWhere('id', '=', $contributionPageID)->addSelect('is_email_receipt')->execute()->first()['is_email_receipt']; + } + // This would be the case for backoffice (where is_email_receipt is not passed in) or events, where Event::sendMail will filter + // again anyway. + return TRUE; + } + /** * @inheritDoc */ @@ -2575,12 +2609,11 @@ LEFT JOIN civicrm_contribution contribution ON ( componentPayment.contribution_ * @param CRM_Contribute_BAO_Contribution $contribution * @param array $input * @param array $contributionParams - * @param int $paymentProcessorID * - * @return bool + * @return bool|array * @throws CiviCRM_API3_Exception */ - protected static function repeatTransaction(&$contribution, &$input, $contributionParams, $paymentProcessorID) { + protected static function repeatTransaction(&$contribution, &$input, $contributionParams) { if (!empty($contribution->id)) { return FALSE; } @@ -2611,17 +2644,21 @@ LEFT JOIN civicrm_contribution contribution ON ( componentPayment.contribution_ ]) ); $input['line_item'] = $contributionParams['line_item'] = $templateContribution['line_item']; - $contributionParams['status_id'] = 'Pending'; - if (isset($contributionParams['financial_type_id'])) { - // Give precedence to passed in type. + + if (isset($contributionParams['financial_type_id']) && count($input['line_item']) === 1) { + // We permit the financial type to be overridden for single line items. + // More comments on this are in getTemplateTransaction. $contribution->financial_type_id = $contributionParams['financial_type_id']; } else { $contributionParams['financial_type_id'] = $templateContribution['financial_type_id']; } - $contributionParams['contact_id'] = $templateContribution['contact_id']; - $contributionParams['source'] = empty($templateContribution['source']) ? ts('Recurring contribution') : $templateContribution['source']; + foreach (['contact_id', 'currency', 'source'] as $fieldName) { + $contributionParams[$fieldName] = $templateContribution[$fieldName]; + } + + $contributionParams['source'] = $contributionParams['source'] ?: ts('Recurring contribution'); //CRM-18805 -- Contribution page not recorded on recurring transactions, Recurring contribution payments //do not create CC or BCC emails or profile notifications. @@ -2636,9 +2673,9 @@ LEFT JOIN civicrm_contribution contribution ON ( componentPayment.contribution_ $createContribution = civicrm_api3('Contribution', 'create', $contributionParams); $contribution->id = $createContribution['id']; - CRM_Contribute_BAO_ContributionRecur::copyCustomValues($contributionParams['contribution_recur_id'], $contribution->id); + $contribution->copyCustomFields($templateContribution['id'], $contribution->id); self::handleMembershipIDOverride($contribution->id, $input); - return TRUE; + return $createContribution; } } @@ -2927,7 +2964,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac //not really sure what params might be passed in but lets merge em into values $values = array_merge($this->_gatherMessageValues($input, $values, $ids), $values); - $values['is_email_receipt'] = $this->isEmailReceipt($input, $values); + $values['is_email_receipt'] = !$returnMessageText; if (!empty($input['receipt_date'])) { $values['receipt_date'] = $input['receipt_date']; } @@ -3109,7 +3146,11 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac //get soft contributions $softContributions = CRM_Contribute_BAO_ContributionSoft::getSoftContribution($this->id, TRUE); if (!empty($softContributions)) { - $values['softContributions'] = $softContributions['soft_credit']; + // For pcp soft credit, there is no 'soft_credit' member it comes + // back in different array members, but shortly after returning from + // this function it calls _assignMessageVariablesToTemplate which does + // its own lookup of any pcp soft credit, so we can skip it here. + $values['softContributions'] = $softContributions['soft_credit'] ?? NULL; } if (isset($this->contribution_page_id)) { // This is a call we want to use less, in favour of loading related objects. @@ -3925,6 +3966,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac 'Refunded' => ['Cancelled', 'Completed'], 'Partially paid' => ['Completed'], 'Pending refund' => ['Completed', 'Refunded'], + 'Failed' => ['Pending'], ]; if (!in_array($contributionStatuses[$fields['contribution_status_id']], @@ -4401,8 +4443,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac * @param array $input * @param array $ids * @param array $objects - * @param CRM_Core_Transaction $transaction - * It is not recommended to pass this in. The calling function handle it's own roll back if it wants it. * @param bool $isPostPaymentCreate * Is this being called from the payment.create api. If so the api has taken care of financial entities. * Note that our goal is that this would only ever be called from payment.create and never handle financials (only @@ -4412,10 +4452,8 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function completeOrder($input, &$ids, $objects, $transaction = NULL, $isPostPaymentCreate = FALSE) { - if (!$transaction) { - $transaction = new CRM_Core_Transaction(); - } + public static function completeOrder($input, &$ids, $objects, $isPostPaymentCreate = FALSE) { + $transaction = new CRM_Core_Transaction(); $contribution = $objects['contribution']; $primaryContributionID = $contribution->id ?? $objects['first_contribution']->id; // The previous details are used when calculating line items so keep it before any code that 'does something' @@ -4435,10 +4473,8 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac 'contribution_status_id', 'card_type_id', 'pan_truncation', + 'financial_type_id', ]; - if (self::isSingleLineItem($primaryContributionID)) { - $inputContributionWhiteList[] = 'financial_type_id'; - } $participant = $objects['participant'] ?? NULL; $recurContrib = $objects['contributionRecur'] ?? NULL; @@ -4463,11 +4499,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac ], array_intersect_key($input, array_fill_keys($inputContributionWhiteList, 1) )); - // CRM-20678 Ensure that the currency is correct in subseqent transcations. - if (empty($contributionParams['currency']) && isset($objects['first_contribution']->currency)) { - $contributionParams['currency'] = $objects['first_contribution']->currency; - } - $contributionParams['payment_processor'] = $input['payment_processor'] = $paymentProcessorId; // If paymentProcessor is not set then the payment_instrument_id would not be correct. @@ -4481,41 +4512,12 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac } $changeDate = CRM_Utils_Array::value('trxn_date', $input, date('YmdHis')); - if (empty($contributionParams['receive_date']) && $changeDate) { - $contributionParams['receive_date'] = $changeDate; - } - - self::repeatTransaction($contribution, $input, $contributionParams, $paymentProcessorId); - $contributionParams['financial_type_id'] = $contribution->financial_type_id; - - $values = []; + $contributionResult = self::repeatTransaction($contribution, $input, $contributionParams); if ($input['component'] == 'contribute') { - if ($contribution->contribution_page_id) { - // Figure out what we gain from this. - // Note that we may have overwritten the is_email_receipt input, fix that below. - CRM_Contribute_BAO_ContributionPage::setValues($contribution->contribution_page_id, $values); - } - elseif ($recurContrib && $recurringContributionID) { - $values['amount'] = $recurContrib->amount; - $values['financial_type_id'] = $objects['contributionType']->id; - $values['title'] = $source = ts('Offline Recurring Contribution'); - } - - if ($recurContrib && $recurringContributionID) { - //CRM-13273 - is_email_receipt setting on recurring contribution should take precedence over contribution page setting - // but CRM-16124 if $input['is_email_receipt'] is set then that should not be overridden. - // dev/core#1245 this maybe not the desired effect because the default value for is_email_receipt is set to 0 rather than 1 in - // Instance that had the table added via an upgrade in 4.1 - // see also https://github.com/civicrm/civicrm-svn/commit/7f39befd60bc735408d7866b02b3ac7fff1d4eea#diff-9ad8e290180451a2d6eacbd3d1ca7966R354 - // https://lab.civicrm.org/dev/core/issues/1245 - $values['is_email_receipt'] = $recurContrib->is_email_receipt; - } - if ($contributionParams['contribution_status_id'] === $completedContributionStatusID) { self::updateMembershipBasedOnCompletionOfContribution( $contribution, - $primaryContributionID, $changeDate ); } @@ -4535,10 +4537,9 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac $contributionParams['id'] = $contribution->id; $contributionParams['is_post_payment_create'] = $isPostPaymentCreate; - // CRM-19309 - if you update the contribution here with financial_type_id it can/will mess with $lineItem - // unsetting it here does NOT cause any other contribution test to fail! - unset($contributionParams['financial_type_id']); - $contributionResult = civicrm_api3('Contribution', 'create', $contributionParams); + if (!$contributionResult) { + $contributionResult = civicrm_api3('Contribution', 'create', $contributionParams); + } // Add new soft credit against current $contribution. if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id) { @@ -4556,10 +4557,12 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac CRM_Core_Error::debug_log_message('Contribution record updated successfully'); $transaction->commit(); + // @todo - check if Contribution::create does this, test, remove. CRM_Contribute_BAO_ContributionRecur::updateRecurLinkedPledge($contribution->id, $recurringContributionID, $contributionParams['contribution_status_id'], $input['amount']); // create an activity record + // @todo - check if Contribution::create does this, test, remove. if ($input['component'] == 'contribute') { //CRM-4027 $targetContactID = NULL; @@ -4570,13 +4573,7 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID); } - $isEmailReceipt = !array_key_exists('is_email_receipt', $values) || $values['is_email_receipt'] == 1; - if (isset($input['is_email_receipt'])) { - $isEmailReceipt = $input['is_email_receipt']; - } - // CRM-9132 legacy behaviour was that receipts were sent out in all instances. Still sending - // when array_key 'is_email_receipt doesn't exist in case some instances where is needs setting haven't been set - if ($isEmailReceipt) { + if (self::isEmailReceipt($input, $contribution->contribution_page_id, $recurringContributionID)) { civicrm_api3('Contribution', 'sendconfirmation', [ 'id' => $contribution->id, 'payment_processor_id' => $paymentProcessorId, @@ -4601,8 +4598,6 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac * @param array $ids * Related object IDs. * @param int $contributionID - * @param array $values - * Values related to objects that have already been loaded. * @param bool $returnMessageText * Should text be returned instead of sent. This. * is because the function is also used to generate pdfs @@ -4612,9 +4607,8 @@ INNER JOIN civicrm_activity ON civicrm_activity_contact.activity_id = civicrm_ac * @throws \CiviCRM_API3_Exception * @throws \Exception */ - public static function sendMail(&$input, &$ids, $contributionID, &$values, - $returnMessageText = FALSE) { - + public static function sendMail($input, $ids, $contributionID, $returnMessageText = FALSE) { + $values = []; $contribution = new CRM_Contribute_BAO_Contribution(); $contribution->id = $contributionID; if (!$contribution->find(TRUE)) { @@ -5216,13 +5210,12 @@ LEFT JOIN civicrm_contribution on (civicrm_contribution.contact_id = civicrm_co * load them in this function. Code clean up would compensate for any minor performance implication. * * @param \CRM_Contribute_BAO_Contribution $contribution - * @param int $primaryContributionID * @param string $changeDate * * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public static function updateMembershipBasedOnCompletionOfContribution($contribution, $primaryContributionID, $changeDate) { + public static function updateMembershipBasedOnCompletionOfContribution($contribution, $changeDate) { $memberships = self::getRelatedMemberships($contribution->id); foreach ($memberships as $membership) { $membershipParams = [ @@ -5259,10 +5252,11 @@ LIMIT 1;"; // Passing num_terms to the api triggers date calculations, but for pending memberships these may be already calculated. // sigh - they should be consistent but removing the end date check causes test failures & maybe UI too? // The api assumes num_terms is a special sauce for 'is_renewal' so we need to not pass it when updating a pending to completed. + // ... except testCompleteTransactionMembershipPriceSetTwoTerms hits this line so the above is obviously not true.... // @todo once apiv4 ships with core switch to that & find sanity. $membershipParams['num_terms'] = $contribution->getNumTermsByContributionAndMembershipType( $membershipParams['membership_type_id'], - $primaryContributionID + $contribution->id ); } // @todo remove all this stuff in favour of letting the api call further down handle in @@ -5278,6 +5272,8 @@ LIMIT 1;"; * In BAO/Membership.php(renewMembership function), we skip the extend membership date and status * when Contribution mode is notify and membership is for renewal ) */ + // Test cover for this is in testRepeattransactionRenewMembershipOldMembership + // Be afraid. CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeDate); // @todo - we should pass membership_type_id instead of null here but not @@ -5712,23 +5708,6 @@ LIMIT 1;"; } } - /** - * Should an email receipt be sent for this contribution when complete. - * - * @param array $input - * - * @return mixed - */ - protected function isEmailReceipt($input) { - if (isset($input['is_email_receipt'])) { - return $input['is_email_receipt']; - } - if (!empty($this->_relatedObjects['contribution_page_id'])) { - return $this->_relatedObjects['contribution_page_id']->is_email_receipt; - } - return TRUE; - } - /** * Function to replace contribution tokens. * diff --git a/CRM/Contribute/BAO/ContributionRecur.php b/CRM/Contribute/BAO/ContributionRecur.php index c1985a3f1b..63d7799768 100644 --- a/CRM/Contribute/BAO/ContributionRecur.php +++ b/CRM/Contribute/BAO/ContributionRecur.php @@ -442,8 +442,15 @@ INNER JOIN civicrm_contribution con ON ( con.id = mp.contribution_id ) } if ($templateContributions->count()) { $templateContribution = $templateContributions->first(); - $result = array_merge($templateContribution, $overrides); $lineItems = CRM_Price_BAO_LineItem::getLineItemsByContributionID($templateContribution['id']); + // We only permit the financial type to be overridden for single line items. + // Otherwise we need to figure out a whole lot of extra complexity. + // It's not UI-possible to alter financial_type_id for recurring contributions + // with more than one line item. + if (count($lineItems) > 1 && isset($overrides['financial_type_id'])) { + unset($overrides['financial_type_id']); + } + $result = array_merge($templateContribution, $overrides); $result['line_item'] = self::reformatLineItemsForRepeatContribution($result['total_amount'], $result['financial_type_id'], $lineItems, (array) $templateContribution); return $result; } @@ -545,6 +552,8 @@ INNER JOIN civicrm_contribution con ON ( con.id = mp.contribution_id ) /** * Copy custom data of the initial contribution into its recurring contributions. * + * @deprecated + * * @param int $recurId * @param int $targetContributionId */ diff --git a/CRM/Contribute/Form/AbstractEditPayment.php b/CRM/Contribute/Form/AbstractEditPayment.php index 435c239813..cdf5b2f6a1 100644 --- a/CRM/Contribute/Form/AbstractEditPayment.php +++ b/CRM/Contribute/Form/AbstractEditPayment.php @@ -718,7 +718,7 @@ WHERE contribution_id = {$id} $title .= " - {$info['title']}"; } $this->assign('transaction', TRUE); - $this->assign('payments', $paymentInfo['transaction']); + $this->assign('payments', $paymentInfo['transaction'] ?? NULL); $this->assign('paymentLinks', $paymentInfo['payment_links']); return $title; } diff --git a/CRM/Contribute/Form/Contribution.php b/CRM/Contribute/Form/Contribution.php index 09f05ba0bd..d453df3a7c 100644 --- a/CRM/Contribute/Form/Contribution.php +++ b/CRM/Contribute/Form/Contribution.php @@ -951,30 +951,7 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP CRM_Core_Error::statusBounce($e->getMessage(), $urlParams, ts('Payment Processor Error')); } - $session = CRM_Core_Session::singleton(); - $buttonName = $this->controller->getButtonName(); - if ($this->_context == 'standalone') { - if ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contribute/add', - 'reset=1&action=add&context=standalone' - )); - } - else { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', - "reset=1&cid={$this->_contactID}&selectedChild=contribute" - )); - } - } - elseif ($this->_context == 'contribution' && $this->_mode && $buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', - "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}&mode={$this->_mode}" - )); - } - elseif ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', - "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}" - )); - } + $this->setUserContext(); //store contribution ID if not yet set (on create) if (empty($this->_id) && !empty($contribution->id)) { @@ -1814,4 +1791,34 @@ class CRM_Contribute_Form_Contribution extends CRM_Contribute_Form_AbstractEditP } } + /** + * Set context in session + */ + public function setUserContext(): void { + $session = CRM_Core_Session::singleton(); + $buttonName = $this->controller->getButtonName(); + if ($this->_context == 'standalone') { + if ($buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contribute/add', + 'reset=1&action=add&context=standalone' + )); + } + else { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', + "reset=1&cid={$this->_contactID}&selectedChild=contribute" + )); + } + } + elseif ($this->_context == 'contribution' && $this->_mode && $buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', + "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}&mode={$this->_mode}" + )); + } + elseif ($buttonName == $this->getButtonName('upload', 'new')) { + $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/contribution', + "reset=1&action=add&context={$this->_context}&cid={$this->_contactID}" + )); + } + } + } diff --git a/CRM/Contribute/Form/Contribution/Confirm.php b/CRM/Contribute/Form/Contribution/Confirm.php index a1da2697a6..228e285dfd 100644 --- a/CRM/Contribute/Form/Contribution/Confirm.php +++ b/CRM/Contribute/Form/Contribution/Confirm.php @@ -954,7 +954,7 @@ class CRM_Contribute_Form_Contribution_Confirm extends CRM_Contribute_Form_Contr } $smarty = CRM_Core_Smarty::singleton(); $smarty->assign('dataArray', $dataArray); - $smarty->assign('totalTaxAmount', $params['tax_amount']); + $smarty->assign('totalTaxAmount', $params['tax_amount'] ?? NULL); } // lets store it in the form variable so postProcess hook can get to this and use it diff --git a/CRM/Contribute/Form/Task/Invoice.php b/CRM/Contribute/Form/Task/Invoice.php index d189158cd8..b2087a6474 100644 --- a/CRM/Contribute/Form/Task/Invoice.php +++ b/CRM/Contribute/Form/Task/Invoice.php @@ -9,9 +9,6 @@ +--------------------------------------------------------------------+ */ -use Dompdf\Dompdf; -use Dompdf\Options; - /** * * @package CRM @@ -479,7 +476,11 @@ class CRM_Contribute_Form_Task_Invoice extends CRM_Contribute_Form_Task { 'metric' => 'px', ]); // functions call for adding activity with attachment - $fileName = self::putFile($html, $pdfFileName); + $fileName = self::putFile($html, $pdfFileName, [ + 'margin_top' => 10, + 'margin_left' => 65, + 'metric' => 'px', + ]); self::addActivities($subject, $contactIds, $fileName, $params); CRM_Utils_System::civiExit(); @@ -554,22 +555,13 @@ class CRM_Contribute_Form_Task_Invoice extends CRM_Contribute_Form_Task { * Content for pdf in html format. * * @param string $name + * @param array $format * * @return string * Name of file which is in pdf format */ - public static function putFile($html, $name = 'Invoice.pdf') { - $options = new Options(); - $options->set('isRemoteEnabled', TRUE); - - $doc = new DOMPDF($options); - $doc->load_html($html); - $doc->render(); - $html = $doc->output(); - $config = CRM_Core_Config::singleton(); - $fileName = $config->uploadDir . $name; - file_put_contents($fileName, $html); - return $fileName; + public static function putFile($html, $name = 'Invoice.pdf', $format = NULL) { + return CRM_Utils_Mail::appendPDF($name, $html, $format)['fullPath'] ?? ''; } /** diff --git a/CRM/Contribute/Form/Task/PDF.php b/CRM/Contribute/Form/Task/PDF.php index 76dac1989f..e05ebab9d5 100644 --- a/CRM/Contribute/Form/Task/PDF.php +++ b/CRM/Contribute/Form/Task/PDF.php @@ -184,7 +184,6 @@ AND {$this->_componentClause}"; // CRM_Contribute_BAO_Contribution::composeMessageArray expects mysql formatted date $objects['contribution']->receive_date = CRM_Utils_Date::isoToMysql($objects['contribution']->receive_date); - $values = []; if (isset($params['from_email_address']) && !$elements['createPdf']) { // If a logged in user from email is used rather than a domain wide from email address // the from_email_address params key will be numerical and we need to convert it to be @@ -196,8 +195,7 @@ AND {$this->_componentClause}"; $input['receipt_from_name'] = str_replace('"', '', $fromDetails[0]); } - $mail = CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $objects['contribution']->id, $values, - $elements['createPdf']); + $mail = CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $objects['contribution']->id, $elements['createPdf']); if ($mail['html']) { $message[] = $mail['html']; @@ -253,7 +251,7 @@ AND {$this->_componentClause}"; $pdfElements['contribIDs'] = implode(',', $contribIds); - $pdfElements['details'] = CRM_Contribute_Form_Task_Status::getDetails($pdfElements['contribIDs']); + $pdfElements['details'] = self::getDetails($pdfElements['contribIDs']); $pdfElements['baseIPN'] = new CRM_Core_Payment_BaseIPN(); @@ -295,4 +293,47 @@ AND {$this->_componentClause}"; return $pdfElements; } + /** + * @param string $contributionIDs + * + * @return array + */ + private static function getDetails($contributionIDs) { + if (empty($contributionIDs)) { + return []; + } + $query = " +SELECT c.id as contribution_id, + c.contact_id as contact_id , + mp.membership_id as membership_id , + pp.participant_id as participant_id , + p.event_id as event_id +FROM civicrm_contribution c +LEFT JOIN civicrm_membership_payment mp ON mp.contribution_id = c.id +LEFT JOIN civicrm_participant_payment pp ON pp.contribution_id = c.id +LEFT JOIN civicrm_participant p ON pp.participant_id = p.id +WHERE c.id IN ( $contributionIDs )"; + + $rows = []; + $dao = CRM_Core_DAO::executeQuery($query); + + while ($dao->fetch()) { + $rows[$dao->contribution_id]['component'] = $dao->participant_id ? 'event' : 'contribute'; + $rows[$dao->contribution_id]['contact'] = $dao->contact_id; + if ($dao->membership_id) { + if (!array_key_exists('membership', $rows[$dao->contribution_id])) { + $rows[$dao->contribution_id]['membership'] = []; + } + $rows[$dao->contribution_id]['membership'][] = $dao->membership_id; + } + if ($dao->participant_id) { + $rows[$dao->contribution_id]['participant'] = $dao->participant_id; + } + if ($dao->event_id) { + $rows[$dao->contribution_id]['event'] = $dao->event_id; + } + } + return $rows; + } + } diff --git a/CRM/Contribute/Form/Task/Status.php b/CRM/Contribute/Form/Task/Status.php index e3c2a6e7da..b501327a88 100644 --- a/CRM/Contribute/Form/Task/Status.php +++ b/CRM/Contribute/Form/Task/Status.php @@ -68,21 +68,6 @@ AND {$this->_componentClause}"; * Build the form object. */ public function buildQuickForm() { - $status = CRM_Contribute_BAO_Contribution_Utils::getContributionStatuses( - 'contribution', $this->_contributionIds[0] - ); - $byName = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - // FIXME: if it's invalid to transition from Pending to - // In Progress or Overdue, we should move that logic to - // CRM_Contribute_BAO_Contribution_Utils::getContributionStatuses. - foreach (['Pending', 'In Progress', 'Overdue'] as $suppress) { - unset($status[CRM_Utils_Array::key($suppress, $byName)]); - } - $this->add('select', 'contribution_status_id', - ts('Contribution Status'), - $status, - TRUE - ); $this->add('checkbox', 'is_email_receipt', ts('Send e-mail receipt')); $this->setDefaults(['is_email_receipt' => 1]); @@ -198,7 +183,7 @@ AND co.id IN ( $contribIDs )"; // submit the form with values. self::processForm($this, $params); - CRM_Core_Session::setStatus(ts('Contribution status has been updated for selected record(s).'), ts('Status Updated'), 'success'); + CRM_Core_Session::setStatus(ts('Payments have been recorded for selected record(s).'), ts('Payments recorded'), 'success'); } /** @@ -212,124 +197,26 @@ AND co.id IN ( $contribIDs )"; * @throws \Exception */ public static function processForm($form, $params) { - $statusID = $params['contribution_status_id'] ?? NULL; - $baseIPN = new CRM_Core_Payment_BaseIPN(); - - // get the missing pieces for each contribution - $contribIDs = implode(',', $form->_contributionIds); - $details = self::getDetails($contribIDs); - $template = CRM_Core_Smarty::singleton(); - - // for each contribution id, we just call the baseIPN stuff foreach ($form->_rows as $row) { - $input = $ids = $objects = []; - $input['component'] = $details[$row['contribution_id']]['component']; - - $ids['contact'] = $row['contact_id']; - $ids['contribution'] = $row['contribution_id']; - $ids['contributionRecur'] = NULL; - $ids['contributionPage'] = NULL; - $ids['membership'] = $details[$row['contribution_id']]['membership'] ?? NULL; - $ids['participant'] = $details[$row['contribution_id']]['participant'] ?? NULL; - $ids['event'] = $details[$row['contribution_id']]['event'] ?? NULL; - - if (!$baseIPN->validateData($input, $ids, $objects, FALSE)) { - CRM_Core_Error::statusBounce('Supplied data was not able to be validated'); - } - - $contribution = &$objects['contribution']; - - $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, - 'name' - ); - - if ($statusID == array_search('Cancelled', $contributionStatuses)) { - $transaction = new CRM_Core_Transaction(); - $baseIPN->cancelled($objects, $transaction); - $transaction->commit(); - continue; - } - elseif ($statusID == array_search('Failed', $contributionStatuses)) { - $transaction = new CRM_Core_Transaction(); - $baseIPN->failed($objects, $transaction); - $transaction->commit(); - continue; - } - - // status is not pending - if ($contribution->contribution_status_id != array_search('Pending', - $contributionStatuses - ) - ) { - continue; - } - - // set some fake input values so we can reuse IPN code - $input['amount'] = $contribution->total_amount; - $input['is_test'] = $contribution->is_test; - $input['fee_amount'] = $params["fee_amount_{$row['contribution_id']}"]; - $input['check_number'] = $params["check_number_{$row['contribution_id']}"]; - $input['payment_instrument_id'] = $params["payment_instrument_id_{$row['contribution_id']}"]; - $input['net_amount'] = $contribution->total_amount - $input['fee_amount']; - - if (!empty($params["trxn_id_{$row['contribution_id']}"])) { - $input['trxn_id'] = trim($params["trxn_id_{$row['contribution_id']}"]); - } - else { - $input['trxn_id'] = $contribution->invoice_id; - } - $input['trxn_date'] = $params["trxn_date_{$row['contribution_id']}"] . ' ' . date('H:i:s'); - $input['is_email_receipt'] = !empty($params['is_email_receipt']); - - // @todo calling CRM_Contribute_BAO_Contribution::completeOrder like this is a pattern in it's last gasps. Call contribute.completetransaction api. - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); - - // reset template values before processing next transactions - $template->clearTemplateVars(); - } - } - - /** - * @param string $contributionIDs - * - * @return array - */ - public static function &getDetails($contributionIDs) { - if (empty($contributionIDs)) { - return []; - } - $query = " -SELECT c.id as contribution_id, - c.contact_id as contact_id , - mp.membership_id as membership_id , - pp.participant_id as participant_id , - p.event_id as event_id -FROM civicrm_contribution c -LEFT JOIN civicrm_membership_payment mp ON mp.contribution_id = c.id -LEFT JOIN civicrm_participant_payment pp ON pp.contribution_id = c.id -LEFT JOIN civicrm_participant p ON pp.participant_id = p.id -WHERE c.id IN ( $contributionIDs )"; - - $rows = []; - $dao = CRM_Core_DAO::executeQuery($query); - - while ($dao->fetch()) { - $rows[$dao->contribution_id]['component'] = $dao->participant_id ? 'event' : 'contribute'; - $rows[$dao->contribution_id]['contact'] = $dao->contact_id; - if ($dao->membership_id) { - if (!array_key_exists('membership', $rows[$dao->contribution_id])) { - $rows[$dao->contribution_id]['membership'] = []; - } - $rows[$dao->contribution_id]['membership'][] = $dao->membership_id; - } - if ($dao->participant_id) { - $rows[$dao->contribution_id]['participant'] = $dao->participant_id; - } - if ($dao->event_id) { - $rows[$dao->contribution_id]['event'] = $dao->event_id; - } + $contribData = civicrm_api3('Contribution', 'getSingle', ['id' => $row['contribution_id']]); + $trxnParams = [ + 'contribution_id' => $row['contribution_id'], + // We are safe assuming that payments will be for the total amount of + // the contribution because the contributions must be in "Pending" + // status. + 'total_amount' => $contribData['total_amount'], + 'fee_amount' => $params["fee_amount_{$row['contribution_id']}"], + 'check_number' => $params["check_number_{$row['contribution_id']}"], + 'payment_instrument_id' => $params["payment_instrument_id_{$row['contribution_id']}"], + 'net_amount' => $contribData['total_amount'] - $params["fee_amount_{$row['contribution_id']}"], + // Not sure why to default to invoice_id, but that's what the form has + // been doing historically + 'trxn_id' => $params["trxn_id_{$row['contribution_id']}"] ?? $contribData['invoice_id'], + 'trxn_date' => $params["trxn_date_{$row['contribution_id']}"] ?? 'now', + 'is_send_contribution_notification' => !empty($params['is_email_receipt']), + ]; + $result = civicrm_api3('Payment', 'create', $trxnParams); } - return $rows; } } diff --git a/CRM/Contribute/Task.php b/CRM/Contribute/Task.php index 9a88cfc6d5..24feae8a87 100644 --- a/CRM/Contribute/Task.php +++ b/CRM/Contribute/Task.php @@ -81,7 +81,7 @@ class CRM_Contribute_Task extends CRM_Core_Task { 'result' => TRUE, ], self::UPDATE_STATUS => [ - 'title' => ts('Update pending contribution status'), + 'title' => ts('Record payments for contributions'), 'class' => 'CRM_Contribute_Form_Task_Status', 'result' => TRUE, ], diff --git a/CRM/Core/BAO/Cache.php b/CRM/Core/BAO/Cache.php index a13a6fee85..c753411930 100644 --- a/CRM/Core/BAO/Cache.php +++ b/CRM/Core/BAO/Cache.php @@ -33,225 +33,10 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { */ const DEFAULT_SESSION_TTL = 172800; - /** - * Cache. - * - * Format is ($cacheKey => $cacheValue) - * - * @var array - */ - public static $_cache = NULL; - - /** - * Retrieve an item from the DB cache. - * - * @param string $group - * (required) The group name of the item. - * @param string $path - * (required) The path under which this item is stored. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - * - * @return object - * The data if present in cache, else null - * @deprecated - */ - public static function &getItem($group, $path, $componentID = NULL) { - CRM_Core_Error::deprecatedFunctionWarning( - 'CRM_Core_BAO_Cache::getItem is deprecated and will be removed from core soon, use Civi::cache() facade or define cache group using hook_civicrm_container' - ); - if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { - $value = $adapter::getItem($group, $path, $componentID); - return $value; - } - - if (self::$_cache === NULL) { - self::$_cache = []; - } - - $argString = "CRM_CT_{$group}_{$path}_{$componentID}"; - if (!array_key_exists($argString, self::$_cache)) { - $cache = CRM_Utils_Cache::singleton(); - $cleanKey = self::cleanKey($argString); - self::$_cache[$argString] = $cache->get($cleanKey); - if (self::$_cache[$argString] === NULL) { - $table = self::getTableName(); - $where = self::whereCache($group, $path, $componentID); - $rawData = CRM_Core_DAO::singleValueQuery("SELECT data FROM $table WHERE $where"); - $data = $rawData ? self::decode($rawData) : NULL; - - self::$_cache[$argString] = $data; - if ($data !== NULL) { - // Do not cache 'null' as that is most likely a cache miss & we shouldn't then cache it. - $cache->set($cleanKey, self::$_cache[$argString]); - } - } - } - return self::$_cache[$argString]; - } - - /** - * Retrieve all items in a group. - * - * @param string $group - * (required) The group name of the item. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - * - * @return object - * The data if present in cache, else null - * @deprecated - */ - public static function &getItems($group, $componentID = NULL) { - CRM_Core_Error::deprecatedFunctionWarning( - 'CRM_Core_BAO_Cache::getItems is deprecated and will be removed from core soon, use Civi::cache() facade or define cache group using hook_civicrm_container' - ); - if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { - return $adapter::getItems($group, $componentID); - } - - if (self::$_cache === NULL) { - self::$_cache = []; - } - - $argString = "CRM_CT_CI_{$group}_{$componentID}"; - if (!array_key_exists($argString, self::$_cache)) { - $cache = CRM_Utils_Cache::singleton(); - $cleanKey = self::cleanKey($argString); - self::$_cache[$argString] = $cache->get($cleanKey); - if (!self::$_cache[$argString]) { - $table = self::getTableName(); - $where = self::whereCache($group, NULL, $componentID); - $dao = CRM_Core_DAO::executeQuery("SELECT path, data FROM $table WHERE $where"); - - $result = []; - while ($dao->fetch()) { - $result[$dao->path] = self::decode($dao->data); - } - - self::$_cache[$argString] = $result; - $cache->set($cleanKey, self::$_cache[$argString]); - } - } - - return self::$_cache[$argString]; - } - - /** - * Store an item in the DB cache. - * - * @param object $data - * (required) A reference to the data that will be serialized and stored. - * @param string $group - * (required) The group name of the item. - * @param string $path - * (required) The path under which this item is stored. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - * @deprecated - * @throws CRM_Core_Exception - */ - public static function setItem(&$data, $group, $path, $componentID = NULL) { - CRM_Core_Error::deprecatedFunctionWarning( - 'CRM_Core_BAO_Cache::setItem is deprecated and will be removed from core soon, use Civi::cache() facade or define cache group using hook_civicrm_container' - ); - if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { - return $adapter::setItem($data, $group, $path, $componentID); - } - - if (self::$_cache === NULL) { - self::$_cache = []; - } - - // get a lock so that multiple ajax requests on the same page - // dont trample on each other - // CRM-11234 - $lock = Civi::lockManager()->acquire("cache.{$group}_{$path}._{$componentID}"); - if (!$lock->isAcquired()) { - throw new CRM_Core_Exception('Cannot acquire database lock'); - } - - $table = self::getTableName(); - $where = self::whereCache($group, $path, $componentID); - $dataExists = CRM_Core_DAO::singleValueQuery("SELECT COUNT(*) FROM $table WHERE {$where}"); - // FIXME - Use SQL NOW() or CRM_Utils_Time? - $now = date('Y-m-d H:i:s'); - $dataSerialized = self::encode($data); - - // This table has a wonky index, so we cannot use REPLACE or - // "INSERT ... ON DUPE". Instead, use SELECT+(INSERT|UPDATE). - if ($dataExists) { - $sql = "UPDATE $table SET data = %1, created_date = %2 WHERE {$where}"; - $args = [ - 1 => [$dataSerialized, 'String'], - 2 => [$now, 'String'], - ]; - $dao = CRM_Core_DAO::executeQuery($sql, $args, TRUE, NULL, FALSE, FALSE); - } - else { - $insert = CRM_Utils_SQL_Insert::into($table) - ->row([ - 'group_name' => $group, - 'path' => $path, - 'component_id' => $componentID, - 'data' => $dataSerialized, - 'created_date' => $now, - ]); - $dao = CRM_Core_DAO::executeQuery($insert->toSQL(), [], TRUE, NULL, FALSE, FALSE); - } - - $lock->release(); - - // cache coherency - refresh or remove dependent caches - - $argString = "CRM_CT_{$group}_{$path}_{$componentID}"; - $cache = CRM_Utils_Cache::singleton(); - $data = self::decode($dataSerialized); - self::$_cache[$argString] = $data; - $cache->set(self::cleanKey($argString), $data); - - $argString = "CRM_CT_CI_{$group}_{$componentID}"; - unset(self::$_cache[$argString]); - $cache->delete(self::cleanKey($argString)); - } - - /** - * Delete all the cache elements that belong to a group OR delete the entire cache if group is not specified. - * - * @param string $group - * The group name of the entries to be deleted. - * @param string $path - * Path of the item that needs to be deleted. - * @param bool $clearAll clear all caches - * @deprecated - */ - public static function deleteGroup($group = NULL, $path = NULL, $clearAll = TRUE) { - CRM_Core_Error::deprecatedFunctionWarning( - 'CRM_Core_BAO_Cache::deleteGroup is deprecated and will be removed from core soon, use Civi::cache() facade or define cache group using hook_civicrm_container' - ); - if (($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) !== NULL) { - return $adapter::deleteGroup($group, $path); - } - else { - $table = self::getTableName(); - $where = self::whereCache($group, $path, NULL); - CRM_Core_DAO::executeQuery("DELETE FROM $table WHERE $where"); - } - - if ($clearAll) { - self::resetCaches(); - } - } - /** * Cleanup ACL and System Level caches */ public static function resetCaches() { - // also reset ACL Cache - // @todo why is this called when CRM_Utils_System::flushCache() does it as well. - CRM_ACL_BAO_Cache::resetCache(); - - // also reset memory cache if any CRM_Utils_System::flushCache(); } @@ -476,6 +261,7 @@ class CRM_Core_BAO_Cache extends CRM_Core_DAO_Cache { * @see CRM_Utils_Cache::cleanKey() */ public static function cleanKey($key) { + CRM_Core_Error::deprecatedFunctionWarning('CRM_Utils_Cache::cleanKey'); return CRM_Utils_Cache::cleanKey($key); } diff --git a/CRM/Core/BAO/Cache/Psr16.php b/CRM/Core/BAO/Cache/Psr16.php deleted file mode 100644 index 1255f6d8a9..0000000000 --- a/CRM/Core/BAO/Cache/Psr16.php +++ /dev/null @@ -1,215 +0,0 @@ -warning('Unrecognized BAO cache group ({group}). This should work generally, but data may not be flushed in some edge-cases. Consider migrating explicitly to PSR-16.', [ - 'group' => $group, - ]); - } - - $cache = CRM_Utils_Cache::create([ - 'name' => "bao_$group", - 'type' => ['*memory*', 'SqlGroup', 'ArrayCache'], - // We're replacing CRM_Core_BAO_Cache, which traditionally used a front-cache - // that was not aware of TTLs. So it seems more consistent/performant to - // use 'fast' here. - 'withArray' => 'fast', - ]); - Civi::$statics[__CLASS__][$group] = $cache; - } - return Civi::$statics[__CLASS__][$group]; - } - - /** - * Retrieve an item from the DB cache. - * - * @param string $group - * (required) The group name of the item. - * @param string $path - * (required) The path under which this item is stored. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - * - * @return object - * The data if present in cache, else null - */ - public static function getItem($group, $path, $componentID = NULL) { - // TODO: Generate a general deprecation notice. - if ($componentID) { - Civi::log() - ->warning('getItem({group},{path},...) uses unsupported componentID. Consider migrating explicitly to PSR-16.', [ - 'group' => $group, - 'path' => $path, - ]); - } - return self::getGroup($group)->get(CRM_Utils_Cache::cleanKey($path)); - } - - /** - * Retrieve all items in a group. - * - * @param string $group - * (required) The group name of the item. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - * - * @throws CRM_Core_Exception - */ - public static function &getItems($group, $componentID = NULL) { - // Based on grepping universe, this function is not currently used. - // Moreover, it's hard to implement in PSR-16. (We'd have to extend the - // interface.) Let's wait and see if anyone actually needs this... - throw new \CRM_Core_Exception('Not implemented: CRM_Core_BAO_Cache_Psr16::getItems'); - } - - /** - * Store an item in the DB cache. - * - * @param object $data - * (required) A reference to the data that will be serialized and stored. - * @param string $group - * (required) The group name of the item. - * @param string $path - * (required) The path under which this item is stored. - * @param int $componentID - * The optional component ID (so componenets can share the same name space). - */ - public static function setItem(&$data, $group, $path, $componentID = NULL) { - // TODO: Generate a general deprecation notice. - - if ($componentID) { - Civi::log() - ->warning('setItem({group},{path},...) uses unsupported componentID. Consider migrating explicitly to PSR-16.', [ - 'group' => $group, - 'path' => $path, - ]); - } - self::getGroup($group) - ->set(CRM_Utils_Cache::cleanKey($path), $data, self::TTL); - } - - /** - * Delete all the cache elements that belong to a group OR delete the entire cache if group is not specified. - * - * @param string $group - * The group name of the entries to be deleted. - * @param string $path - * Path of the item that needs to be deleted. - */ - public static function deleteGroup($group = NULL, $path = NULL) { - // FIXME: Generate a general deprecation notice. - - if ($path) { - self::getGroup($group)->delete(CRM_Utils_Cache::cleanKey($path)); - } - else { - self::getGroup($group)->clear(); - } - } - - /** - * Cleanup any caches that we've mapped. - * - * Traditional SQL-backed caches are cleared as a matter of course during a - * system flush (by way of "TRUNCATE TABLE civicrm_cache"). This provides - * a spot where the adapter can - */ - public static function clearDBCache() { - foreach (self::getLegacyGroups() as $groupName) { - $group = self::getGroup($groupName); - $group->clear(); - } - } - - /** - * Get a list of known cache-groups - * - * @return array - */ - public static function getLegacyGroups() { - $groups = [ - // Universe - - // biz.jmaconsulting.lineitemedit - 'lineitem-editor', - - // civihr/uk.co.compucorp.civicrm.hrcore - 'HRCore_Info', - - ]; - // Handle Legacy Multisite caching group. - $extensions = CRM_Extension_System::singleton()->getManager(); - $multisiteExtensionStatus = $extensions->getStatus('org.civicrm.multisite'); - if ($multisiteExtensionStatus == $extensions::STATUS_INSTALLED) { - $extension_version = civicrm_api3('Extension', 'get', ['key' => 'org.civicrm.multisite'])['values'][0]['version']; - if (version_compare($extension_version, '2.7', '<')) { - Civi::log()->warning( - 'CRM_Core_BAO_Cache_PSR is deprecated for multisite extension, you should upgrade to the latest version to avoid this warning, this code will be removed at the end of 2019', - ['civi.tag' => 'deprecated'] - ); - $groups[] = 'descendant groups for an org'; - } - } - $entitySettingExtensionStatus = $extensions->getStatus('nz.co.fuzion.entitysetting'); - if ($multisiteExtensionStatus == $extensions::STATUS_INSTALLED) { - $extension_version = civicrm_api3('Extension', 'get', ['key' => 'nz.co.fuzion.entitysetting'])['values'][0]['version']; - if (version_compare($extension_version, '1.3', '<')) { - Civi::log()->warning( - 'CRM_Core_BAO_Cache_PSR is deprecated for entity setting extension, you should upgrade to the latest version to avoid this warning, this code will be removed at the end of 2019', - ['civi.tag' => 'deprecated'] - ); - $groups[] = 'CiviCRM setting Spec'; - } - } - $atomFeedsSettingExtensionStatus = $extensions->getStatus('be.chiro.civi.atomfeeds'); - if ($atomFeedsSettingExtensionStatus == $extensions::STATUS_INSTALLED) { - $extension_version = civicrm_api3('Extension', 'get', ['key' => 'be.chiro.civi.atomfeeds'])['values'][0]['version']; - if (version_compare($extension_version, '0.1-alpha2', '<')) { - Civi::log()->warning( - 'CRM_Core_BAO_Cache_PSR is deprecated for Atomfeeds extension, you should upgrade to the latest version to avoid this warning, this code will be removed at the end of 2019', - ['civi.tag' => 'deprecated'] - ); - $groups[] = 'dashboard'; - } - } - return $groups; - } - -} diff --git a/CRM/Core/BAO/ConfigSetting.php b/CRM/Core/BAO/ConfigSetting.php index c462bae664..45e0378974 100644 --- a/CRM/Core/BAO/ConfigSetting.php +++ b/CRM/Core/BAO/ConfigSetting.php @@ -119,105 +119,120 @@ class CRM_Core_BAO_ConfigSetting { } /** - * Evaluate locale preferences and activate a chosen locale by - * updating session+global variables. + * Activate a chosen locale. + * + * The locale is set by updating the session and global variables. + * + * When there is a choice of permitted languages (set on the "Administer" -> + * "Localisation" -> "Languages, Currency, Locations" screen) the locale to + * be applied can come from a variety of sources. The list below is the order + * of priority for deciding which of the sources "wins": + * + * - The request - when the "lcMessages" query variable is present in the URL. + * - The session - when the "lcMessages" session variable has been set. + * - Inherited from the CMS - when the "inheritLocale" setting is set. + * - CiviCRM settings - the fallback when none of the above set the locale. + * + * Single-language installs skip this and always set the default locale. * * @param \Civi\Core\SettingsBag $settings * @param string $activatedLocales * Imploded list of locales which are supported in the DB. */ public static function applyLocale($settings, $activatedLocales) { - // are we in a multi-language setup? - $multiLang = (bool) $activatedLocales; - // set the current language - $chosenLocale = NULL; + // Declare access to locale globals. + global $dbLocale, $tsLocale; + // Grab session reference. $session = CRM_Core_Session::singleton(); - $permittedLanguages = CRM_Core_I18n::uiLanguages(TRUE); + // Set flag for multi-language setup. + $multiLang = (bool) $activatedLocales; + + // Initialise the default and chosen locales. + $defaultLocale = $settings->get('lcMessages'); + $chosenLocale = NULL; - // The locale to be used can come from various places: - // - the request (url) - // - the session - // - civicrm_uf_match - // - inherited from the CMS - // Only look at this if there is actually a choice of permitted languages + // When there is a choice of permitted languages. + $permittedLanguages = CRM_Core_I18n::uiLanguages(TRUE); if (count($permittedLanguages) >= 2) { + + // Is the "lcMessages" query variable present in the URL? $requestLocale = CRM_Utils_Request::retrieve('lcMessages', 'String'); if (in_array($requestLocale, $permittedLanguages)) { $chosenLocale = $requestLocale; - - //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache. - // Ed: This doesn't sound good. - // Civi::cache('navigation')->flush(); - } - else { - $requestLocale = NULL; } - if (!$requestLocale) { + // Check the session if the chosen locale hasn't been set yet. + if (empty($chosenLocale)) { $sessionLocale = $session->get('lcMessages'); if (in_array($sessionLocale, $permittedLanguages)) { $chosenLocale = $sessionLocale; } - else { - $sessionLocale = NULL; - } } - if ($requestLocale) { - $ufm = new CRM_Core_DAO_UFMatch(); - $ufm->contact_id = $session->get('userID'); - if ($ufm->find(TRUE)) { - $ufm->language = $chosenLocale; - $ufm->save(); + /* + * Maybe inherit the language from the CMS. + * + * If the language is specified via "lcMessages" we skip this, since the + * intention of the URL query var is to override all other sources. + */ + if ($settings->get('inheritLocale') && empty($chosenLocale)) { + + /* + * FIXME: On multi-language installs, CRM_Utils_System::getUFLocale() in + * many cases returns nothing if $dbLocale is not set, so set it to the + * default - even if it's overridden later. + */ + $dbLocale = $multiLang && $defaultLocale ? "_{$defaultLocale}" : ''; + + // Retrieve locale as reported by CMS. + $cmsLocale = CRM_Utils_System::getUFLocale(); + if (in_array($cmsLocale, $permittedLanguages)) { + $chosenLocale = $cmsLocale; } - $session->set('lcMessages', $chosenLocale); - } - if (!$chosenLocale and $session->get('userID')) { - $ufm = new CRM_Core_DAO_UFMatch(); - $ufm->contact_id = $session->get('userID'); - if ($ufm->find(TRUE) && - in_array($ufm->language, $permittedLanguages) - ) { - $chosenLocale = $ufm->language; + // Clear chosen locale if not activated in multi-language CiviCRM. + if ($activatedLocales && !in_array($chosenLocale, explode(CRM_Core_DAO::VALUE_SEPARATOR, $activatedLocales))) { + $chosenLocale = NULL; } - $session->set('lcMessages', $chosenLocale); + } - } - global $dbLocale; - - // try to inherit the language from the hosting CMS - // If the language is specified in the session (ie. via lcMessages) we still allow it to be overridden. - if ($settings->get('inheritLocale') && empty($sessionLocale)) { - // FIXME: On multilanguage installs, CRM_Utils_System::getUFLocale() in many cases returns nothing if $dbLocale is not set - $lcMessages = $settings->get('lcMessages'); - $dbLocale = $multiLang && $lcMessages ? "_{$lcMessages}" : ''; - $chosenLocale = CRM_Utils_System::getUFLocale(); - if ($activatedLocales and !in_array($chosenLocale, explode(CRM_Core_DAO::VALUE_SEPARATOR, $activatedLocales))) { - $chosenLocale = NULL; + + // Assign the system default if the chosen locale hasn't been set. + if (empty($chosenLocale)) { + $chosenLocale = $defaultLocale; } + + // Always assign the chosen locale to the session. + $session->set('lcMessages', $chosenLocale); + } + else { + + // CRM-11993 - Use default when it's a single-language install. + $chosenLocale = $defaultLocale; - if (empty($chosenLocale)) { - //CRM-11993 - if a single-lang site, use default - $chosenLocale = $settings->get('lcMessages'); } - // set suffix for table names - use views if more than one language + /* + * Set suffix for table names in multi-language installs. + * Use views if more than one language. + */ $dbLocale = $multiLang && $chosenLocale ? "_{$chosenLocale}" : ''; - // FIXME: an ugly hack to fix CRM-4041 - global $tsLocale; + // FIXME: an ugly hack to fix CRM-4041. $tsLocale = $chosenLocale; - // FIXME: as bad aplace as any to fix CRM-5428 - // (to be moved to a sane location along with the above) + /* + * FIXME: as bad a place as any to fix CRM-5428. + * (to be moved to a sane location along with the above) + */ if (function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); } + } /** diff --git a/CRM/Core/Config.php b/CRM/Core/Config.php index 3243ebb2a3..1785fa9a93 100644 --- a/CRM/Core/Config.php +++ b/CRM/Core/Config.php @@ -349,10 +349,6 @@ class CRM_Core_Config extends CRM_Core_Config_MagicMerge { CRM_Core_DAO::executeQuery($query); } - if ($adapter = CRM_Utils_Constant::value('CIVICRM_BAO_CACHE_ADAPTER')) { - return $adapter::clearDBCache(); - } - // also delete all the import and export temp tables self::clearTempTables(); } diff --git a/CRM/Core/Controller.php b/CRM/Core/Controller.php index 4a4254c255..68c6531858 100644 --- a/CRM/Core/Controller.php +++ b/CRM/Core/Controller.php @@ -568,8 +568,8 @@ class CRM_Core_Controller extends HTML_QuickForm_Controller { public function addWizardStyle(&$wizard) { $wizard['style'] = [ 'barClass' => '', - 'stepPrefixCurrent' => ' ', - 'stepPrefixPast' => ' ', + 'stepPrefixCurrent' => ' ', + 'stepPrefixPast' => ' ', 'stepPrefixFuture' => ' ', 'subStepPrefixCurrent' => '  ', 'subStepPrefixPast' => '  ', diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 430005a894..a1cd6f7498 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -163,6 +163,11 @@ class CRM_Core_DAO extends DB_DataObject { $options = &PEAR::getStaticProperty('DB_DataObject', 'options'); $options['database'] = $dsn; $options['quote_identifiers'] = TRUE; + if (self::isSSLDSN($dsn)) { + // There are two different options arrays. + $other_options = &PEAR::getStaticProperty('DB', 'options'); + $other_options['ssl'] = TRUE; + } if (defined('CIVICRM_DAO_DEBUG')) { self::DebugLevel(CIVICRM_DAO_DEBUG); } @@ -3106,4 +3111,21 @@ SELECT contact_id } } + /** + * Does the DSN indicate the connection should use ssl. + * + * @param string $dsn + * + * @return bool + */ + public static function isSSLDSN(string $dsn):bool { + // Note that ssl= below is not an official PEAR::DB option. It doesn't know + // what to do with it. We made it up because it's not required + // to have client-side certificates to use ssl, so here you can specify + // you want that by putting ssl=1 in the DSN string. + // + // Cast to bool in case of error which we interpret as no ssl. + return (bool) preg_match('/[\?&](key|cert|ca|capath|cipher|ssl)=/', $dsn); + } + } diff --git a/CRM/Core/Error.php b/CRM/Core/Error.php index d23b83d959..04f7e8cd39 100644 --- a/CRM/Core/Error.php +++ b/CRM/Core/Error.php @@ -358,7 +358,7 @@ class CRM_Core_Error extends PEAR_ErrorStack { if (CRM_Utils_Array::value('snippet', $_REQUEST) === CRM_Core_Smarty::PRINT_JSON) { $out = [ 'status' => 'fatal', - 'content' => '
' . ts('Sorry but we are not able to provide this at the moment.') . '
', + 'content' => '
' . CRM_Core_Page::crmIcon('fa-info-circle') . ' ' . ts('Sorry but we are not able to provide this at the moment.') . '
', ]; if ($config->backtrace && CRM_Core_Permission::check('view debug output')) { $out['backtrace'] = self::parseBacktrace(debug_backtrace()); @@ -573,18 +573,6 @@ class CRM_Core_Error extends PEAR_ErrorStack { } $file_log->close(); - // Use the custom fatalErrorHandler if defined - if (in_array($priority, [PEAR_LOG_EMERG, PEAR_LOG_ALERT, PEAR_LOG_CRIT, PEAR_LOG_ERR])) { - if ($config->fatalErrorHandler && function_exists($config->fatalErrorHandler)) { - $name = $config->fatalErrorHandler; - $vars = [ - 'debugLogMessage' => $message, - 'priority' => $priority, - ]; - $name($vars); - } - } - if (!isset(\Civi::$statics[__CLASS__]['userFrameworkLogging'])) { // Set it to FALSE first & then try to set it. This is to prevent a loop as calling // $config->userFrameworkLogging can trigger DB queries & under log mode this diff --git a/CRM/Core/Form.php b/CRM/Core/Form.php index 7bd57e0dd7..ae4391a371 100644 --- a/CRM/Core/Form.php +++ b/CRM/Core/Form.php @@ -366,6 +366,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page { $type, $name, $label = '', $attributes = '', $required = FALSE, $extra = NULL ) { + if ($type === 'radio') { + CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_Form::addRadio'); + } // Fudge some extra types that quickform doesn't support $inputType = $type; if ($type == 'wysiwyg' || in_array($type, self::$html5Types)) { @@ -1202,7 +1205,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page { } } } - $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $optAttributes); + $element = $this->createElement('radio', NULL, NULL, $var, $key, $optAttributes); + if ($required) { + $element->setAttribute('required', TRUE); + } + $options[] = $element; } $group = $this->addGroup($options, $name, $title, $separator); diff --git a/CRM/Core/Key.php b/CRM/Core/Key.php index 637a3a6147..3e4ee1235b 100644 --- a/CRM/Core/Key.php +++ b/CRM/Core/Key.php @@ -110,30 +110,19 @@ class CRM_Core_Key { } /** - * @param $key + * The original version of this function, added circa 2010 and untouched + * since then, seemed intended to check for a 32-digit hex string followed + * optionally by an underscore and 4-digit number. But it had a bug where + * the optional part was never checked ever. So have decided to remove that + * second check to keep it simple since it seems like pseudo-security. + * + * @param string $key * * @return bool */ public static function valid($key) { - // a valid key is a 32 digit hex number - // followed by an optional _ and a number between 1 and 10000 - if (strpos('_', $key) !== FALSE) { - list($hash, $seq) = explode('_', $key); - - // ensure seq is between 1 and 10000 - if (!is_numeric($seq) || - $seq < 1 || - $seq > 10000 - ) { - return FALSE; - } - } - else { - $hash = $key; - } - - // ensure that hash is a 32 digit hex number - return (bool) preg_match('#[0-9a-f]{32}#i', $hash); + // ensure that key contains a 32 digit hex string + return (bool) preg_match('#[0-9a-f]{32}#i', $key); } } diff --git a/CRM/Core/Payment.php b/CRM/Core/Payment.php index 277c534611..9c998dc66d 100644 --- a/CRM/Core/Payment.php +++ b/CRM/Core/Payment.php @@ -1504,7 +1504,8 @@ abstract class CRM_Core_Payment { catch (CRM_Core_Exception $e) { Civi::log()->error('ipn_payment_callback_exception', [ 'context' => [ - 'backtrace' => CRM_Core_Error::formatBacktrace(debug_backtrace()), + 'backtrace' => $e->getTraceAsString(), + 'message' => $e->getMessage(), ], ]); } diff --git a/CRM/Core/Payment/AuthorizeNetIPN.php b/CRM/Core/Payment/AuthorizeNetIPN.php index 7946ecacbe..83697bd338 100644 --- a/CRM/Core/Payment/AuthorizeNetIPN.php +++ b/CRM/Core/Payment/AuthorizeNetIPN.php @@ -100,22 +100,20 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { * * @return bool */ - public function recur(&$input, &$ids, &$objects, $first) { + public function recur($input, $ids, $objects, $first) { $this->_isRecurring = TRUE; $recur = &$objects['contributionRecur']; $paymentProcessorObject = $objects['contribution']->_relatedObjects['paymentProcessor']['object']; // do a subscription check if ($recur->processor_id != $input['subscription_id']) { - CRM_Core_Error::debug_log_message("Unrecognized subscription."); - echo "Failure: Unrecognized subscription

"; + CRM_Core_Error::debug_log_message('Unrecognized subscription.'); + echo 'Failure: Unrecognized subscription

'; return FALSE; } $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); - $transaction = new CRM_Core_Transaction(); - $now = date('YmdHis'); //load new contribution object if required. @@ -148,18 +146,17 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { $recur->trxn_id = $recur->processor_id; $isFirstOrLastRecurringPayment = CRM_Core_Payment::RECURRING_PAYMENT_START; } - $statusName = 'In Progress'; + if (($recur->installments > 0) && ($input['subscription_paynum'] >= $recur->installments) ) { // this is the last payment - $statusName = 'Completed'; $recur->end_date = $now; $isFirstOrLastRecurringPayment = CRM_Core_Payment::RECURRING_PAYMENT_END; + // This end date update should occur in ContributionRecur::updateOnNewPayment + // testIPNPaymentRecurNoReceipt has test cover. + $recur->save(); } - $recur->modified_date = $now; - $recur->contribution_status_id = array_search($statusName, $contributionStatus); - $recur->save(); } else { // Declined @@ -168,7 +165,7 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { $recur->cancel_date = $now; $recur->save(); - $message = ts("Subscription payment failed - %1", [1 => htmlspecialchars($input['response_reason_text'])]); + $message = ts('Subscription payment failed - %1', [1 => htmlspecialchars($input['response_reason_text'])]); CRM_Core_Error::debug_log_message($message); // the recurring contribution has declined a payment or has failed @@ -180,13 +177,12 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { // check if contribution is already completed, if so we ignore this ipn if ($objects['contribution']->contribution_status_id == 1) { - $transaction->commit(); CRM_Core_Error::debug_log_message("Returning since contribution has already been handled."); - echo "Success: Contribution has already been handled

"; + echo 'Success: Contribution has already been handled

'; return TRUE; } - $this->completeTransaction($input, $ids, $objects, $transaction, $recur); + $this->completeTransaction($input, $ids, $objects); // Only Authorize.net does this so it is on the a.net class. If there is a need for other processors // to do this we should make it available via the api, e.g as a parameter, changing the nuance @@ -203,11 +199,10 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { * Get the input from passed in fields. * * @param array $input - * @param array $ids * * @throws \CRM_Core_Exception */ - public function getInput(&$input, &$ids) { + public function getInput(&$input) { $input['amount'] = $this->retrieve('x_amount', 'String'); $input['subscription_id'] = $this->retrieve('x_subscription_id', 'Integer'); $input['response_code'] = $this->retrieve('x_response_code', 'Integer'); @@ -216,7 +211,6 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { $input['response_reason_text'] = $this->retrieve('x_response_reason_text', 'String', FALSE); $input['subscription_paynum'] = $this->retrieve('x_subscription_paynum', 'Integer', FALSE, 0); $input['trxn_id'] = $this->retrieve('x_trans_id', 'String', FALSE); - $input['trxn_id'] = $this->retrieve('x_trans_id', 'String', FALSE); $input['receive_date'] = $this->retrieve('receive_date', 'String', FALSE, date('YmdHis', strtotime('now'))); if ($input['trxn_id']) { @@ -229,7 +223,7 @@ class CRM_Core_Payment_AuthorizeNetIPN extends CRM_Core_Payment_BaseIPN { $input['trxn_id'] = md5(uniqid(rand(), TRUE)); } - $billingID = $ids['billing'] = CRM_Core_BAO_LocationType::getBilling(); + $billingID = CRM_Core_BAO_LocationType::getBilling(); $params = [ 'first_name' => 'x_first_name', 'last_name' => 'x_last_name', diff --git a/CRM/Core/Payment/BaseIPN.php b/CRM/Core/Payment/BaseIPN.php index 973fbd9888..27b0074814 100644 --- a/CRM/Core/Payment/BaseIPN.php +++ b/CRM/Core/Payment/BaseIPN.php @@ -214,7 +214,7 @@ class CRM_Core_Payment_BaseIPN { * @return bool * @throws \CiviCRM_API3_Exception */ - public function failed(&$objects, &$transaction, $input = []) { + public function failed(&$objects, $transaction = NULL, $input = []) { $contribution = &$objects['contribution']; $memberships = []; if (!empty($objects['membership'])) { @@ -224,21 +224,9 @@ class CRM_Core_Payment_BaseIPN { } } - $addLineItems = FALSE; - if (empty($contribution->id)) { - $addLineItems = TRUE; - } + $addLineItems = empty($contribution->id); $participant = &$objects['participant']; - - // CRM-15546 - $contributionStatuses = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'contribution_status_id', [ - 'labelColumn' => 'name', - 'flip' => 1, - ]); - $contribution->contribution_status_id = $contributionStatuses['Failed']; - $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date); - $contribution->receipt_date = CRM_Utils_Date::isoToMysql($contribution->receipt_date); - $contribution->thankyou_date = CRM_Utils_Date::isoToMysql($contribution->thankyou_date); + $contribution->contribution_status_id = CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Failed'); $contribution->save(); // Add line items for recurring payments. @@ -266,7 +254,9 @@ class CRM_Core_Payment_BaseIPN { } } - $transaction->commit(); + if ($transaction) { + $transaction->commit(); + } Civi::log()->debug("Setting contribution status to Failed"); return TRUE; } @@ -299,7 +289,7 @@ class CRM_Core_Payment_BaseIPN { * @return bool * @throws \CiviCRM_API3_Exception */ - public function cancelled(&$objects, &$transaction, $input = []) { + public function cancelled(&$objects, $transaction = NULL, $input = []) { $contribution = &$objects['contribution']; $memberships = []; if (!empty($objects['membership'])) { @@ -353,7 +343,9 @@ class CRM_Core_Payment_BaseIPN { $this->cancelParticipant($participant->id); } } - $transaction->commit(); + if ($transaction) { + $transaction->commit(); + } Civi::log()->debug("Setting contribution status to Cancelled"); return TRUE; } @@ -477,13 +469,12 @@ class CRM_Core_Payment_BaseIPN { * @param array $input * @param array $ids * @param array $objects - * @param CRM_Core_Transaction $transaction * * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception */ - public function completeTransaction(&$input, &$ids, &$objects, $transaction = NULL) { - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, $transaction); + public function completeTransaction(&$input, &$ids, &$objects) { + CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects); } /** @@ -518,21 +509,14 @@ class CRM_Core_Payment_BaseIPN { * @param array $ids * Related object IDs. * @param array $objects - * @param array $values - * Values related to objects that have already been loaded. - * @param bool $recur - * Is it part of a recurring contribution. - * @param bool $returnMessageText - * Should text be returned instead of sent. This. - * is because the function is also used to generate pdfs - * - * @return array - * @throws \CRM_Core_Exception + * * @throws \CiviCRM_API3_Exception */ - public function sendMail(&$input, &$ids, &$objects, &$values, $recur = FALSE, $returnMessageText = FALSE) { - return CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $objects['contribution']->id, $values, - $returnMessageText); + public function sendMail($input, $ids, $objects) { + CRM_Core_Error::deprecatedFunctionWarning('this should be done via completetransaction api'); + civicrm_api3('Contribution', 'sendconfirmation', [ + 'id' => $objects['contribution']->id, + ]); } } diff --git a/CRM/Core/Payment/Manual.php b/CRM/Core/Payment/Manual.php index a2c7c5dd25..4c254691cc 100644 --- a/CRM/Core/Payment/Manual.php +++ b/CRM/Core/Payment/Manual.php @@ -238,25 +238,6 @@ class CRM_Core_Payment_Manual extends CRM_Core_Payment { return TRUE; } - /** - * Submit a manual payment. - * - * @param array $params - * Assoc array of input parameters for this transaction. - * - * @return array - */ - public function doDirectPayment(&$params) { - $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id'); - if ($params['is_pay_later']) { - $result['payment_status_id'] = array_search('Pending', $statuses); - } - else { - $result['payment_status_id'] = array_search('Completed', $statuses); - } - return $result; - } - /** * Should a receipt be sent out for a pending payment. * diff --git a/CRM/Core/Payment/PayPalIPN.php b/CRM/Core/Payment/PayPalIPN.php index 0e87056c87..b6948ddbd4 100644 --- a/CRM/Core/Payment/PayPalIPN.php +++ b/CRM/Core/Payment/PayPalIPN.php @@ -229,8 +229,10 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN { * @param bool $first * * @return void + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ - public function single(&$input, &$ids, &$objects, $recur = FALSE, $first = FALSE) { + public function single($input, $ids, $objects, $recur = FALSE, $first = FALSE) { $contribution = &$objects['contribution']; // make sure the invoice is valid and matches what we have in the contribution record @@ -256,18 +258,18 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN { $contribution->total_amount = $input['amount']; } - $transaction = new CRM_Core_Transaction(); - $status = $input['paymentStatus']; - if ($status == 'Denied' || $status == 'Failed' || $status == 'Voided') { - return $this->failed($objects, $transaction); + if ($status === 'Denied' || $status === 'Failed' || $status === 'Voided') { + $this->failed($objects); + return; } if ($status === 'Pending') { Civi::log()->debug('Returning since contribution status is Pending'); return; } - elseif ($status == 'Refunded' || $status == 'Reversed') { - return $this->cancelled($objects, $transaction); + elseif ($status === 'Refunded' || $status === 'Reversed') { + $this->cancelled($objects); + return; } elseif ($status !== 'Completed') { Civi::log()->debug('Returning since contribution status is not handled'); @@ -277,13 +279,12 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN { // check if contribution is already completed, if so we ignore this ipn $completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); if ($contribution->contribution_status_id == $completedStatusId) { - $transaction->commit(); Civi::log()->debug('PayPalIPN: Returning since contribution has already been handled. (ID: ' . $contribution->id . ').'); echo 'Success: Contribution has already been handled

'; return; } - $this->completeTransaction($input, $ids, $objects, $transaction, $recur); + $this->completeTransaction($input, $ids, $objects); } /** @@ -302,7 +303,7 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN { $membershipID = $this->retrieve('membershipID', 'Integer', FALSE); $contributionRecurID = $this->retrieve('contributionRecurID', 'Integer', FALSE); - $this->getInput($input, $ids); + $this->getInput($input); if ($component == 'event') { $ids['event'] = $this->retrieve('eventID', 'Integer', TRUE); @@ -370,17 +371,16 @@ class CRM_Core_Payment_PayPalIPN extends CRM_Core_Payment_BaseIPN { return; } } - $this->single($input, $ids, $objects, FALSE, FALSE); + $this->single($input, $ids, $objects); } /** * @param array $input - * @param array $ids * * @throws \CRM_Core_Exception */ - public function getInput(&$input, &$ids) { - $billingID = $ids['billing'] = CRM_Core_BAO_LocationType::getBilling(); + public function getInput(&$input) { + $billingID = CRM_Core_BAO_LocationType::getBilling(); $input['txnType'] = $this->retrieve('txn_type', 'String', FALSE); $input['paymentStatus'] = $this->retrieve('payment_status', 'String', FALSE); $input['invoice'] = $this->retrieve('invoice', 'String', TRUE); diff --git a/CRM/Core/Payment/PayPalProIPN.php b/CRM/Core/Payment/PayPalProIPN.php index d249aa0bef..e5c58d3de7 100644 --- a/CRM/Core/Payment/PayPalProIPN.php +++ b/CRM/Core/Payment/PayPalProIPN.php @@ -145,16 +145,19 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { /** * Process recurring contributions. + * * @param array $input * @param array $ids * @param array $objects * @param bool $first - * @return void + * + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ - public function recur(&$input, &$ids, &$objects, $first) { + public function recur($input, $ids, $objects, $first) { if (!isset($input['txnType'])) { Civi::log()->debug('PayPalProIPN: Could not find txn_type in input request.'); - echo "Failure: Invalid parameters

"; + echo 'Failure: Invalid parameters

'; return; } @@ -165,7 +168,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { // the contribution record if ($recur->invoice_id != $input['invoice']) { Civi::log()->debug('PayPalProIPN: Invoice values dont match between database and IPN request recur is ' . $recur->invoice_id . ' input is ' . $input['invoice']); - echo "Failure: Invoice values dont match between database and IPN request recur is " . $recur->invoice_id . " input is " . $input['invoice']; + echo 'Failure: Invoice values dont match between database and IPN request recur is ' . $recur->invoice_id . " input is " . $input['invoice']; return; } @@ -344,19 +347,17 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $contribution->total_amount = $input['amount']; } - $transaction = new CRM_Core_Transaction(); - $status = $input['paymentStatus']; - if ($status == 'Denied' || $status == 'Failed' || $status == 'Voided') { - $this->failed($objects, $transaction); + if ($status === 'Denied' || $status === 'Failed' || $status === 'Voided') { + $this->failed($objects); return; } if ($status === 'Pending') { Civi::log()->debug('Returning since contribution status is Pending'); return; } - elseif ($status == 'Refunded' || $status == 'Reversed') { - $this->cancelled($objects, $transaction); + elseif ($status === 'Refunded' || $status === 'Reversed') { + $this->cancelled($objects); return; } elseif ($status !== 'Completed') { @@ -367,13 +368,12 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { // check if contribution is already completed, if so we ignore this ipn $completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); if ($contribution->contribution_status_id == $completedStatusId) { - $transaction->commit(); Civi::log()->debug('PayPalProIPN: Returning since contribution has already been handled.'); echo 'Success: Contribution has already been handled

'; return; } - $this->completeTransaction($input, $ids, $objects, $transaction, $recur); + $this->completeTransaction($input, $ids, $objects); } /** @@ -423,7 +423,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $ids['contact'] = self::getValue('c', TRUE); $ids['contribution'] = self::getValue('b', TRUE); - $this->getInput($input, $ids); + $this->getInput($input); if ($this->_component == 'event') { $ids['event'] = self::getValue('e', TRUE); @@ -487,13 +487,12 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr /** * @param array $input - * @param array $ids * * @return void * @throws CRM_Core_Exception */ - public function getInput(&$input, &$ids) { - $billingID = $ids['billing'] = CRM_Core_BAO_LocationType::getBilling(); + public function getInput(&$input) { + $billingID = CRM_Core_BAO_LocationType::getBilling(); $input['txnType'] = self::retrieve('txn_type', 'String', 'POST', FALSE); $input['paymentStatus'] = self::retrieve('payment_status', 'String', 'POST', FALSE); diff --git a/CRM/Core/Payment/PaymentExpress.php b/CRM/Core/Payment/PaymentExpress.php deleted file mode 100644 index 2315f599d7..0000000000 --- a/CRM/Core/Payment/PaymentExpress.php +++ /dev/null @@ -1,223 +0,0 @@ -_mode = $mode; - $this->_paymentProcessor = $paymentProcessor; - } - - /** - * This function checks to see if we have the right config values. - * - * @internal param string $mode the mode we are operating in (live or test) - * - * @return string - * the error message if any - */ - public function checkConfig() { - $config = CRM_Core_Config::singleton(); - - $error = []; - - if (empty($this->_paymentProcessor['user_name'])) { - $error[] = ts('UserID is not set in the Administer » System Settings » Payment Processors'); - } - - if (empty($this->_paymentProcessor['password'])) { - $error[] = ts('pxAccess / pxPay Key is not set in the Administer » System Settings » Payment Processors'); - } - - if (!empty($error)) { - return implode('

', $error); - } - else { - return NULL; - } - } - - /** - * This function collects all the information from a web/api form and invokes - * the relevant payment processor specific functions to perform the transaction - * - * @param array $params - * Assoc array of input parameters for this transaction. - */ - public function doDirectPayment(&$params) { - throw new CRM_Core_Exception(ts('This function is not implemented')); - } - - /** - * Main transaction function. - * - * @param array $params - * Name value pair of contribution data. - * - * @param $component - */ - public function doTransferCheckout(&$params, $component) { - // This is broken - in 2015 this commit broke it... https://github.com/civicrm/civicrm-core/commit/204c86d59f0cfc4c4d917cc245fb41633d36916e#diff-b00e65c9829c27da8b34e35f2e64d9b6L114 - $component = strtolower($component); - $config = CRM_Core_Config::singleton(); - if ($component != 'contribute' && $component != 'event') { - throw new CRM_Core_Exception(ts('Component is invalid')); - } - - $url = CRM_Utils_System::externUrl('extern/pxIPN'); - - if ($component == 'event') { - $cancelURL = CRM_Utils_System::url('civicrm/event/register', - "_qf_Confirm_display=true&qfKey={$params['qfKey']}", - FALSE, NULL, FALSE - ); - } - elseif ($component == 'contribute') { - $cancelURL = CRM_Utils_System::url('civicrm/contribute/transact', - "_qf_Confirm_display=true&qfKey={$params['qfKey']}", - FALSE, NULL, FALSE - ); - } - - /* - * Build the private data string to pass to DPS, which they will give back to us with the - * - * transaction result. We are building this as a comma-separated list so as to avoid long URLs. - * - * Parameters passed: a=contactID, b=contributionID,c=contributionTypeID,d=invoiceID,e=membershipID,f=participantID,g=eventID - */ - - $privateData = "a={$params['contactID']},b={$params['contributionID']},c={$params['contributionTypeID']},d={$params['invoiceID']}"; - - if ($component == 'event') { - $merchantRef = substr($params['contactID'] . "-" . $params['contributionID'] . " " . substr($params['description'], 27, 20), 0, 24); - $privateData .= ",f={$params['participantID']},g={$params['eventID']}"; - } - elseif ($component == 'contribute') { - $membershipID = $params['membershipID'] ?? NULL; - if ($membershipID) { - $privateData .= ",e=$membershipID"; - } - $merchantRef = substr($params['contactID'] . "-" . $params['contributionID'] . " " . substr($params['description'], 20, 20), 0, 24); - - } - - $dpsParams = [ - 'AmountInput' => str_replace(",", "", number_format($params['amount'], 2)), - 'CurrencyInput' => $params['currencyID'], - 'MerchantReference' => $merchantRef, - 'TxnData1' => $params['qfKey'], - 'TxnData2' => $privateData, - 'TxnData3' => $component . "," . $this->_paymentProcessor['id'], - 'TxnType' => 'Purchase', - // Leave this empty for now, causes an error with DPS if we populate it - 'TxnId' => '', - 'UrlFail' => $url, - 'UrlSuccess' => $url, - ]; - // Allow further manipulation of params via custom hooks - CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $dpsParams); - - /* - * determine whether method is pxaccess or pxpay by whether signature (mac key) is defined - */ - - if (empty($this->_paymentProcessor['signature'])) { - /* - * Processor is pxpay - * - * This contains the XML/Curl functions we'll need to generate the XML request - */ - - $dpsParams['PxPayUserId'] = $this->_paymentProcessor['user_name']; - $dpsParams['PxPayKey'] = $this->_paymentProcessor['password']; - // Build a valid XML string to pass to DPS - $generateRequest = CRM_Core_Payment_PaymentExpressUtils::_valueXml($dpsParams); - - $generateRequest = CRM_Core_Payment_PaymentExpressUtils::_valueXml('GenerateRequest', $generateRequest); - // Get the special validated URL back from DPS by sending them the XML we've generated - $curl = CRM_Core_Payment_PaymentExpressUtils::_initCURL($generateRequest, $this->_paymentProcessor['url_site']); - $success = FALSE; - - if ($response = curl_exec($curl)) { - curl_close($curl); - $valid = CRM_Core_Payment_PaymentExpressUtils::_xmlAttribute($response, 'valid'); - if (1 == $valid) { - // the request was validated, so we'll get the URL and redirect to it - $uri = CRM_Core_Payment_PaymentExpressUtils::_xmlElement($response, 'URI'); - CRM_Utils_System::redirect($uri); - } - else { - // redisplay confirmation page - CRM_Utils_System::redirect($cancelURL); - } - } - else { - // calling DPS failed - throw new CRM_Core_Exception(ts('Unable to establish connection to the payment gateway.')); - } - } - else { - $processortype = "pxaccess"; - require_once 'PaymentExpress/pxaccess.inc.php'; - // URL - $PxAccess_Url = $this->_paymentProcessor['url_site']; - // User ID - $PxAccess_Userid = $this->_paymentProcessor['user_name']; - // Your DES Key from DPS - $PxAccess_Key = $this->_paymentProcessor['password']; - // Your MAC key from DPS - $Mac_Key = $this->_paymentProcessor['signature']; - - $pxaccess = new PxAccess($PxAccess_Url, $PxAccess_Userid, $PxAccess_Key, $Mac_Key); - $request = new PxPayRequest(); - $request->setAmountInput($dpsParams['AmountInput']); - $request->setTxnData1($dpsParams['TxnData1']); - $request->setTxnData2($dpsParams['TxnData2']); - $request->setTxnData3($dpsParams['TxnData3']); - $request->setTxnType($dpsParams['TxnType']); - $request->setInputCurrency($dpsParams['InputCurrency']); - $request->setMerchantReference($dpsParams['MerchantReference']); - $request->setUrlFail($dpsParams['UrlFail']); - $request->setUrlSuccess($dpsParams['UrlSuccess']); - $request_string = $pxaccess->makeRequest($request); - CRM_Utils_System::redirect($request_string); - } - } - -} diff --git a/CRM/Core/Payment/PaymentExpressUtils.php b/CRM/Core/Payment/PaymentExpressUtils.php deleted file mode 100644 index 015207e780..0000000000 --- a/CRM/Core/Payment/PaymentExpressUtils.php +++ /dev/null @@ -1,96 +0,0 @@ - $value) { - $xml .= self::_valueXml($elem, $value); - } - return $xml; - } - return "<" . $element . ">" . $value . "" . $nl; - } - - /** - * @param $xml - * @param string $name - * - * @return mixed - */ - public static function _xmlElement($xml, $name) { - $value = preg_replace('/.*<' . $name . '[^>]*>(.*)<\/' . $name . '>.*/', '\1', $xml); - return $value; - } - - /** - * @param $xml - * @param string $name - * - * @return mixed|null - */ - public static function _xmlAttribute($xml, $name) { - $value = preg_replace('/<.*' . $name . '="([^"]*)".*>/', '\1', $xml); - return $value != $xml ? $value : NULL; - } - - /** - * @param $query - * @param $url - * - * @return resource - */ - public static function &_initCURL($query, $url) { - $curl = curl_init(); - - curl_setopt($curl, CURLOPT_URL, $url); - curl_setopt($curl, CURLOPT_FRESH_CONNECT, TRUE); - curl_setopt($curl, CURLOPT_POST, TRUE); - curl_setopt($curl, CURLOPT_POSTFIELDS, $query); - curl_setopt($curl, CURLOPT_TIMEOUT, 30); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); - if (ini_get('open_basedir') == '' && ini_get('safe_mode') == 'Off') { - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, FALSE); - } - curl_setopt($curl, CURLOPT_HEADER, 0); - curl_setopt($curl, CURLOPT_SSLVERSION, 0); - - if (strtoupper(substr(@php_uname('s'), 0, 3)) === 'WIN') { - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, Civi::settings()->get('verifySSL')); - curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, Civi::settings()->get('verifySSL') ? 2 : 0); - } - return $curl; - } - -} diff --git a/CRM/Core/Smarty/plugins/block.crmButton.php b/CRM/Core/Smarty/plugins/block.crmButton.php index 390a8e7303..64543d5957 100644 --- a/CRM/Core/Smarty/plugins/block.crmButton.php +++ b/CRM/Core/Smarty/plugins/block.crmButton.php @@ -47,7 +47,7 @@ function smarty_block_crmButton($params, $text, &$smarty) { if (strpos($icon, 'fa-') !== 0) { $icon = "fa-$icon"; } - $iconMarkup = "  "; + $iconMarkup = " "; } // All other params are treated as html attributes CRM_Utils_Array::remove($params, 'icon', 'p', 'q', 'a', 'f', 'h', 'fb', 'fe'); diff --git a/CRM/Dedupe/Merger.php b/CRM/Dedupe/Merger.php index 0bab319abb..ca548fba2d 100644 --- a/CRM/Dedupe/Merger.php +++ b/CRM/Dedupe/Merger.php @@ -935,7 +935,8 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m unset($dupePairs[$index]); continue; } - if (($result = self::dedupePair($dupes, $mode, $checkPermissions, $cacheKeyString)) === FALSE) { + CRM_Utils_Hook::merge('flip', $dupes, $dupes['dstID'], $dupes['srcID']); + if (($result = self::dedupePair((int) $dupes['dstID'], (int) $dupes['srcID'], $mode, $checkPermissions, $cacheKeyString)) === FALSE) { unset($dupePairs[$index]); continue; } @@ -1005,7 +1006,11 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m } /** - * Compare 2 addresses to see if they are the same. + * Compare 2 addresses to see if they are the effectively the same. + * + * Being the same would mean same location type and any populated fields that describe the locationn match. + * + * Metadata fields such as is_primary, on_hold, manual_geocode may differ. * * @param array $mainAddress * @param array $comparisonAddress @@ -1025,6 +1030,39 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m return TRUE; } + /** + * Does the location array have valid data. + * + * While not UI-creatable some sites wind up with email or address rows with no actual email or address + * through non core-UI processes. + * + * @param array $location + * + * @return bool + */ + public static function locationHasData($location) { + return !empty(self::getLocationDataFields($location)); + } + + /** + * Get the location data from a location array, filtering out metadata. + * + * This returns data like street_address but not metadata like is_primary, on_hold etc. + * + * @param array $location + * + * @return mixed + */ + public static function getLocationDataFields($location) { + $keysToIgnore = array_merge(self::ignoredFields(), ['display', 'location_type_id']); + foreach ($location as $field => $value) { + if (in_array($field, $keysToIgnore, TRUE)) { + unset($location[$field]); + } + } + return $location; + } + /** * A function to build an array of information about location blocks that is * required when merging location fields @@ -1862,26 +1900,22 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m /** * Dedupe a pair of contacts. * - * @param array $dupes + * @param int $mainId Id of contact to keep. + * @param int $otherId Id of contact to delete. * @param string $mode * @param bool $checkPermissions * @param string $cacheKeyString * * @return bool|array + * @throws \API_Exception * @throws \CRM_Core_Exception + * @throws \CRM_Core_Exception_ResourceConflictException * @throws \CiviCRM_API3_Exception - * @throws \API_Exception + * @throws \Civi\API\Exception\UnauthorizedException */ - protected static function dedupePair($dupes, $mode = 'safe', $checkPermissions = TRUE, $cacheKeyString = NULL) { - CRM_Utils_Hook::merge('flip', $dupes, $dupes['dstID'], $dupes['srcID']); - $mainId = $dupes['dstID']; - $otherId = $dupes['srcID']; + protected static function dedupePair(int $mainId, int $otherId, $mode = 'safe', $checkPermissions = TRUE, $cacheKeyString = NULL) { $resultStats = []; - if (!$mainId || !$otherId) { - // return error - return FALSE; - } $migrationInfo = []; $conflicts = []; // Try to lock the contacts before we load the data as we don't want it changing under us. @@ -2144,13 +2178,19 @@ INNER JOIN civicrm_membership membership2 ON membership1.membership_type_id = m // If it exists on the 'main' contact already, skip it. Otherwise // if the location type exists already, log a conflict. foreach ($migrationInfo['main_details']['location_blocks'][$fieldName] as $mainAddressKey => $mainAddressRecord) { + if (!self::locationHasData($mainAddressRecord)) { + // Go ahead & overwrite the main address - it has no data in it. + // if it is the primary address then pass that honour to the address that actually has data. + $migrationInfo['location_blocks'][$fieldName][$mainAddressKey]['set_other_primary'] = $mainAddressRecord['is_primary']; + continue; + } if (self::locationIsSame($addressRecord, $mainAddressRecord)) { unset($migrationInfo[$key]); - break; + continue; } - elseif ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) { + if ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) { $conflicts[$key] = NULL; - break; + continue; } } } diff --git a/CRM/Event/BAO/Participant.php b/CRM/Event/BAO/Participant.php index aa0aaf6170..1f19c32f74 100644 --- a/CRM/Event/BAO/Participant.php +++ b/CRM/Event/BAO/Participant.php @@ -1877,19 +1877,15 @@ WHERE civicrm_participant.contact_id = {$contactID} AND * Evaluate whether a participant record is eligible for self-service transfer/cancellation. If so, * return additional participant/event details. * - * TODO: BAO-level functions shouldn't set a redirect, and it should be possible to return "false" to the - * calling function. The next refactor will add a fourth param $errors, which can be passed by reference - * from the calling function. Instead of redirecting, we will return the error. - * TODO: This function should always return FALSE when self-service has been disabled on an event. - * TODO: This function fails when the "hours until self-service" is greater than 24 or less than zero. + * TODO: This function fails when the "hours until self-service" is less than zero. * @param int $participantId * @param string $url * @param bool $isBackOffice */ - public static function getSelfServiceEligibility($participantId, $url, $isBackOffice) { + public static function getSelfServiceEligibility(int $participantId, string $url, bool $isBackOffice) : array { $optionGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'participant_role', 'id', 'name'); $query = " - SELECT cpst.name as status, cov.name as role, cp.fee_level, cp.fee_amount, cp.register_date, cp.status_id, ce.start_date, ce.title, cp.event_id + SELECT cpst.name as status, cov.name as role, cp.fee_level, cp.fee_amount, cp.register_date, cp.status_id, ce.start_date, ce.title, cp.event_id, ce.allow_selfcancelxfer FROM civicrm_participant cp LEFT JOIN civicrm_participant_status_type cpst ON cpst.id = cp.status_id LEFT JOIN civicrm_option_value cov ON cov.value = cp.role_id and cov.option_group_id = {$optionGroupId} @@ -1897,40 +1893,44 @@ WHERE civicrm_participant.contact_id = {$contactID} AND WHERE cp.id = {$participantId}"; $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { + $details['eligible'] = TRUE; $details['status'] = $dao->status; $details['role'] = $dao->role; $details['fee_level'] = trim($dao->fee_level, CRM_Core_DAO::VALUE_SEPARATOR); $details['fee_amount'] = $dao->fee_amount; $details['register_date'] = $dao->register_date; $details['event_start_date'] = $dao->start_date; + $details['allow_selfcancelxfer'] = $dao->allow_selfcancelxfer; $eventTitle = $dao->title; $eventId = $dao->event_id; } + if (!$details['allow_selfcancelxfer']) { + $details['eligible'] = FALSE; + $details['ineligible_message'] = ts('This event registration can not be transferred or cancelled. Contact the event organizer if you have questions.'); + return $details; + } //verify participant status is still Registered if ($details['status'] != 'Registered') { - $status = "You cannot transfer or cancel your registration for " . $eventTitle . ' as you are not currently registered for this event.'; - CRM_Core_Session::setStatus($status, ts('Sorry'), 'alert'); - CRM_Utils_System::redirect($url); + $details['eligible'] = FALSE; + $details['ineligible_message'] = "You cannot transfer or cancel your registration for " . $eventTitle . ' as you are not currently registered for this event.'; + return $details; } + // Determine if it's too late to self-service cancel/transfer. $query = "select start_date as start, selfcancelxfer_time as time from civicrm_event where id = " . $eventId; $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { $time_limit = $dao->time; $start_date = $dao->start; } - $start_time = new Datetime($start_date); $timenow = new Datetime(); - if (!$isBackOffice && !empty($start_time) && $start_time < $timenow) { - $status = ts('Registration for this event cannot be cancelled or transferred once the event has begun. Contact the event organizer if you have questions.'); - CRM_Core_Error::statusBounce($status, $url, ts('Sorry')); - } - if (!$isBackOffice && !empty($time_limit) && $time_limit > 0) { - $interval = $timenow->diff($start_time); - $days = $interval->format('%d'); - $hours = $interval->format('%h'); - if ($hours <= $time_limit && $days < 1) { - $status = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]); - CRM_Core_Error::statusBounce($status, $url, ts('Sorry')); + if (!$isBackOffice && !empty($time_limit)) { + $cancelHours = abs($time_limit); + $cancelInterval = new DateInterval("PT${cancelHours}H"); + $cancelInterval->invert = $time_limit < 0 ? 1 : 0; + $cancelDeadline = (new Datetime($start_date))->sub($cancelInterval); + if ($timenow > $cancelDeadline) { + $details['eligible'] = FALSE; + $details['ineligible_message'] = ts("Registration for this event cannot be cancelled or transferred less than %1 hours prior to the event's start time. Contact the event organizer if you have questions.", [1 => $time_limit]); } } return $details; diff --git a/CRM/Event/Form/Registration/Register.php b/CRM/Event/Form/Registration/Register.php index 0bb56e3e0e..e44c213172 100644 --- a/CRM/Event/Form/Registration/Register.php +++ b/CRM/Event/Form/Registration/Register.php @@ -629,6 +629,7 @@ class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration { } } } + $form->_priceSet['id'] = $form->_priceSet['id'] ?? $form->_priceSetId; $form->assign('priceSet', $form->_priceSet); } else { diff --git a/CRM/Event/Form/SelfSvcTransfer.php b/CRM/Event/Form/SelfSvcTransfer.php index 5d72f2aaa3..1d5ac36d72 100644 --- a/CRM/Event/Form/SelfSvcTransfer.php +++ b/CRM/Event/Form/SelfSvcTransfer.php @@ -137,7 +137,7 @@ class CRM_Event_Form_SelfSvcTransfer extends CRM_Core_Form { $this->_userContext = $session->readUserContext(); $this->_from_participant_id = CRM_Utils_Request::retrieve('pid', 'Positive', $this, FALSE, NULL, 'REQUEST'); $this->_userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE, NULL, 'REQUEST'); - $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST'); + $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST') ?? FALSE; $params = ['id' => $this->_from_participant_id]; $participant = $values = []; $this->_participant = CRM_Event_BAO_Participant::getValues($params, $values, $participant); @@ -165,6 +165,9 @@ class CRM_Event_Form_SelfSvcTransfer extends CRM_Core_Form { $details = CRM_Event_BAO_Participant::participantDetails($this->_from_participant_id); $selfServiceDetails = CRM_Event_BAO_Participant::getSelfServiceEligibility($this->_from_participant_id, $url, $this->isBackoffice); + if (!$selfServiceDetails['eligible']) { + CRM_Core_Error::statusBounce($selfServiceDetails['ineligible_message'], $url, ts('Sorry')); + } $details = array_merge($details, $selfServiceDetails); $this->assign('details', $details); //This participant row will be cancelled. Get line item(s) to cancel diff --git a/CRM/Event/Form/SelfSvcUpdate.php b/CRM/Event/Form/SelfSvcUpdate.php index 33b9f7f3f8..a87d7aff27 100644 --- a/CRM/Event/Form/SelfSvcUpdate.php +++ b/CRM/Event/Form/SelfSvcUpdate.php @@ -110,7 +110,7 @@ class CRM_Event_Form_SelfSvcUpdate extends CRM_Core_Form { $participant = $values = []; $this->_participant_id = CRM_Utils_Request::retrieve('pid', 'Positive', $this, FALSE, NULL, 'REQUEST'); $this->_userChecksum = CRM_Utils_Request::retrieve('cs', 'String', $this, FALSE, NULL, 'REQUEST'); - $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, NULL, 'REQUEST'); + $this->isBackoffice = CRM_Utils_Request::retrieve('is_backoffice', 'String', $this, FALSE, FALSE, 'REQUEST') ?? FALSE; $params = ['id' => $this->_participant_id]; $this->_participant = CRM_Event_BAO_Participant::getValues($params, $values, $participant); $this->_part_values = $values[$this->_participant_id]; @@ -140,6 +140,9 @@ class CRM_Event_Form_SelfSvcUpdate extends CRM_Core_Form { $contributionId = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_ParticipantPayment', $this->_participant_id, 'contribution_id', 'participant_id'); $this->assign('contributionId', $contributionId); $selfServiceDetails = CRM_Event_BAO_Participant::getSelfServiceEligibility($this->_participant_id, $url, $this->isBackoffice); + if (!$selfServiceDetails['eligible']) { + CRM_Core_Error::statusBounce($selfServiceDetails['ineligible_message'], $url, ts('Sorry')); + } $details = array_merge($details, $selfServiceDetails); $this->assign('details', $details); $this->selfsvcupdateUrl = CRM_Utils_System::url('civicrm/event/selfsvcupdate', "reset=1&id={$this->_participant_id}&id=0"); diff --git a/CRM/Export/BAO/Export.php b/CRM/Export/BAO/Export.php index 83e2fa5ba2..575aa9af24 100644 --- a/CRM/Export/BAO/Export.php +++ b/CRM/Export/BAO/Export.php @@ -132,7 +132,7 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c $addPaymentHeader = FALSE; - list($outputColumns, $metadata) = $processor->getExportStructureArrays(); + list($outputColumns) = $processor->getExportStructureArrays(); if ($processor->isMergeSameAddress()) { foreach (array_keys($processor->getAdditionalFieldsForSameAddressMerge()) as $field) { @@ -185,7 +185,7 @@ INSERT INTO {$componentTable} SELECT distinct gc.contact_id FROM civicrm_group_c while ($iterationDAO->fetch()) { $count++; $rowsThisIteration++; - $row = $processor->buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader); + $row = $processor->buildRow($query, $iterationDAO, $outputColumns, $paymentDetails, $addPaymentHeader); if ($row === FALSE) { continue; } diff --git a/CRM/Export/BAO/ExportProcessor.php b/CRM/Export/BAO/ExportProcessor.php index 58aa78ba16..1dc733d9fd 100644 --- a/CRM/Export/BAO/ExportProcessor.php +++ b/CRM/Export/BAO/ExportProcessor.php @@ -870,8 +870,8 @@ class CRM_Export_BAO_ExportProcessor { // These oddly constructed keys are for legacy reasons. Altering them will affect test success // but in time it may be good to rationalise them. $label = $this->getOutputSpecificationLabel($key, $relationshipType, $locationType, $entityLabel); - $index = $this->getOutputSpecificationIndex($key, $relationshipType, $locationType, $entityLabel); - $fieldKey = $this->getOutputSpecificationFieldKey($key, $relationshipType, $locationType, $entityLabel); + $index = $this->getOutputSpecificationIndex($key, $relationshipType, $locationType, $entityTypeID); + $fieldKey = $this->getOutputSpecificationFieldKey($key, $relationshipType, $locationType, $entityTypeID); $this->outputSpecification[$index]['header'] = $label; $this->outputSpecification[$index]['sql_columns'] = $this->getSqlColumnDefinition($fieldKey, $key); @@ -961,13 +961,12 @@ class CRM_Export_BAO_ExportProcessor { * @param \CRM_Contact_BAO_Query $query * @param CRM_Core_DAO $iterationDAO * @param array $outputColumns - * @param $metadata * @param $paymentDetails * @param $addPaymentHeader * * @return array|bool */ - public function buildRow($query, $iterationDAO, $outputColumns, $metadata, $paymentDetails, $addPaymentHeader) { + public function buildRow($query, $iterationDAO, $outputColumns, $paymentDetails, $addPaymentHeader) { $paymentTableId = $this->getPaymentTableID(); if ($this->isHouseholdToSkip($iterationDAO->contact_id)) { return FALSE; @@ -1021,7 +1020,7 @@ class CRM_Export_BAO_ExportProcessor { $this->buildRelationshipFieldsForRow($row, $iterationDAO->contact_id, $value, $field); } else { - $row[$field] = $this->getTransformedFieldValue($field, $iterationDAO, $fieldValue, $metadata, $paymentDetails); + $row[$field] = $this->getTransformedFieldValue($field, $iterationDAO, $fieldValue, $paymentDetails); } } @@ -1083,12 +1082,13 @@ class CRM_Export_BAO_ExportProcessor { * @param $field * @param $iterationDAO * @param $fieldValue - * @param $metadata * @param $paymentDetails * * @return string + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception */ - public function getTransformedFieldValue($field, $iterationDAO, $fieldValue, $metadata, $paymentDetails) { + public function getTransformedFieldValue($field, $iterationDAO, $fieldValue, $paymentDetails) { $i18n = CRM_Core_I18n::singleton(); if ($field == 'id') { @@ -1146,31 +1146,27 @@ class CRM_Export_BAO_ExportProcessor { return $i18n->crm_translate($fieldValue); default: - $fieldSpec = $metadata[$field] ?? []; + $fieldSpec = $this->outputSpecification[$this->getMungedFieldName($field)]['metadata']; // No I don't know why we do it this way & whether we could // make better use of pseudoConstants. if (!empty($fieldSpec['context'])) { return $i18n->crm_translate($fieldValue, $fieldSpec); } - if (!empty($fieldSpec['pseudoconstant'])) { + if (!empty($fieldSpec['pseudoconstant']) && !empty($fieldSpec['hasLocationType'])) { if (!empty($fieldSpec['bao'])) { - return CRM_Core_PseudoConstant::getLabel($fieldSpec['bao'], $fieldSpec['name'], $fieldValue); + $transformedValue = CRM_Core_PseudoConstant::getLabel($fieldSpec['bao'], $fieldSpec['name'], $fieldValue); + if ($transformedValue) { + return $transformedValue; + } + return $fieldValue; } - // This is not our normal syntax for pseudoconstants but I am a bit loath to - // call an external function until sure it is not increasing php processing given this - // may be iterated 100,000 times & we already have the $imProvider var loaded. - // That can be next refactor... // Yes - definitely feeling hatred for this bit of code - I know you will beat me up over it's awfulness // but I have to reach a stable point.... $varName = $fieldSpec['pseudoconstant']['var']; if ($varName === 'imProviders') { return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_IM', 'provider_id', $fieldValue); } - if ($varName === 'phoneTypes') { - return CRM_Core_PseudoConstant::getLabel('CRM_Core_DAO_Phone', 'phone_type_id', $fieldValue); - } } - return $fieldValue; } } @@ -1692,7 +1688,7 @@ class CRM_Export_BAO_ExportProcessor { * yet find a way to comment them for posterity. */ public function getExportStructureArrays() { - $outputColumns = $metadata = []; + $outputColumns = []; $queryFields = $this->getQueryFields(); foreach ($this->getReturnProperties() as $key => $value) { if (($key != 'location' || !is_array($value)) && !$this->isRelationshipTypeKey($key)) { @@ -1729,13 +1725,12 @@ class CRM_Export_BAO_ExportProcessor { $daoFieldName .= "-" . $type[1]; } $this->addOutputSpecification($actualDBFieldName, NULL, $locationType, CRM_Utils_Array::value(1, $type)); - $metadata[$daoFieldName] = $this->getMetaDataForField($actualDBFieldName); $outputColumns[$daoFieldName] = TRUE; } } } } - return [$outputColumns, $metadata]; + return [$outputColumns]; } /** @@ -2070,13 +2065,13 @@ WHERE id IN ( $deleteIDString ) */ public function getPreview($limit) { $rows = []; - list($outputColumns, $metadata) = $this->getExportStructureArrays(); + list($outputColumns) = $this->getExportStructureArrays(); $query = $this->runQuery([], ''); CRM_Core_DAO::disableFullGroupByMode(); $result = CRM_Core_DAO::executeQuery($query[1] . ' LIMIT ' . (int) $limit); CRM_Core_DAO::reenableFullGroupByMode(); while ($result->fetch()) { - $rows[] = $this->buildRow($query[0], $result, $outputColumns, $metadata, [], []); + $rows[] = $this->buildRow($query[0], $result, $outputColumns, [], []); } return $rows; } diff --git a/CRM/Financial/BAO/Payment.php b/CRM/Financial/BAO/Payment.php index 1101aab35e..f841363f14 100644 --- a/CRM/Financial/BAO/Payment.php +++ b/CRM/Financial/BAO/Payment.php @@ -79,6 +79,21 @@ class CRM_Financial_BAO_Payment { $paymentTrxnParams['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Refunded'); } + //If Payment is recorded on Failed contribution, update it to Pending. + if ($contributionStatus === 'Failed' && $params['total_amount'] > 0) { + //Enter a financial trxn to record a payment in receivable account + //as failed transaction does not insert any trxn values. Hence, if Payment is + //recorded on a failed contribution, the transition happens from Failed -> Pending -> Completed. + $ftParams = array_merge($paymentTrxnParams, [ + 'from_financial_account_id' => NULL, + 'to_financial_account_id' => $accountsReceivableAccount, + 'is_payment' => 0, + 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending'), + ]); + CRM_Core_BAO_FinancialTrxn::create($ftParams); + $contributionStatus = 'Pending'; + self::updateContributionStatus($contribution['id'], $contributionStatus); + } $trxn = CRM_Core_BAO_FinancialTrxn::create($paymentTrxnParams); if ($params['total_amount'] < 0 && !empty($params['cancelled_payment_id'])) { diff --git a/CRM/Mailing/BAO/Mailing.php b/CRM/Mailing/BAO/Mailing.php index 8169513e89..8a1e952909 100644 --- a/CRM/Mailing/BAO/Mailing.php +++ b/CRM/Mailing/BAO/Mailing.php @@ -1528,6 +1528,12 @@ ORDER BY civicrm_email.is_bulkmail DESC \Civi::log('Parameter $ids is no longer used by Mailing::create. Use the api or just pass $params', ['civi.tag' => 'deprecated']); } + // CRM-#1843 + // If it is a mass sms, set url_tracking to false + if (!empty($params['sms_provider_id'])) { + $params['url_tracking'] = 0; + } + // CRM-12430 // Do the below only for an insert // for an update, we should not set the defaults diff --git a/CRM/Mailing/Event/BAO/Reply.php b/CRM/Mailing/Event/BAO/Reply.php index 25fb8f4fff..ed240b720f 100644 --- a/CRM/Mailing/Event/BAO/Reply.php +++ b/CRM/Mailing/Event/BAO/Reply.php @@ -227,17 +227,15 @@ class CRM_Mailing_Event_BAO_Reply extends CRM_Mailing_Event_DAO_Reply { $component->id = $mailing->reply_id; $component->find(TRUE); - $message = new Mail_Mime("\n"); - $domain = CRM_Core_BAO_Domain::getDomain(); list($domainEmailName, $_) = CRM_Core_BAO_Domain::getNameAndEmail(); - $headers = [ - 'Subject' => $component->subject, - 'To' => $to, - 'From' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', - 'Reply-To' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), - 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + $params = [ + 'subject' => $component->subject, + 'toEmail' => $to, + 'from' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', + 'replyTo' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + 'returnPath' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), ]; // TODO: do we need reply tokens? @@ -257,24 +255,19 @@ class CRM_Mailing_Event_BAO_Reply extends CRM_Mailing_Event_DAO_Reply { if ($eq->format == 'HTML' || $eq->format == 'Both') { $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html']); $html = CRM_Utils_Token::replaceMailingTokens($html, $mailing, NULL, $tokens['html']); - $message->setHTMLBody($html); } if (!$html || $eq->format == 'Text' || $eq->format == 'Both') { $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']); $text = CRM_Utils_Token::replaceMailingTokens($text, $mailing, NULL, $tokens['text']); - $message->setTxtBody($text); } + $params['html'] = $html; + $params['text'] = $text; - $b = CRM_Utils_Mail::setMimeParams($message); - $h = $message->headers($headers); - CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 'a', $eq->job_id, queue_id, $eq->hash); - - $mailer = \Civi::service('pear_mail'); - if (is_object($mailer)) { - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $mailer->send($to, $h, $b); - unset($errorScope); + CRM_Mailing_BAO_Mailing::addMessageIdHeader($params, 'a', $eq->job_id, queue_id, $eq->hash); + if (CRM_Core_BAO_MailSettings::includeMessageId()) { + $params['messageId'] = $params['Message-ID']; } + CRM_Utils_Mail::send($params); } /** diff --git a/CRM/Mailing/Event/BAO/Resubscribe.php b/CRM/Mailing/Event/BAO/Resubscribe.php index e13ee159b6..3468c533b1 100644 --- a/CRM/Mailing/Event/BAO/Resubscribe.php +++ b/CRM/Mailing/Event/BAO/Resubscribe.php @@ -229,8 +229,6 @@ class CRM_Mailing_Event_BAO_Resubscribe { } } - $message = new Mail_mime("\n"); - list($addresses, $urls) = CRM_Mailing_BAO_Mailing::getVerpAndUrls($job, $queue_id, $eq->hash, $eq->email); $bao = new CRM_Mailing_BAO_Mailing(); $bao->body_text = $text; @@ -241,34 +239,28 @@ class CRM_Mailing_Event_BAO_Resubscribe { $html = CRM_Utils_Token::replaceResubscribeTokens($html, $domain, $groups, TRUE, $eq->contact_id, $eq->hash); $html = CRM_Utils_Token::replaceActionTokens($html, $addresses, $urls, TRUE, $tokens['html']); $html = CRM_Utils_Token::replaceMailingTokens($html, $dao, NULL, $tokens['html']); - $message->setHTMLBody($html); } if (!$html || $eq->format == 'Text' || $eq->format == 'Both') { $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, TRUE, $tokens['text']); $text = CRM_Utils_Token::replaceResubscribeTokens($text, $domain, $groups, FALSE, $eq->contact_id, $eq->hash); $text = CRM_Utils_Token::replaceActionTokens($text, $addresses, $urls, FALSE, $tokens['text']); $text = CRM_Utils_Token::replaceMailingTokens($text, $dao, NULL, $tokens['text']); - $message->setTxtBody($text); } - $headers = [ - 'Subject' => $component->subject, - 'From' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', - 'To' => $eq->email, - 'Reply-To' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), - 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + $params = [ + 'subject' => $component->subject, + 'from' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', + 'toEmail' => $eq->email, + 'replyTo' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + 'returnPath' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + 'html' => $html, + 'text' => $text, ]; - CRM_Mailing_BAO_Mailing::addMessageIdHeader($headers, 'e', $job, $queue_id, $eq->hash); - $b = CRM_Utils_Mail::setMimeParams($message); - $h = $message->headers($headers); - - $mailer = \Civi::service('pear_mail'); - - if (is_object($mailer)) { - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $mailer->send($eq->email, $h, $b); - unset($errorScope); + CRM_Mailing_BAO_Mailing::addMessageIdHeader($params, 'e', $job, $queue_id, $eq->hash); + if (CRM_Core_BAO_MailSettings::includeMessageId()) { + $params['messageId'] = $params['Message-ID']; } + CRM_Utils_Mail::send($params); } } diff --git a/CRM/Mailing/Event/BAO/Subscribe.php b/CRM/Mailing/Event/BAO/Subscribe.php index 1dfad54a4a..71baa417fe 100644 --- a/CRM/Mailing/Event/BAO/Subscribe.php +++ b/CRM/Mailing/Event/BAO/Subscribe.php @@ -205,12 +205,12 @@ SELECT civicrm_email.id as email_id $component->find(TRUE); - $headers = [ - 'Subject' => $component->subject, - 'From' => "\"{$domainEmailName}\" <{$domainEmailAddress}>", - 'To' => $email, - 'Reply-To' => $confirm, - 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + $params = [ + 'subject' => $component->subject, + 'from' => "\"{$domainEmailName}\" <{$domainEmailAddress}>", + 'toEmail' => $email, + 'replyTo' => $confirm, + 'returnPath' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), ]; $url = CRM_Utils_System::url('civicrm/mailing/confirm', @@ -246,24 +246,17 @@ SELECT civicrm_email.id as email_id // render the & entities in text mode, so that the links work $text = str_replace('&', '&', $text); - $message = new Mail_mime("\n"); - - $message->setHTMLBody($html); - $message->setTxtBody($text); - $b = CRM_Utils_Mail::setMimeParams($message); - $h = $message->headers($headers); - CRM_Mailing_BAO_Mailing::addMessageIdHeader($h, 's', + CRM_Mailing_BAO_Mailing::addMessageIdHeader($params, 's', $this->contact_id, $this->id, $this->hash ); - $mailer = \Civi::service('pear_mail'); - - if (is_object($mailer)) { - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $mailer->send($email, $h, $b); - unset($errorScope); + $params['html'] = $html; + $params['text'] = $text; + if (CRM_Core_BAO_MailSettings::includeMessageId()) { + $params['messageId'] = $params['Message-ID']; } + CRM_Utils_Mail::send($params); } /** diff --git a/CRM/Mailing/Event/BAO/Unsubscribe.php b/CRM/Mailing/Event/BAO/Unsubscribe.php index 920ae7d3bc..ce92b388e2 100644 --- a/CRM/Mailing/Event/BAO/Unsubscribe.php +++ b/CRM/Mailing/Event/BAO/Unsubscribe.php @@ -365,8 +365,6 @@ WHERE email = %2 } } - $message = new Mail_mime("\n"); - list($addresses, $urls) = CRM_Mailing_BAO_Mailing::getVerpAndUrls($job, $queue_id, $eq->hash, $eq->email); $bao = new CRM_Mailing_BAO_Mailing(); $bao->body_text = $text; @@ -377,37 +375,30 @@ WHERE email = %2 $html = CRM_Utils_Token::replaceUnsubscribeTokens($html, $domain, $groups, TRUE, $eq->contact_id, $eq->hash); $html = CRM_Utils_Token::replaceActionTokens($html, $addresses, $urls, TRUE, $tokens['html']); $html = CRM_Utils_Token::replaceMailingTokens($html, $dao, NULL, $tokens['html']); - $message->setHTMLBody($html); } if (!$html || $eq->format == 'Text' || $eq->format == 'Both') { $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text']); $text = CRM_Utils_Token::replaceUnsubscribeTokens($text, $domain, $groups, FALSE, $eq->contact_id, $eq->hash); $text = CRM_Utils_Token::replaceActionTokens($text, $addresses, $urls, FALSE, $tokens['text']); $text = CRM_Utils_Token::replaceMailingTokens($text, $dao, NULL, $tokens['text']); - $message->setTxtBody($text); } $emailDomain = CRM_Core_BAO_MailSettings::defaultDomain(); - $headers = [ - 'Subject' => $component->subject, - 'From' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', - 'To' => $eq->email, - 'Reply-To' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), - 'Return-Path' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + $params = [ + 'subject' => $component->subject, + 'from' => "\"$domainEmailName\" <" . CRM_Core_BAO_Domain::getNoReplyEmailAddress() . '>', + 'toEmail' => $eq->email, + 'replyTo' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + 'returnPath' => CRM_Core_BAO_Domain::getNoReplyEmailAddress(), + 'html' => $html, + 'text' => $text, ]; - CRM_Mailing_BAO_Mailing::addMessageIdHeader($headers, 'u', $job, $queue_id, $eq->hash); - - $b = CRM_Utils_Mail::setMimeParams($message); - $h = $message->headers($headers); - - $mailer = \Civi::service('pear_mail'); - - if (is_object($mailer)) { - $errorScope = CRM_Core_TemporaryErrorScope::ignoreException(); - $mailer->send($eq->email, $h, $b); - unset($errorScope); + CRM_Mailing_BAO_Mailing::addMessageIdHeader($params, 'u', $job, $queue_id, $eq->hash); + if (CRM_Core_BAO_MailSettings::includeMessageId()) { + $params['messageId'] = $params['Message-ID']; } + CRM_Utils_Mail::send($params); } /** diff --git a/CRM/Mailing/Form/Subscribe.php b/CRM/Mailing/Form/Subscribe.php index 1be92c1214..97405dce40 100644 --- a/CRM/Mailing/Form/Subscribe.php +++ b/CRM/Mailing/Form/Subscribe.php @@ -105,32 +105,11 @@ ORDER BY title"; $this->addFormRule(['CRM_Mailing_Form_Subscribe', 'formRule']); } - $addCaptcha = TRUE; - - // if recaptcha is not configured, then dont add it - // CRM-11316 Only enable ReCAPTCHA for anonymous visitors - $config = CRM_Core_Config::singleton(); + // CRM-11316 Enable ReCAPTCHA for anonymous visitors $session = CRM_Core_Session::singleton(); $contactID = $session->get('userID'); - if (empty($config->recaptchaPublicKey) || - empty($config->recaptchaPrivateKey) || - $contactID - ) { - $addCaptcha = FALSE; - } - else { - // If this is POST request and came from a block, - // lets add recaptcha only if already present. - // Gross hack for now. - if (!empty($_POST) && - !array_key_exists('recaptcha_challenge_field', $_POST) - ) { - $addCaptcha = FALSE; - } - } - - if ($addCaptcha) { + if (!$contactID) { CRM_Utils_ReCAPTCHA::enableCaptchaOnForm($this); } diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 18cffb95a5..6640b4b113 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -403,8 +403,8 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership { // ie in an update situation. $membership->find(TRUE); } - $membershipTypes = CRM_Member_PseudoConstant::membershipType(); - $title = CRM_Contact_BAO_Contact::displayName($membership->contact_id) . ' - ' . ts('Membership Type:') . ' ' . $membershipTypes[$membership->membership_type_id]; + $title = CRM_Contact_BAO_Contact::displayName($membership->contact_id) . ' - ' . ts('Membership Type:') + . ' ' . CRM_Core_PseudoConstant::getLabel('CRM_Member_BAO_Membership', 'membership_type_id', $membership->membership_type_id); $recentOther = []; if (CRM_Core_Permission::checkActionPermission('CiviMember', CRM_Core_Action::UPDATE)) { diff --git a/CRM/Member/BAO/MembershipStatus.php b/CRM/Member/BAO/MembershipStatus.php index c7ddc7636c..fa4a7665f8 100644 --- a/CRM/Member/BAO/MembershipStatus.php +++ b/CRM/Member/BAO/MembershipStatus.php @@ -197,61 +197,45 @@ class CRM_Member_BAO_MembershipStatus extends CRM_Member_DAO_MembershipStatus { /** * Find the membership status based on start date, end date, join date & status date. * + * Loop through all the membership status definitions, ordered by their + * weight. For each, we loop through all possible variations of the given + * start, end, and join dates and adjust the starts and ends based on that + * membership status's rules, where the last computed set of adjusted start + * and end becomes a candidate. Then we compare that candidate to either + * "today" or some other given date, and if it falls between the adjusted + * start and end we have a match and we stop looping through status + * definitions. Then we call a hook in case that wasn't enough loops. + * * @param string $startDate * Start date of the member whose membership status is to be calculated. * @param string $endDate * End date of the member whose membership status is to be calculated. * @param string $joinDate * Join date of the member whose membership status is to be calculated. - * @param \date|string $statusDate status date of the member whose membership status is to be calculated. - * @param bool $excludeIsAdmin the statuses those having is_admin = 1. - * Exclude the statuses those having is_admin = 1. + * @param string $statusDate + * Either the string "today" or a date against which we compare the adjusted start and end based on the status rules. + * @param bool $excludeIsAdmin + * Exclude the statuses having is_admin = 1. * @param int $membershipTypeID + * Not used directly but gets passed to the hook. * @param array $membership - * Membership params as available to calling function - passed to the hook. + * Membership params as available to calling function - not used directly but passed to the hook. * * @return array */ public static function getMembershipStatusByDate( $startDate, $endDate, $joinDate, - $statusDate = 'today', $excludeIsAdmin = FALSE, $membershipTypeID, $membership = [] + $statusDate = 'today', $excludeIsAdmin = FALSE, $membershipTypeID = NULL, $membership = [] ) { $membershipDetails = []; if (!$statusDate || $statusDate == 'today') { - $statusDate = getdate(); - $statusDate = date('Ymd', - mktime($statusDate['hours'], - $statusDate['minutes'], - $statusDate['seconds'], - $statusDate['mon'], - $statusDate['mday'], - $statusDate['year'] - ) - ); + $statusDate = date('Ymd'); } else { $statusDate = CRM_Utils_Date::customFormat($statusDate, '%Y%m%d'); } - $dates = ['start', 'end', 'join']; - $events = ['start', 'end']; - - foreach ($dates as $dat) { - if (${$dat . 'Date'} && ${$dat . 'Date'} != "null") { - ${$dat . 'Date'} = CRM_Utils_Date::customFormat(${$dat . 'Date'}, '%Y%m%d'); - - ${$dat . 'Year'} = substr(${$dat . 'Date'}, 0, 4); - - ${$dat . 'Month'} = substr(${$dat . 'Date'}, 4, 2); - - ${$dat . 'Day'} = substr(${$dat . 'Date'}, 6, 2); - } - else { - ${$dat . 'Date'} = ''; - } - } - //fix for CRM-3570, if we have statuses with is_admin=1, //exclude these statuses from calculatation during import. $where = "is_active = 1"; @@ -266,48 +250,56 @@ class CRM_Member_BAO_MembershipStatus extends CRM_Member_DAO_MembershipStatus { ORDER BY weight ASC"; $membershipStatus = CRM_Core_DAO::executeQuery($query); - $hour = $minute = $second = 0; + + $dates = [ + 'start' => ($startDate && $startDate !== 'null') ? date('Ymd', strtotime($startDate)) : '', + 'end' => ($endDate && $endDate !== 'null') ? date('Ymd', strtotime($endDate)) : '', + 'join' => ($joinDate && $joinDate !== 'null') ? date('Ymd', strtotime($joinDate)) : '', + ]; while ($membershipStatus->fetch()) { $startEvent = NULL; $endEvent = NULL; - foreach ($events as $eve) { - foreach ($dates as $dat) { + foreach (['start', 'end'] as $eve) { + foreach ($dates as $dat => $date) { // calculate start-event/date and end-event/date - if (($membershipStatus->{$eve . '_event'} == $dat . '_date') && - ${$dat . 'Date'} + if (($membershipStatus->{$eve . '_event'} === $dat . '_date') && + $date ) { if ($membershipStatus->{$eve . '_event_adjust_unit'} && $membershipStatus->{$eve . '_event_adjust_interval'} ) { + $month = date('m', strtotime($date)); + $day = date('d', strtotime($date)); + $year = date('Y', strtotime($date)); // add in months - if ($membershipStatus->{$eve . '_event_adjust_unit'} == 'month') { - ${$eve . 'Event'} = date('Ymd', mktime($hour, $minute, $second, - ${$dat . 'Month'} + $membershipStatus->{$eve . '_event_adjust_interval'}, - ${$dat . 'Day'}, - ${$dat . 'Year'} + if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'month') { + ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0, + $month + $membershipStatus->{$eve . '_event_adjust_interval'}, + $day, + $year )); } // add in days - if ($membershipStatus->{$eve . '_event_adjust_unit'} == 'day') { - ${$eve . 'Event'} = date('Ymd', mktime($hour, $minute, $second, - ${$dat . 'Month'}, - ${$dat . 'Day'} + $membershipStatus->{$eve . '_event_adjust_interval'}, - ${$dat . 'Year'} + if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'day') { + ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0, + $month, + $day + $membershipStatus->{$eve . '_event_adjust_interval'}, + $year )); } // add in years - if ($membershipStatus->{$eve . '_event_adjust_unit'} == 'year') { - ${$eve . 'Event'} = date('Ymd', mktime($hour, $minute, $second, - ${$dat . 'Month'}, - ${$dat . 'Day'}, - ${$dat . 'Year'} + $membershipStatus->{$eve . '_event_adjust_interval'} + if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'year') { + ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0, + $month, + $day, + $year + $membershipStatus->{$eve . '_event_adjust_interval'} )); } // if no interval and unit, present } else { - ${$eve . 'Event'} = ${$dat . 'Date'}; + ${$eve . 'Event'} = $date; } } } diff --git a/CRM/Member/Form/Membership.php b/CRM/Member/Form/Membership.php index 88e21e4025..3c98b8570b 100644 --- a/CRM/Member/Form/Membership.php +++ b/CRM/Member/Form/Membership.php @@ -1780,23 +1780,24 @@ class CRM_Member_Form_Membership extends CRM_Member_Form { $buttonName = $this->controller->getButtonName(); $session = CRM_Core_Session::singleton(); - if ($this->_context === 'standalone') { - if ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/member/add', + if ($buttonName == $this->getButtonName('upload', 'new')) { + if ($this->_context === 'standalone') { + $url = CRM_Utils_System::url('civicrm/member/add', 'reset=1&action=add&context=standalone' - )); + ); } else { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view', - "reset=1&cid={$this->_contactID}&selectedChild=member" - )); + $url = CRM_Utils_System::url('civicrm/contact/view/membership', + "reset=1&action=add&context=membership&cid={$this->_contactID}" + ); } } - elseif ($buttonName == $this->getButtonName('upload', 'new')) { - $session->replaceUserContext(CRM_Utils_System::url('civicrm/contact/view/membership', - "reset=1&action=add&context=membership&cid={$this->_contactID}" - )); + else { + $url = CRM_Utils_System::url('civicrm/contact/view', + "reset=1&cid={$this->_contactID}&selectedChild=member" + ); } + $session->replaceUserContext($url); } /** diff --git a/CRM/PCP/BAO/PCP.php b/CRM/PCP/BAO/PCP.php index 45c962f2fe..e71609eccd 100644 --- a/CRM/PCP/BAO/PCP.php +++ b/CRM/PCP/BAO/PCP.php @@ -661,7 +661,7 @@ WHERE pcp.id = %1 AND cc.contribution_status_id = %2 AND cc.is_test = 0"; list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail(); if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') { - $fixUrl = CRM_Utils_System::url("civicrm/admin/domain", 'action=update&reset=1'); + $fixUrl = CRM_Utils_System::url('civicrm/admin/options/from_email_address', 'reset=1'); throw new CRM_Core_Exception(ts('The site administrator needs to enter a valid \'FROM Email Address\' in Administer CiviCRM » Communications » FROM Email Addresses. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl])); } diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index 2ed80ff8eb..bc87b6e9fe 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -860,6 +860,7 @@ WHERE id = %1"; } } } + $form->_priceSet['id'] = $form->_priceSet['id'] ?? $priceSetId; $form->assign('priceSet', $form->_priceSet); $component = 'contribution'; diff --git a/CRM/SMS/Form/Provider.php b/CRM/SMS/Form/Provider.php index 9659e1a892..68e64de811 100644 --- a/CRM/SMS/Form/Provider.php +++ b/CRM/SMS/Form/Provider.php @@ -114,7 +114,7 @@ class CRM_SMS_Form_Provider extends CRM_Core_Form { if ($name) { $defaults['name'] = $name; $provider = CRM_SMS_Provider::singleton(['provider' => $name]); - $defaults['api_url'] = $provider->_apiURL; + $defaults['api_url'] = $provider->_apiURL ?? ''; } if (!$this->_id) { diff --git a/CRM/UF/Page/ProfileEditor.php b/CRM/UF/Page/ProfileEditor.php index 466278a3fa..061b7ce742 100644 --- a/CRM/UF/Page/ProfileEditor.php +++ b/CRM/UF/Page/ProfileEditor.php @@ -192,7 +192,20 @@ class CRM_UF_Page_ProfileEditor extends CRM_Core_Page { break; default: - throw new CRM_Core_Exception("Unrecognized entity type: $entityType"); + if (strpos($entityType, 'Model') !== FALSE) { + $entity = str_replace('Model', '', $entityType); + $backboneModel = self::convertCiviModelToBackboneModel( + $entity, + ts('%1', [1 => $entity]), + $availableFields + ); + if (!empty($backboneModel['schema'])) { + $civiSchema[$entityType] = $backboneModel; + } + } + if (!isset($civiSchema[$entityType])) { + throw new CRM_Core_Exception("Unrecognized entity type: $entityType"); + } } } diff --git a/CRM/Upgrade/Incremental/php/FiveTwentyEight.php b/CRM/Upgrade/Incremental/php/FiveTwentyEight.php index 3c040d82b2..2d10885877 100644 --- a/CRM/Upgrade/Incremental/php/FiveTwentyEight.php +++ b/CRM/Upgrade/Incremental/php/FiveTwentyEight.php @@ -26,9 +26,9 @@ class CRM_Upgrade_Incremental_php_FiveTwentyEight extends CRM_Upgrade_Incrementa */ public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) { // Example: Generate a pre-upgrade message. - // if ($rev == '5.12.34') { - // $preUpgradeMessage .= '

' . ts('A new permission, "%1", has been added. This permission is now used to control access to the Manage Tags screen.', array(1 => ts('manage tags'))) . '

'; - // } + if ($rev == '5.28.alpha1') { + $preUpgradeMessage .= CRM_Upgrade_Incremental_php_FiveTwentyEight::createWpFilesMessage(); + } } /** @@ -40,10 +40,39 @@ class CRM_Upgrade_Incremental_php_FiveTwentyEight extends CRM_Upgrade_Incrementa * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs. */ public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { - // Example: Generate a post-upgrade message. - // if ($rev == '5.12.34') { - // $postUpgradeMessage .= '

' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'."); - // } + // Example: Generate a pre-upgrade message. + if ($rev == '5.28.alpha1') { + $postUpgradeMessage .= CRM_Upgrade_Incremental_php_FiveTwentyEight::createWpFilesMessage(); + } + } + + public static function createWpFilesMessage() { + if (!function_exists('civi_wp')) { + return ''; + } + + if (isset($GLOBALS['civicrm_paths']['civicrm.files']['path'])) { + // They've explicitly chosen to use a non-default path. + return ''; + } + + $table = '' + . sprintf('', ts('[civicrm.files] Path')) + . sprintf('', ts('5.29 Default:'), wp_upload_dir()['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR) + . sprintf('', ts('5.28 Default:'), CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage()['path']) + . sprintf('', ts('Active Value:'), Civi::paths()->getVariable('civicrm.files', 'path')) + . sprintf('', ts('[civicrm.files] URL')) + . sprintf('', ts('5.29 Default:'), wp_upload_dir()['baseurl'] . '/civicrm/') + . sprintf('', ts('5.28 Default:'), CRM_Core_Config::singleton()->userSystem->getDefaultFileStorage()['url']) + . sprintf('', ts('Active Value:'), Civi::paths()->getVariable('civicrm.files', 'url')) + . '
%s
%s%s
%s%s
%s%s
%s
%s%s
%s%s
%s%s
'; + + return '

' . ts('Starting with version 5.29.0, CiviCRM on WordPress may make a subtle change in the calculation of [civicrm.files]. + To ensure a smooth upgrade, please review the following table. All paths and URLs should appear the same. If there is any discrepancy, + then consult the upgrade documentation.', [ + 1 => 'https://docs.civicrm.org/sysadmin/en/latest/upgrade/version-specific/#civicrm-5.29', + 2 => '...wp-content/uploads/civicrm', + ]) . '

' . $table; } /* @@ -58,8 +87,8 @@ class CRM_Upgrade_Incremental_php_FiveTwentyEight extends CRM_Upgrade_Incrementa * @param string $rev */ public function upgrade_5_28_alpha1($rev) { - $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); $this->addTask('Populate missing Contact Type name fields', 'populateMissingContactTypeName'); + $this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev); $this->addTask('Add icon column to civicrm_custom_group', 'addColumn', 'civicrm_custom_group', 'icon', "varchar(255) COMMENT 'crm-i icon class' DEFAULT NULL"); $this->addTask('Remove index on medium_id from civicrm_activity', 'dropIndex', 'civicrm_activity', 'index_medium_id'); diff --git a/CRM/Upgrade/Incremental/sql/5.29.alpha1.mysql.tpl b/CRM/Upgrade/Incremental/sql/5.29.alpha1.mysql.tpl index 7508dec80c..18b176d4b0 100644 --- a/CRM/Upgrade/Incremental/sql/5.29.alpha1.mysql.tpl +++ b/CRM/Upgrade/Incremental/sql/5.29.alpha1.mysql.tpl @@ -1,5 +1,9 @@ {* file to handle db changes in 5.29.alpha1 during upgrade *} +{* https://github.com/civicrm/civicrm-core/pull/17824 *} +UPDATE civicrm_status_pref SET name = 'checkExtensionsOk' WHERE name = 'extensionsOk'; +UPDATE civicrm_status_pref SET name = 'checkExtensionsUpdates' WHERE name = 'extensionUpdates'; + -- The RelationshipCache is a high-level index/cache for querying relationships. DROP TABLE IF EXISTS `civicrm_relationship_cache`; CREATE TABLE `civicrm_relationship_cache` ( @@ -24,3 +28,20 @@ CREATE TABLE `civicrm_relationship_cache` ( CONSTRAINT FK_civicrm_relationship_cache_near_contact_id FOREIGN KEY (`near_contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE, CONSTRAINT FK_civicrm_relationship_cache_far_contact_id FOREIGN KEY (`far_contact_id`) REFERENCES `civicrm_contact`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci; + +-- Fix missing resubscribeUrl token. There doesn't seem to be any precedent +-- for doing an upgrade for these, since the last update was in 2009 when +-- the token went missing and it had no upgrade script for it. Also unlike +-- message templates, there doesn't seem to be a way to tell whether it's +-- been changed. Using ts is a bit unreliable if the translation has changed +-- but it would be no worse than now and just end up not updating it. +-- Also, I'm drawing a blank on why the %3 is replaced differently during +-- install than during upgrade, hence the OR clause. +{capture assign=unsubgroup}{ldelim}unsubscribe.group{rdelim}{/capture} +{capture assign=actresub}{ldelim}action.resubscribe{rdelim}{/capture} +{capture assign=actresuburl}{ldelim}action.resubscribeUrl{rdelim}{/capture} +UPDATE civicrm_mailing_component +SET body_text = '{ts escape="sql" 1=$unsubgroup 2=$actresub 3=$actresuburl}You have been un-subscribed from the following groups: %1. You can re-subscribe by mailing %2 or clicking %3{/ts}' +WHERE component_type = 'Unsubscribe' +AND (body_text = '{ts escape="sql" 1=$unsubgroup 2=$actresub}You have been un-subscribed from the following groups: %1. You can re-subscribe by mailing %2 or clicking %3{/ts}' + OR body_text = '{ts escape="sql" 1=$unsubgroup 2=$actresub}You have been un-subscribed from the following groups: %1. You can re-subscribe by mailing %2 or clicking {/ts}'); diff --git a/CRM/Utils/Check.php b/CRM/Utils/Check.php index e92bb9ec7c..1e4d9f7df7 100644 --- a/CRM/Utils/Check.php +++ b/CRM/Utils/Check.php @@ -170,7 +170,7 @@ class CRM_Utils_Check { } /** - * Run all system checks. + * Run all enabled system checks. * * This functon is wrapped by the System.check api. * @@ -180,21 +180,10 @@ class CRM_Utils_Check { * @param bool $max * Whether to return just the maximum non-hushed severity * - * @return array - * Array of CRM_Utils_Check_Message objects + * @return CRM_Utils_Check_Message[] */ public static function checkAll($max = FALSE) { - $messages = []; - 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); + $messages = self::checkStatus(); uasort($messages, [__CLASS__, 'severitySort']); @@ -212,6 +201,38 @@ class CRM_Utils_Check { return ($max) ? $maxSeverity : $messages; } + /** + * @param array $statusNames + * Optionally specify the names of specific checks to run, or leave empty to run all + * @param bool $includeDisabled + * Run checks that have been explicitly disabled (default false) + * + * @return CRM_Utils_Check_Message[] + */ + public static function checkStatus($statusNames = [], $includeDisabled = FALSE) { + $messages = []; + $checksNeeded = $statusNames; + foreach (glob(__DIR__ . '/Check/Component/*.php') as $filePath) { + $className = 'CRM_Utils_Check_Component_' . basename($filePath, '.php'); + /* @var CRM_Utils_Check_Component $component */ + $component = new $className(); + if ($includeDisabled || $component->isEnabled()) { + $messages = array_merge($messages, $component->checkAll($statusNames, $includeDisabled)); + } + if ($statusNames) { + // Early return if we have already run (or skipped) all the requested checks. + $checksNeeded = array_diff($checksNeeded, $component->getAllChecks()); + if (!$checksNeeded) { + return $messages; + } + } + } + + CRM_Utils_Hook::check($messages, $statusNames, $includeDisabled); + + return $messages; + } + /** * @param int $level * @return string diff --git a/CRM/Utils/Check/Component.php b/CRM/Utils/Check/Component.php index 3ecf8bd347..5b5dcc7462 100644 --- a/CRM/Utils/Check/Component.php +++ b/CRM/Utils/Check/Component.php @@ -44,26 +44,60 @@ abstract class CRM_Utils_Check_Component { return TRUE; } + /** + * Get the names of all check functions in this class + * + * @return string[] + */ + public function getAllChecks() { + return array_filter(get_class_methods($this), function($method) { + return $method !== 'checkAll' && strpos($method, 'check') === 0; + }); + } + /** * Run all checks in this class. * - * @return array - * [CRM_Utils_Check_Message] + * @param array $requestedChecks + * Optionally specify the names of specific checks requested, or leave empty to run all + * @param bool $includeDisabled + * Run checks that have been explicitly disabled (default false) * - * @throws \API_Exception + * @return CRM_Utils_Check_Message[] + * + * @throws API_Exception * @throws \Civi\API\Exception\UnauthorizedException */ - public function checkAll() { + public function checkAll($requestedChecks = [], $includeDisabled = FALSE) { $messages = []; - foreach (get_class_methods($this) as $method) { + foreach ($this->getAllChecks() as $method) { // Note that we should check if the test is disabled BEFORE running it in case it's disabled for performance. - if ($method !== 'checkAll' && strpos($method, 'check') === 0 && !$this->isDisabled($method)) { - $messages = array_merge($messages, $this->$method()); + if ($this->isRequested($method, $requestedChecks) && ($includeDisabled || !$this->isDisabled($method))) { + $messages = array_merge($messages, $this->$method($includeDisabled)); } } return $messages; } + /** + * Is this check one of those requested + * + * @param string $method + * @param array $requestedChecks + * @return bool + */ + private function isRequested($method, $requestedChecks) { + if (!$requestedChecks) { + return TRUE; + } + foreach ($requestedChecks as $name) { + if (strpos($name, $method) === 0) { + return TRUE; + } + } + return FALSE; + } + /** * Is the specified check disabled. * diff --git a/CRM/Utils/Check/Component/Env.php b/CRM/Utils/Check/Component/Env.php index 76ae62ca5f..f3a494ab95 100644 --- a/CRM/Utils/Check/Component/Env.php +++ b/CRM/Utils/Check/Component/Env.php @@ -157,13 +157,14 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { } /** + * @param bool $force * @return CRM_Utils_Check_Message[] */ - public function checkOutboundMail() { + public function checkOutboundMail($force = FALSE) { $messages = []; // CiviMail doesn't work in non-production environments; skip. - if (CRM_Core_Config::environment() != 'Production') { + if (!$force && CRM_Core_Config::environment() != 'Production') { return $messages; } @@ -188,13 +189,14 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { /** * Check that domain email and org name are set + * @param bool $force * @return CRM_Utils_Check_Message[] */ - public function checkDomainNameEmail() { + public function checkDomainNameEmail($force = FALSE) { $messages = []; // CiviMail doesn't work in non-production environments; skip. - if (CRM_Core_Config::environment() != 'Production') { + if (!$force && CRM_Core_Config::environment() != 'Production') { return $messages; } @@ -238,13 +240,14 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { /** * Checks if a default bounce handling mailbox is set up + * @param bool $force * @return CRM_Utils_Check_Message[] */ - public function checkDefaultMailbox() { + public function checkDefaultMailbox($force = FALSE) { $messages = []; // CiviMail doesn't work in non-production environments; skip. - if (CRM_Core_Config::environment() != 'Production') { + if (!$force && CRM_Core_Config::environment() != 'Production') { return $messages; } @@ -273,14 +276,15 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { /** * Checks if cron has run in the past hour (3600 seconds) + * @param bool $force * @return CRM_Utils_Check_Message[] * @throws CRM_Core_Exception */ - public function checkLastCron() { + public function checkLastCron($force = FALSE) { $messages = []; // Cron doesn't work in non-production environments; skip. - if (CRM_Core_Config::environment() != 'Production') { + if (!$force && CRM_Core_Config::environment() != 'Production') { return $messages; } @@ -321,7 +325,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { // After 1 day (86400 seconds) increase the error level $level = ($lastCron > $now - 86400) ? \Psr\Log\LogLevel::WARNING : \Psr\Log\LogLevel::ERROR; } - $msg .= '

' . ts('To enable scheduling support, please set up the cron job.') . + $msg .= '

' . ts('A cron job is required to execute scheduled jobs automatically.') . '
' . CRM_Utils_System::docURL2('sysadmin/setup/jobs/') . '

'; } @@ -655,7 +659,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { if (!$okextensions && !$updates && !$errors) { $messages[] = new CRM_Utils_Check_Message( - 'extensionsOk', + __FUNCTION__ . 'Ok', ts('No extensions installed. Browse available extensions.', [ 1 => 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"', ]), @@ -677,7 +681,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { if ($updates) { $messages[] = new CRM_Utils_Check_Message( - 'extensionUpdates', + __FUNCTION__ . 'Updates', '', ts('Extension Update Available', ['plural' => '%count Extension Updates Available', 'count' => count($updates)]), \Psr\Log\LogLevel::WARNING, @@ -693,7 +697,7 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { $message = ts('All extensions are up-to-date:'); } $messages[] = new CRM_Utils_Check_Message( - 'extensionsOk', + __FUNCTION__ . 'Ok', $message . '', ts('Extensions'), \Psr\Log\LogLevel::INFO, @@ -820,13 +824,14 @@ class CRM_Utils_Check_Component_Env extends CRM_Utils_Check_Component { /** * Ensure reply id is set to any default value + * @param bool $force * @return CRM_Utils_Check_Message[] */ - public function checkReplyIdForMailing() { + public function checkReplyIdForMailing($force = FALSE) { $messages = []; // CiviMail doesn't work in non-production environments; skip. - if (CRM_Core_Config::environment() != 'Production') { + if (!$force && CRM_Core_Config::environment() != 'Production') { return $messages; } diff --git a/CRM/Utils/Hook.php b/CRM/Utils/Hook.php index 5a5e612c05..381b69f8f6 100644 --- a/CRM/Utils/Hook.php +++ b/CRM/Utils/Hook.php @@ -132,7 +132,7 @@ abstract class CRM_Utils_Hook { * but also accepts enough information to support Symfony Event * dispatching. * - * @param array|int $names + * @param array $names * (Recommended) Array of parameter names, in order. * Using an array is recommended because it enables full * event-broadcasting behaviors. @@ -155,15 +155,14 @@ abstract class CRM_Utils_Hook { if (!is_array($names)) { // We were called with the old contract wherein $names is actually an int. // Symfony dispatcher requires some kind of name. - // TODO: Emit a warning, eg - // error_log("Warning: hook_$fnSuffix does not give names for its parameters. It will present odd names to any Symfony event listeners."); + Civi::log()->warning("hook_$fnSuffix should be updated to pass an array of parameter names to CRM_Utils_Hook::invoke().", ['civi.tag' => 'deprecated']); $compatNames = ['arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6']; $names = array_slice($compatNames, 0, (int) $names); } $event = \Civi\Core\Event\GenericHookEvent::createOrdered( $names, - array(&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6) + [&$arg1, &$arg2, &$arg3, &$arg4, &$arg5, &$arg6] ); \Civi::dispatcher()->dispatch('hook_' . $fnSuffix, $event); return $event->getReturnValues(); @@ -678,7 +677,7 @@ abstract class CRM_Utils_Hook { * the return value is ignored */ public static function activeTheme(&$theme, $context) { - return self::singleton()->invoke(array('theme', 'context'), $theme, $context, + return self::singleton()->invoke(['theme', 'context'], $theme, $context, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_activeTheme' ); @@ -869,7 +868,7 @@ abstract class CRM_Utils_Hook { * @return mixed */ public static function alterAdminPanel(&$panels) { - return self::singleton()->invoke(array('panels'), $panels, + return self::singleton()->invoke(['panels'], $panels, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_alterAdminPanel' ); @@ -2411,13 +2410,20 @@ abstract class CRM_Utils_Hook { /** * Check system status. * - * @param array $messages - * Array. A list of messages regarding system status. + * @param CRM_Utils_Check_Message[] $messages + * A list of messages regarding system status + * @param array $statusNames + * If specified, only these checks are being requested and others should be skipped + * @param bool $includeDisabled + * Run checks that have been explicitly disabled (default false) * @return mixed */ - public static function check(&$messages) { - return self::singleton() - ->invoke(['messages'], $messages, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject, 'civicrm_check'); + public static function check(&$messages, $statusNames = [], $includeDisabled = FALSE) { + return self::singleton()->invoke(['messages'], + $messages, $statusNames, $includeDisabled, + self::$_nullObject, self::$_nullObject, self::$_nullObject, + 'civicrm_check' + ); } /** diff --git a/CRM/Utils/Mail.php b/CRM/Utils/Mail.php index a49cb27acd..0ac404f143 100644 --- a/CRM/Utils/Mail.php +++ b/CRM/Utils/Mail.php @@ -152,6 +152,8 @@ class CRM_Utils_Mail { * text : text of the message * html : html version of the message * replyTo : reply-to header in the email + * returnpath : email address for bounces to be sent to + * messageId : Message ID for this email mesage * attachments: an associative array of * fullPath : complete pathname to the file * mime_type: mime type of the attachment @@ -223,7 +225,7 @@ class CRM_Utils_Mail { } $headers['Date'] = date('r'); if ($includeMessageId) { - $headers['Message-ID'] = '<' . uniqid('civicrm_', TRUE) . "@$emailDomain>"; + $headers['Message-ID'] = $params['messageId'] ?? '<' . uniqid('civicrm_', TRUE) . "@$emailDomain>"; } if (!empty($params['autoSubmitted'])) { $headers['Auto-Submitted'] = "Auto-Generated"; diff --git a/CRM/Utils/REST.php b/CRM/Utils/REST.php index 5b436c0f20..03c7c26d1c 100644 --- a/CRM/Utils/REST.php +++ b/CRM/Utils/REST.php @@ -212,7 +212,7 @@ class CRM_Utils_REST { // 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"); + $docLink = CRM_Utils_System::docURL2('sysadmin/setup/jobs', TRUE); $key = $requestParams['key'] ?? NULL; if (empty($key)) { return self::error("FATAL: mandatory param 'key' missing. More info at: " . $docLink); diff --git a/CRM/Utils/SQL/TempTable.php b/CRM/Utils/SQL/TempTable.php index 9d886dced3..f0024d7ef7 100644 --- a/CRM/Utils/SQL/TempTable.php +++ b/CRM/Utils/SQL/TempTable.php @@ -148,8 +148,7 @@ class CRM_Utils_SQL_TempTable { return ''; } $dbUTF = CRM_Core_BAO_SchemaHandler::getDBCollation(); - if (in_array($dbUTF, ['utf8_unicode_ci', 'utf8mb4_unicode_ci']) - && in_array($dbUTF, ['utf8', 'utf8mb4'])) { + if (strpos($dbUTF, 'utf8') !== FALSE) { return ''; } return self::UTF8; @@ -168,7 +167,7 @@ class CRM_Utils_SQL_TempTable { $this->toSQL('CREATE'), $columns, $this->memory ? self::MEMORY : self::INNODB, - $this->utf8 ? self::UTF8 : '' + $this->getUtf8String() ); CRM_Core_DAO::executeQuery($sql, [], TRUE, NULL, TRUE, FALSE); $this->createSql = $sql; diff --git a/CRM/Utils/System.php b/CRM/Utils/System.php index 42b0960d2b..cedef4ca0d 100644 --- a/CRM/Utils/System.php +++ b/CRM/Utils/System.php @@ -600,7 +600,7 @@ class CRM_Utils_System { // also make sure the key is sent and is valid $key = trim(CRM_Utils_Array::value('key', $_REQUEST)); - $docAdd = "More info at:" . CRM_Utils_System::docURL2("Managing Scheduled Jobs", TRUE, NULL, NULL, NULL, "wiki"); + $docAdd = "More info at: " . CRM_Utils_System::docURL2('sysadmin/setup/jobs', TRUE); if (!$key) { return self::authenticateAbort( @@ -1497,7 +1497,7 @@ class CRM_Utils_System { = CRM_Contribute_BAO_Contribution::$_exportableFields = CRM_Pledge_BAO_Pledge::$_exportableFields = CRM_Core_BAO_CustomField::$_importFields - = CRM_Core_BAO_Cache::$_cache = CRM_Core_DAO::$_dbColumnValueCache = NULL; + = CRM_Core_DAO::$_dbColumnValueCache = NULL; CRM_Core_OptionGroup::flushAll(); CRM_Utils_PseudoConstant::flushAll(); diff --git a/CRM/Utils/System/Drupal8.php b/CRM/Utils/System/Drupal8.php index 53e2a67dea..78479501e3 100644 --- a/CRM/Utils/System/Drupal8.php +++ b/CRM/Utils/System/Drupal8.php @@ -56,6 +56,9 @@ class CRM_Utils_System_Drupal8 extends CRM_Utils_System_DrupalBase { // Validate the user object $violations = $account->validate(); if (count($violations)) { + foreach ($violations as $violation) { + CRM_Core_Session::setStatus($violation->getPropertyPath() . ': ' . $violation->getMessage(), '', 'alert'); + } return FALSE; } diff --git a/CRM/Utils/System/WordPress.php b/CRM/Utils/System/WordPress.php index 134fa0ffb2..4e86eb89bf 100644 --- a/CRM/Utils/System/WordPress.php +++ b/CRM/Utils/System/WordPress.php @@ -54,13 +54,6 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base { }; Civi::paths()->register('cms', $cmsRoot); Civi::paths()->register('cms.root', $cmsRoot); - Civi::paths()->register('civicrm.files', function () { - $upload_dir = wp_get_upload_dir(); - return [ - 'path' => $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR, - 'url' => $upload_dir['baseurl'] . '/civicrm/', - ]; - }); Civi::paths()->register('civicrm.root', function () { return [ 'path' => CIVICRM_PLUGIN_DIR . 'civicrm' . DIRECTORY_SEPARATOR, @@ -140,6 +133,9 @@ class CRM_Utils_System_WordPress extends CRM_Utils_System_Base { * Moved from CRM_Utils_System_Base */ public function getDefaultFileStorage() { + // NOTE: On WordPress, this will be circumvented in the future. However, + // should retain it to allow transitional/upgrade code determine the old value. + $config = CRM_Core_Config::singleton(); $cmsUrl = CRM_Utils_System::languageNegotiationURL($config->userFrameworkBaseURL, FALSE, TRUE); $cmsPath = $this->cmsRootPath(); diff --git a/Civi/Api4/Action/System/Check.php b/Civi/Api4/Action/System/Check.php index 9080f60a8d..19df2a2a11 100644 --- a/Civi/Api4/Action/System/Check.php +++ b/Civi/Api4/Action/System/Check.php @@ -14,12 +14,39 @@ namespace Civi\Api4\Action\System; /** * Retrieve system notices, warnings, errors, etc. + * @method bool getIncludeDisabled() */ class Check extends \Civi\Api4\Generic\BasicGetAction { + /** + * Run checks that have been explicitly disabled (default false) + * @var bool + */ + protected $includeDisabled = FALSE; + + /** + * @param bool $includeDisabled + * @return Check + */ + public function setIncludeDisabled(bool $includeDisabled): Check { + $this->includeDisabled = $includeDisabled; + return $this; + } + protected function getRecords() { - $messages = []; - foreach (\CRM_Utils_Check::checkAll() as $message) { + $messages = $names = []; + + // Filtering by name relies on the component check rather than the api arrayQuery + // @see \CRM_Utils_Check_Component::isCheckable + foreach ($this->where as $i => $clause) { + if ($clause[0] == 'name' && !empty($clause[2]) && in_array($clause[1], ['=', 'IN'], TRUE)) { + $names = (array) $clause[2]; + unset($this->where[$i]); + break; + } + } + + foreach (\CRM_Utils_Check::checkStatus($names, $this->includeDisabled) as $message) { $messages[] = $message->toArray(); } return $messages; diff --git a/Civi/Api4/Generic/AbstractGetAction.php b/Civi/Api4/Generic/AbstractGetAction.php index aaea4ca3c2..153f2e60a0 100644 --- a/Civi/Api4/Generic/AbstractGetAction.php +++ b/Civi/Api4/Generic/AbstractGetAction.php @@ -105,7 +105,7 @@ abstract class AbstractGetAction extends AbstractQueryAction { protected function _itemsToGet($field) { foreach ($this->where as $clause) { // Look for exact-match operators (=, IN, or LIKE with no wildcard) - if ($clause[0] == $field && (in_array($clause[1], ['=', 'IN']) || ($clause[1] == 'LIKE' && !(is_string($clause[2]) && strpos($clause[2], '%') !== FALSE)))) { + if ($clause[0] == $field && (in_array($clause[1], ['=', 'IN'], TRUE) || ($clause[1] == 'LIKE' && !(is_string($clause[2]) && strpos($clause[2], '%') !== FALSE)))) { return (array) $clause[2]; } } diff --git a/Civi/Api4/Generic/AbstractSaveAction.php b/Civi/Api4/Generic/AbstractSaveAction.php index 953f6c59c9..b12dc33204 100644 --- a/Civi/Api4/Generic/AbstractSaveAction.php +++ b/Civi/Api4/Generic/AbstractSaveAction.php @@ -20,7 +20,14 @@ namespace Civi\Api4\Generic; /** - * Base class for all `Save` api actions. + * Create or update one or more $ENTITIES. + * + * Pass an array of one or more $ENTITY to save in the `records` param. + * + * If creating more than one $ENTITY with similar values, use the `defaults` param. + * + * Set `reload` if you need the api to return complete records for each saved $ENTITY + * (including values that were unchanged in from updated $ENTITIES). * * @method $this setRecords(array $records) Set array of records to be saved. * @method array getRecords() diff --git a/Civi/Api4/Generic/BasicSaveAction.php b/Civi/Api4/Generic/BasicSaveAction.php index 4ddb4234df..c1316ede97 100644 --- a/Civi/Api4/Generic/BasicSaveAction.php +++ b/Civi/Api4/Generic/BasicSaveAction.php @@ -22,11 +22,7 @@ namespace Civi\Api4\Generic; use Civi\API\Exception\NotImplementedException; /** - * $ACTION one or more $ENTITIES. - * - * If saving more than one new $ENTITY with similar values, use the `defaults` parameter. - * - * Set `reload` if you need the api to return complete $ENTITY records. + * @inheritDoc */ class BasicSaveAction extends AbstractSaveAction { @@ -37,7 +33,7 @@ class BasicSaveAction extends AbstractSaveAction { private $setter; /** - * Basic Create constructor. + * Basic Save constructor. * * @param string $entityName * @param string $actionName diff --git a/Civi/Api4/Generic/DAOSaveAction.php b/Civi/Api4/Generic/DAOSaveAction.php index c6d0d36aa8..a3d8a288f5 100644 --- a/Civi/Api4/Generic/DAOSaveAction.php +++ b/Civi/Api4/Generic/DAOSaveAction.php @@ -20,11 +20,7 @@ namespace Civi\Api4\Generic; /** - * Create or update one or more $ENTITIES. - * - * If creating more than one $ENTITY with similar values, use the `defaults` param. - * - * Set `reload` if you need the api to return complete records for each saved $ENTITY. + * @inheritDoc */ class DAOSaveAction extends AbstractSaveAction { use Traits\DAOActionTrait; diff --git a/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php b/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php index 7f16a88e69..4ae6096c50 100644 --- a/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php +++ b/Civi/Api4/Generic/Traits/ArrayQueryActionTrait.php @@ -60,7 +60,7 @@ trait ArrayQueryActionTrait { * @return bool */ private function evaluateFilters($row) { - $where = $this->getWhere(); + $where = array_values($this->getWhere()); $allConditions = in_array($where[0], ['AND', 'OR', 'NOT']) ? $where : ['AND', $where]; return $this->walkFilters($row, $allConditions); } diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index 617b6dee2c..5b96e40ef0 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -210,7 +210,7 @@ class Container { ->setFactory('CRM_Utils_Mail::createMailer'); if (empty(\Civi::$statics[__CLASS__]['boot'])) { - throw new \RuntimeException("Cannot initialize container. Boot services are undefined."); + throw new \RuntimeException('Cannot initialize container. Boot services are undefined.'); } foreach (\Civi::$statics[__CLASS__]['boot'] as $bootService => $def) { $container->setDefinition($bootService, new Definition())->setSynthetic(TRUE)->setPublic(TRUE); @@ -461,20 +461,19 @@ class Container { */ public static function createPrevNextCache($container) { $setting = \Civi::settings()->get('prevNextBackend'); - if ($setting === 'default') { - // For initial release (5.8.x), continue defaulting to SQL. - $isTransitional = version_compare(\CRM_Utils_System::version(), '5.9.alpha1', '<'); + if (!$setting || $setting === 'default') { $cacheDriver = \CRM_Utils_Cache::getCacheDriver(); $service = 'prevnext.driver.' . strtolower($cacheDriver); - return $container->has($service) && !$isTransitional + return $container->has($service) ? $container->get($service) : $container->get('prevnext.driver.sql'); } - else { - return $container->get('prevnext.driver.' . $setting); - } + return $container->get('prevnext.driver.' . $setting); } + /** + * @return \ArrayObject + */ public static function createCacheConfig() { $driver = \CRM_Utils_Cache::getCacheDriver(); $settings = \CRM_Utils_Cache::getCacheSettings($driver); @@ -548,6 +547,11 @@ class Container { } } + /** + * @param string $name + * + * @return mixed + */ public static function getBootService($name) { return \Civi::$statics[__CLASS__]['boot'][$name]; } diff --git a/Civi/Install/Requirements.php b/Civi/Install/Requirements.php index d5f3d648eb..da7b2cc765 100644 --- a/Civi/Install/Requirements.php +++ b/Civi/Install/Requirements.php @@ -303,6 +303,9 @@ class Requirements { * @return array */ public function checkMysqlVersion(array $db_config) { + if (!class_exists('\CRM_Upgrade_Incremental_General')) { + require_once dirname(__FILE__) . '/../../CRM/Upgrade/Incremental/General.php'; + } $min = \CRM_Upgrade_Incremental_General::MIN_INSTALL_MYSQL_VER; $results = [ 'title' => 'CiviCRM MySQL Version', diff --git a/ang/crmMailingAB/EditCtrl/report.html b/ang/crmMailingAB/EditCtrl/report.html index 18380923c1..5e97ae964a 100644 --- a/ang/crmMailingAB/EditCtrl/report.html +++ b/ang/crmMailingAB/EditCtrl/report.html @@ -83,7 +83,7 @@ class="crm-hover-button action-item" ng-href="{{statUrl(am.mailing, statType, 'report')}}" title="{{ts('Reports for \'%1\'', {1: statType.title})}}" - crm-icon="clipboard" + crm-icon="fa-clipboard" > diff --git a/ang/crmUi.js b/ang/crmUi.js index c6ba58b146..355939c2dc 100644 --- a/ang/crmUi.js +++ b/ang/crmUi.js @@ -908,7 +908,7 @@ }) // Example for Font Awesome: - // Example for jQuery UI (deprecated): + // Example for jQuery UI (deprecated): .directive('crmIcon', function() { return { restrict: 'EA', diff --git a/api/v3/Contribution.php b/api/v3/Contribution.php index bbe0169181..298980b4a7 100644 --- a/api/v3/Contribution.php +++ b/api/v3/Contribution.php @@ -393,7 +393,7 @@ function _civicrm_api3_contribute_format_params($params, &$values) { * @throws Exception */ function civicrm_api3_contribution_sendconfirmation($params) { - $ids = $values = []; + $ids = []; $allowedParams = [ 'receipt_from_email', 'receipt_from_name', @@ -404,8 +404,7 @@ function civicrm_api3_contribution_sendconfirmation($params) { 'payment_processor_id', ]; $input = array_intersect_key($params, array_flip($allowedParams)); - $input['is_email_receipt'] = TRUE; - CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $params['id'], $values); + CRM_Contribute_BAO_Contribution::sendMail($input, $ids, $params['id']); } /** @@ -488,7 +487,8 @@ function civicrm_api3_contribution_completetransaction($params) { elseif ($contribution->contribution_status_id == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed')) { throw new API_Exception(ts('Contribution already completed'), 'contribution_completed'); } - $input['trxn_id'] = !empty($params['trxn_id']) ? $params['trxn_id'] : $contribution->trxn_id; + $input['trxn_id'] = $params['trxn_id'] ?? $contribution->trxn_id; + if (!empty($params['fee_amount'])) { $input['fee_amount'] = $params['fee_amount']; } @@ -682,7 +682,7 @@ function _ipn_process_transaction(&$params, $contribution, $input, $ids, $firstC } $input['card_type_id'] = $params['card_type_id'] ?? NULL; $input['pan_truncation'] = $params['pan_truncation'] ?? NULL; - return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, NULL, + return CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects, $params['is_post_payment_create'] ?? NULL); } diff --git a/api/v3/Order.php b/api/v3/Order.php index 99b270ec29..32d59a1939 100644 --- a/api/v3/Order.php +++ b/api/v3/Order.php @@ -74,9 +74,9 @@ function civicrm_api3_order_create($params) { civicrm_api3_verify_one_mandatory($params, NULL, ['line_items', 'total_amount']); $entity = NULL; $entityIds = []; - $contributionStatus = $params['contribution_status_id'] ?? NULL; - if ($contributionStatus !== 'Pending' && 'Pending' !== CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $contributionStatus)) { - CRM_Core_Error::deprecatedFunctionWarning("Creating a Order with a status other than pending is deprecated. Currently empty defaults to 'Completed' so as a transition not passing in 'Pending' is deprecated. You can chain payment creation e.g civicrm_api3('Order', 'create', ['blah' => 'blah', 'contribution_status_id' => 'Pending', 'api.Payment.create => ['total_amount' => 5]]"); + $params['contribution_status_id'] = $params['contribution_status_id'] ?? 'Pending'; + if ($params['contribution_status_id'] !== 'Pending' && 'Pending' !== CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $params['contribution_status_id'])) { + CRM_Core_Error::deprecatedFunctionWarning("Creating a Order with a status other than pending is deprecated. Please do not set contribution_status_id, it will default to Pending. You can chain payment creation e.g civicrm_api3('Order', 'create', ['blah' => 'blah', 'contribution_status_id' => 'Pending', 'api.Payment.create => ['total_amount' => 5]]"); } if (!empty($params['line_items']) && is_array($params['line_items'])) { @@ -91,9 +91,7 @@ function civicrm_api3_order_create($params) { if ($entityParams) { if (in_array($entity, ['participant', 'membership'])) { $entityParams['skipLineItem'] = TRUE; - if ($contributionStatus === 'Pending') { - $entityParams['status_id'] = ($entity === 'participant' ? 'Pending from incomplete transaction' : 'Pending'); - } + $entityParams['status_id'] = ($entity === 'participant' ? 'Pending from incomplete transaction' : 'Pending'); $entityResult = civicrm_api3($entity, 'create', $entityParams); $params['contribution_mode'] = $entity; $entityIds[] = $params[$entity . '_id'] = $entityResult['id']; diff --git a/css/civicrm.css b/css/civicrm.css index d260ee66ab..cf61ef5a7d 100644 --- a/css/civicrm.css +++ b/css/civicrm.css @@ -1835,8 +1835,6 @@ input.crm-form-entityref { .crm-container .crm-button input { background: none; - _background: #6C6C6C; - /* IE6 only */ border: medium none; color: #FFF; cursor: pointer; @@ -1850,8 +1848,11 @@ input.crm-form-entityref { .crm-container .crm-button-type-back { margin-left: 20px; } -.crm-container .crm-button-type-cancel input { - color: #E6E6DC!important; + +/* Reset WP backend min-height for buttons */ + +.wp-core-ui .crm-container .button { + min-height: 0; } .crm-container a.button, @@ -1862,7 +1863,7 @@ input.crm-form-entityref { .crm-container input[type=button], .crm-container .crm-button { text-shadow: 0 1px 0 black; - background: #70716B url(../i/crm-button-bg.gif) repeat-x top left; + background: #696969; color: #FFF; font-size: 13px; font-weight: normal; @@ -1873,11 +1874,19 @@ input.crm-form-entityref { border: 1px solid #3e3e3e; } +.crm-container a.button, +.crm-container a.button:link, +.crm-container a.button:visited, .crm-container span.crm-button { display: block; - float: left !important; + float: left; overflow: hidden; - padding: 1px; + line-height: 135%; +} + +/* Preserving the important but not sure why */ +.crm-container span.crm-button { + float: left !important; } .crm-container button.crm-button { @@ -1895,22 +1904,12 @@ input.crm-form-entityref { .crm-container .crm-button input[type=button], .crm-container .crm-button input.crm-form-submit { - padding: 3px 5px 2px; + padding: 0; margin: 0; background: none; - _background: #6C6C6C; - /* IE6 only */ border: none; } -.crm-container a.button, -.crm-container a.button:link, -.crm-container a.button:visited { - display: block; - float: left; - line-height: 135%; -} - .crm-container .crm-button:hover, .crm-container .crm-button:focus, .crm-container input[type=submit]:hover, @@ -1919,7 +1918,7 @@ input.crm-form-entityref { .crm-container .ui-dialog-buttonset .ui-button:focus, .crm-container a.button:hover, .crm-container a.button:focus { - background-position: 0 -25px; + background: #3e3e3e; } .crm-container .crm-button-disabled, @@ -1930,7 +1929,6 @@ input.crm-form-entityref { .crm-container .crm-button[disabled] { opacity: .6; cursor: default; - background-position: top left; } .crm-container .crm-button-disabled input[disabled] { @@ -2050,31 +2048,6 @@ input.crm-form-entityref { margin-left: 3px; } -.crm-container .crm-button.crm-icon-button { - padding: 2px 2px 1px 4px; -} - -.crm-container .crm-button.crm-icon-button input { - padding-left: 18px; -} - -.crm-container .crm-button.button-crm-i { - padding: 2px 0 1px 5px; -} - -.crm-container .crm-button.button-crm-i input { - padding-left: 0; -} - -.crm-container .crm-button-icon { - background-image: url("../i/icons/jquery-ui-FFFFFF.png"); - height: 16px; - width: 16px; - display: block; - position: absolute; - pointer-events: none; -} - .crm-container .delete-icon { background-position: -176px -96px; } @@ -2121,27 +2094,6 @@ a.crm-i:hover { color: #86c661; } -.crm-i-button { - position: relative; -} - -.crm-i-button>.crm-i { - position: absolute; - pointer-events: none; - top: .4em; - left: .4em; -} - -.crm-container .crm-button.crm-i-button input[type="button"], -.crm-container .crm-button.crm-i-button input.crm-form-submit { - padding-left: 1.6em; -} - -.crm-container .inform-icon { - background-position: -16px -144px; - margin-right: 5px; -} - .crm-container a.helpicon { opacity: .8; } @@ -3201,7 +3153,7 @@ span.crm-select-item-color { right: 0; } .crm-container .ui-dialog-titlebar.ui-widget-header { - background: url("../i/crm-button-bg.gif") repeat-x scroll left center #70716B; + background: #5D677B; color: #F5F6F1; } .crm-container .ui-dialog-title { diff --git a/ext/flexmailer/info.xml b/ext/flexmailer/info.xml index f89dbd026e..7317ae5027 100644 --- a/ext/flexmailer/info.xml +++ b/ext/flexmailer/info.xml @@ -14,16 +14,16 @@ http://civicrm.stackexchange.com/ http://www.gnu.org/licenses/agpl-3.0.html - 2019-11-26 - 1.1.1 - alpha + 2020-08-05 + 1.1.2 + stable FlexMailer is an email delivery engine which replaces the internal guts of CiviMail. It is a drop-in replacement which enables *other* extensions to provide richer email features. - 5.13 + 5.29 diff --git a/i/crm-button-bg.gif b/i/crm-button-bg.gif deleted file mode 100644 index 6aa345ebd2..0000000000 Binary files a/i/crm-button-bg.gif and /dev/null differ diff --git a/js/Common.js b/js/Common.js index 6f41eab124..88acc9ef4d 100644 --- a/js/Common.js +++ b/js/Common.js @@ -883,7 +883,7 @@ if (!CRM.vars) CRM.vars = {}; var that = this; validator.settings = $.extend({}, validator.settings, CRM.validate._defaults, CRM.validate.params); // Call our custom validation handler. - $(validator.currentForm).on("invalid-form.validate", validator.settings.invalidHandler ); + $(validator.currentForm).on("invalid-form.validate", validator.settings.invalidHandler); // Call any post-initialization callbacks if (CRM.validate.functions && CRM.validate.functions.length) { $.each(CRM.validate.functions, function(i, func) { diff --git a/js/crm.ajax.js b/js/crm.ajax.js index 9a3c3f1ce5..e65bd7b2a2 100644 --- a/js/crm.ajax.js +++ b/js/crm.ajax.js @@ -517,7 +517,7 @@ added.push(identifier); } // display:none causes the form to not submit when pressing "enter" - $el.parents(buttonContainers).css({height: 0, padding: 0, margin: 0, overflow: 'hidden'}).find('.crm-button-icon').hide(); + $el.parents(buttonContainers).css({height: 0, padding: 0, margin: 0, overflow: 'hidden'}); }); $el.dialog('option', 'buttons', buttons); } diff --git a/release-notes.md b/release-notes.md index 8e17ff63b7..a7c7f0ef35 100644 --- a/release-notes.md +++ b/release-notes.md @@ -26,6 +26,15 @@ Released August 5, 2020 - **[Credits](release-notes/5.28.0.md#credits)** - **[Feedback](release-notes/5.28.0.md#feedback)** +## CiviCRM 5.27.4 + +Released August 3, 2020 + +- **[Synopsis](release-notes/5.27.4.md#synopsis)** +- **[Bugs resolved](release-notes/5.27.4.md#bugs)** +- **[Credits](release-notes/5.27.4.md#credits)** +- **[Feedback](release-notes/5.27.4.md#feedback)** + ## CiviCRM 5.27.3 Released July 23, 2020 diff --git a/release-notes/5.27.4.md b/release-notes/5.27.4.md new file mode 100644 index 0000000000..feaaaa0e4e --- /dev/null +++ b/release-notes/5.27.4.md @@ -0,0 +1,56 @@ +# CiviCRM 5.27.4 + +Released August 3, 2020 + +- **[Synopsis](#synopsis)** +- **[Bugs resolved](#bugs)** +- **[Credits](#credits)** +- **[Feedback](#feedback)** + +## Synopsis + +| *Does this version...?* | | +|:--------------------------------------------------------------- |:-------:| +| Fix security vulnerabilities? | no | +| Change the database schema? | no | +| Alter the API? | no | +| **Require attention to configuration options?** | **yes** | +| **Fix problems installing or upgrading to a previous version?** | **yes** | +| Introduce features? | no | +| **Fix bugs?** | **yes** | + +## Bugs resolved + +* **_CiviCase_: Action links are missing text labels ([dev/core#1899](https://lab.civicrm.org/dev/core/-/issues/1899): + [#17947](https://github.com/civicrm/civicrm-core/pull/17947))** +* **_CiviContribute_: When editing "Amounts", default is mis-saved ([dev/core#1911](https://lab.civicrm.org/dev/core/-/issues/1911): + [#17960](https://github.com/civicrm/civicrm-core/pull/17960))** + + If you recently edited any "Amounts", then please review and verify that the default is correct. + Mis-saved defaults can cause problems when submitting contributions. + +* **_CiviMail_: Blasts sent via Job Manager do not recover from errors ([dev/mail#72](https://lab.civicrm.org/dev/mail/-/issues/72): + [#18017](https://github.com/civicrm/civicrm-core/pull/18017))** +* **_WordPress_: `[civicrm.files]` is incorrect on older sites ([dev/wordpress#66](https://lab.civicrm.org/dev/wordpress/-/issues/66): + [#17868](https://github.com/civicrm/civicrm-core/pull/17868))** + + This fixes a regression affecting older WordPress deployments by partially reverting + [dev/wordpress#47](https://lab.civicrm.org/dev/wordpress/-/issues/47). This means that some WordPress configurations + (eg Polylang) may return to being unsupported. The issue is scheduled to be revisited circa 5.29. + +## Credits + + + +This release was developed by the following authors and reviewers: + +Wikimedia Foundation - Eileen McNaughton; Team Expansion - Greg Harris; Tadpole Collective - Kevin +Cristiano; Megaphone Technology Consulting - Jon Goldberg; MJW Consulting - Matthew Wire; JMA +Consulting - Seamus Lee; Dave D; CiviCRM - Tim Otten; Christian Wach; Agileware - Justin Freeman; +AGH Strategies - Andrew Hunt + +## Feedback + +These release notes are edited by Tim Otten and Andrew Hunt. If you'd like to +provide feedback on them, please login to https://chat.civicrm.org/civicrm and +contact `@agh1`. diff --git a/sql/civicrm_generated.mysql b/sql/civicrm_generated.mysql index 3644a8387a..b6dba3bf1a 100644 --- a/sql/civicrm_generated.mysql +++ b/sql/civicrm_generated.mysql @@ -725,7 +725,7 @@ UNLOCK TABLES; LOCK TABLES `civicrm_mailing_component` WRITE; /*!40000 ALTER TABLE `civicrm_mailing_component` DISABLE KEYS */; -INSERT INTO `civicrm_mailing_component` (`id`, `name`, `component_type`, `subject`, `body_html`, `body_text`, `is_default`, `is_active`) VALUES (1,'Mailing Header','Header','Descriptive Title for this Header','Sample Header for HTML formatted content.','Sample Header for TEXT formatted content.',1,1),(2,'Mailing Footer','Footer','Descriptive Title for this Footer.','Sample Footer for HTML formatted content
Unsubscribe
{domain.address}','to unsubscribe: {action.optOutUrl}\n{domain.address}',1,1),(3,'Subscribe Message','Subscribe','Subscription Confirmation Request','You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click here.','You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click on this link: {subscribe.url}',1,1),(4,'Welcome Message','Welcome','Your Subscription has been Activated','Welcome. Your subscription to the {welcome.group} mailing list has been activated.','Welcome. Your subscription to the {welcome.group} mailing list has been activated.',1,1),(5,'Unsubscribe Message','Unsubscribe','Un-subscribe Confirmation','You have been un-subscribed from the following groups: {unsubscribe.group}. You can re-subscribe by mailing {action.resubscribe} or clicking here.','You have been un-subscribed from the following groups: {unsubscribe.group}. You can re-subscribe by mailing {action.resubscribe} or clicking ',1,1),(6,'Resubscribe Message','Resubscribe','Re-subscribe Confirmation','You have been re-subscribed to the following groups: {resubscribe.group}. You can un-subscribe by mailing {action.unsubscribe} or clicking here.','You have been re-subscribed to the following groups: {resubscribe.group}. You can un-subscribe by mailing {action.unsubscribe} or clicking {action.unsubscribeUrl}',1,1),(7,'Opt-out Message','OptOut','Opt-out Confirmation','Your email address has been removed from {domain.name} mailing lists.','Your email address has been removed from {domain.name} mailing lists.',1,1),(8,'Auto-responder','Reply','Please Send Inquiries to Our Contact Email Address','This is an automated reply from an un-attended mailbox. Please send any inquiries to the contact email address listed on our web-site.','This is an automated reply from an un-attended mailbox. Please send any inquiries to the contact email address listed on our web-site.',1,1); +INSERT INTO `civicrm_mailing_component` (`id`, `name`, `component_type`, `subject`, `body_html`, `body_text`, `is_default`, `is_active`) VALUES (1,'Mailing Header','Header','Descriptive Title for this Header','Sample Header for HTML formatted content.','Sample Header for TEXT formatted content.',1,1),(2,'Mailing Footer','Footer','Descriptive Title for this Footer.','Sample Footer for HTML formatted content
Unsubscribe
{domain.address}','to unsubscribe: {action.optOutUrl}\n{domain.address}',1,1),(3,'Subscribe Message','Subscribe','Subscription Confirmation Request','You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click here.','You have a pending subscription to the {subscribe.group} mailing list. To confirm this subscription, reply to this email or click on this link: {subscribe.url}',1,1),(4,'Welcome Message','Welcome','Your Subscription has been Activated','Welcome. Your subscription to the {welcome.group} mailing list has been activated.','Welcome. Your subscription to the {welcome.group} mailing list has been activated.',1,1),(5,'Unsubscribe Message','Unsubscribe','Un-subscribe Confirmation','You have been un-subscribed from the following groups: {unsubscribe.group}. You can re-subscribe by mailing {action.resubscribe} or clicking here.','You have been un-subscribed from the following groups: {unsubscribe.group}. You can re-subscribe by mailing {action.resubscribe} or clicking {action.resubscribeUrl}',1,1),(6,'Resubscribe Message','Resubscribe','Re-subscribe Confirmation','You have been re-subscribed to the following groups: {resubscribe.group}. You can un-subscribe by mailing {action.unsubscribe} or clicking here.','You have been re-subscribed to the following groups: {resubscribe.group}. You can un-subscribe by mailing {action.unsubscribe} or clicking {action.unsubscribeUrl}',1,1),(7,'Opt-out Message','OptOut','Opt-out Confirmation','Your email address has been removed from {domain.name} mailing lists.','Your email address has been removed from {domain.name} mailing lists.',1,1),(8,'Auto-responder','Reply','Please Send Inquiries to Our Contact Email Address','This is an automated reply from an un-attended mailbox. Please send any inquiries to the contact email address listed on our web-site.','This is an automated reply from an un-attended mailbox. Please send any inquiries to the contact email address listed on our web-site.',1,1); /*!40000 ALTER TABLE `civicrm_mailing_component` ENABLE KEYS */; UNLOCK TABLES; diff --git a/templates/CRM/ACL/Form/ACL.tpl b/templates/CRM/ACL/Form/ACL.tpl index fbecd9fce8..5c8bf2cc6d 100644 --- a/templates/CRM/ACL/Form/ACL.tpl +++ b/templates/CRM/ACL/Form/ACL.tpl @@ -11,7 +11,7 @@
{if $action eq 8}
-
  + {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Delete will remove this permission from the specified ACL Role.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/ACL/Form/ACLBasic.tpl b/templates/CRM/ACL/Form/ACLBasic.tpl index f905c4d583..764ebe85bd 100644 --- a/templates/CRM/ACL/Form/ACLBasic.tpl +++ b/templates/CRM/ACL/Form/ACLBasic.tpl @@ -14,7 +14,7 @@ {if $action eq 8}
-
+
{icon icon="fa-info-circle"}{/icon}
{ts}WARNING: Delete will remove this permission from the specified ACL Role.{/ts} {ts}Do you want to continue?{/ts}
diff --git a/templates/CRM/ACL/Form/EntityRole.tpl b/templates/CRM/ACL/Form/EntityRole.tpl index 75bf26e6a2..a2a8799c98 100644 --- a/templates/CRM/ACL/Form/EntityRole.tpl +++ b/templates/CRM/ACL/Form/EntityRole.tpl @@ -13,7 +13,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this option will remove this ACL Role Assignment.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Activity/Form/Search/EmptyResults.tpl b/templates/CRM/Activity/Form/Search/EmptyResults.tpl index 19eaaefeb2..1a4c04c173 100644 --- a/templates/CRM/Activity/Form/Search/EmptyResults.tpl +++ b/templates/CRM/Activity/Form/Search/EmptyResults.tpl @@ -9,7 +9,7 @@ *} {* No matches for submitted search request. *}
-
  + {icon icon="fa-info-circle"}{/icon} {if $qill}{ts}No matches found for:{/ts} {include file="CRM/common/displaySearchCriteria.tpl"} {else} diff --git a/templates/CRM/Activity/Form/Task/Delete.tpl b/templates/CRM/Activity/Form/Task/Delete.tpl index fa8c71b915..d467db4b42 100644 --- a/templates/CRM/Activity/Form/Task/Delete.tpl +++ b/templates/CRM/Activity/Form/Task/Delete.tpl @@ -11,7 +11,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
-
+ {icon icon="fa-info-circle"}{/icon}

{ts}Are you sure you want to delete the selected Activities?{/ts}

{include file="CRM/Activity/Form/Task.tpl"}

diff --git a/templates/CRM/Activity/Form/Task/Print.tpl b/templates/CRM/Activity/Form/Task/Print.tpl index 7fd288217c..d71d4408c4 100644 --- a/templates/CRM/Activity/Form/Task/Print.tpl +++ b/templates/CRM/Activity/Form/Task/Print.tpl @@ -67,7 +67,7 @@ {include file="CRM/common/formButtons.tpl" location="bottom"} {else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}There are no records selected for Print.{/ts}
{/if} diff --git a/templates/CRM/Activity/Form/Task/SearchTaskHookSample.tpl b/templates/CRM/Activity/Form/Task/SearchTaskHookSample.tpl index 8456ac24b4..4f1d28038a 100644 --- a/templates/CRM/Activity/Form/Task/SearchTaskHookSample.tpl +++ b/templates/CRM/Activity/Form/Task/SearchTaskHookSample.tpl @@ -37,7 +37,7 @@ {else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}There are no records selected.{/ts}
{/if} diff --git a/templates/CRM/Activity/Page/UserDashboard.tpl b/templates/CRM/Activity/Page/UserDashboard.tpl index 8fb1f85f2b..14318c6eb7 100644 --- a/templates/CRM/Activity/Page/UserDashboard.tpl +++ b/templates/CRM/Activity/Page/UserDashboard.tpl @@ -47,7 +47,7 @@ q="action=view&reset=1&id=`$row.activity_id`&cid=`$row.contact_id`&context=dashb {/strip} {else}
-
  + {icon icon="fa-info-circle"}{/icon} {ts}There are no scheduled activities assigned to you.{/ts}
diff --git a/templates/CRM/Admin/Form/ContactType.tpl b/templates/CRM/Admin/Form/ContactType.tpl index ed5b54c53c..90c1962d33 100644 --- a/templates/CRM/Admin/Form/ContactType.tpl +++ b/templates/CRM/Admin/Form/ContactType.tpl @@ -12,7 +12,7 @@
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: {ts}This action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}{/ts}
{else} diff --git a/templates/CRM/Admin/Form/Extensions.tpl b/templates/CRM/Admin/Form/Extensions.tpl index bf114f4f69..6b9345faa6 100644 --- a/templates/CRM/Admin/Form/Extensions.tpl +++ b/templates/CRM/Admin/Form/Extensions.tpl @@ -12,20 +12,20 @@
{if $action eq 8}
-
- {ts}WARNING: Uninstalling this extension might result in the loss of all records which use the extension.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone. Please review the extension information below before you make final decision.{/ts} {ts}Do you want to continue?{/ts} + {icon icon="fa-info-circle"}{/icon} + {ts}WARNING: Uninstalling this extension might result in the loss of all records which use the extension.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone. Please review the extension information below before you make final decision.{/ts} {ts}Do you want to continue?{/ts}
{/if} {if $action eq 1} -
-
- {ts}Installing this extension will provide you with new functionality. Please make sure that the extension you're installing comes from a trusted source.{/ts} {ts}Do you want to continue?{/ts} +
+ {icon icon="fa-info-circle"}{/icon} + {ts}Installing this extension will provide you with new functionality. Please make sure that the extension you're installing comes from a trusted source.{/ts} {ts}Do you want to continue?{/ts}
{/if} {if $action eq 2} -
-
- {ts}Downloading this extension will provide you with new functionality. Please make sure that the extension you're installing or upgrading comes from a trusted source.{/ts} {ts}Do you want to continue?{/ts} +
+ {icon icon="fa-info-circle"}{/icon} + {ts}Downloading this extension will provide you with new functionality. Please make sure that the extension you're installing or upgrading comes from a trusted source.{/ts} {ts}Do you want to continue?{/ts}
{/if} {if $action eq 8 or $action eq 1 or $action eq 2} diff --git a/templates/CRM/Admin/Form/Job.tpl b/templates/CRM/Admin/Form/Job.tpl index 0fb95d4655..a4b78dda1d 100644 --- a/templates/CRM/Admin/Form/Job.tpl +++ b/templates/CRM/Admin/Form/Job.tpl @@ -14,12 +14,12 @@ {if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this Scheduled Job will cause some important site functionality to stop working.{/ts} {ts}Do you want to continue?{/ts}
{elseif $action eq 4}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$jobName}Are you sure you would like to execute %1 job?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/LabelFormats.tpl b/templates/CRM/Admin/Form/LabelFormats.tpl index c370eabac6..d5bcc2a96b 100644 --- a/templates/CRM/Admin/Form/LabelFormats.tpl +++ b/templates/CRM/Admin/Form/LabelFormats.tpl @@ -28,12 +28,12 @@
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$formatName}WARNING: You are about to delete the Label Format titled %1.{/ts} {ts}Do you want to continue?{/ts}
{elseif $action eq 16384}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$formatName}Are you sure you would like to make a copy of the Label Format titled %1?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/LocationType.tpl b/templates/CRM/Admin/Form/LocationType.tpl index 9e11f77665..2689513080 100644 --- a/templates/CRM/Admin/Form/LocationType.tpl +++ b/templates/CRM/Admin/Form/LocationType.tpl @@ -12,7 +12,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this option will result in the loss of all location type records which use the option.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/MailSettings.tpl b/templates/CRM/Admin/Form/MailSettings.tpl index ee8987a22b..361ffe09e1 100644 --- a/templates/CRM/Admin/Form/MailSettings.tpl +++ b/templates/CRM/Admin/Form/MailSettings.tpl @@ -12,7 +12,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this option will result in the loss of mail settings data.{/ts} {ts}Do you want to continue?{/ts}
{include file="CRM/common/formButtons.tpl" location="top"}
diff --git a/templates/CRM/Admin/Form/Mapping.tpl b/templates/CRM/Admin/Form/Mapping.tpl index af99fa96a2..b943bfd881 100644 --- a/templates/CRM/Admin/Form/Mapping.tpl +++ b/templates/CRM/Admin/Form/Mapping.tpl @@ -27,7 +27,7 @@ {else}
-
  + {icon icon="fa-info-circle"}{/icon} {ts 1=$mappingName}WARNING: Are you sure you want to delete mapping '%1'?{/ts} {ts}This action cannot be undone.{/ts}

diff --git a/templates/CRM/Admin/Form/MessageTemplates.tpl b/templates/CRM/Admin/Form/MessageTemplates.tpl index 28430b69e2..537e567516 100644 --- a/templates/CRM/Admin/Form/MessageTemplates.tpl +++ b/templates/CRM/Admin/Form/MessageTemplates.tpl @@ -22,7 +22,7 @@
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$msg_title|escape}Do you want to delete the message template '%1'?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/OptionGroup.tpl b/templates/CRM/Admin/Form/OptionGroup.tpl index 60715a5e95..13f6ddee5b 100644 --- a/templates/CRM/Admin/Form/OptionGroup.tpl +++ b/templates/CRM/Admin/Form/OptionGroup.tpl @@ -12,7 +12,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this option gruop will result in the loss of all records which use the option.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/Options.tpl b/templates/CRM/Admin/Form/Options.tpl index 2a566c9b65..fa435bd63e 100644 --- a/templates/CRM/Admin/Form/Options.tpl +++ b/templates/CRM/Admin/Form/Options.tpl @@ -12,7 +12,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$gLabel}WARNING: Deleting this option will result in the loss of all %1 related records which use the option.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/ParticipantStatusType.tpl b/templates/CRM/Admin/Form/ParticipantStatusType.tpl index a6c81f9b0d..6b159db7b2 100644 --- a/templates/CRM/Admin/Form/ParticipantStatusType.tpl +++ b/templates/CRM/Admin/Form/ParticipantStatusType.tpl @@ -15,7 +15,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this Participant Status will remove all of its settings.{/ts} {ts}Do you want to continue?{/ts}
{include file="CRM/common/formButtons.tpl"} diff --git a/templates/CRM/Admin/Form/PaymentProcessor.tpl b/templates/CRM/Admin/Form/PaymentProcessor.tpl index 016fd7d846..67464d00e9 100644 --- a/templates/CRM/Admin/Form/PaymentProcessor.tpl +++ b/templates/CRM/Admin/Form/PaymentProcessor.tpl @@ -14,7 +14,7 @@ {if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {$deleteMessage|escape}
{else} diff --git a/templates/CRM/Admin/Form/PaymentProcessorType.tpl b/templates/CRM/Admin/Form/PaymentProcessorType.tpl index ccb4a69705..3807b2e9b2 100644 --- a/templates/CRM/Admin/Form/PaymentProcessorType.tpl +++ b/templates/CRM/Admin/Form/PaymentProcessorType.tpl @@ -13,7 +13,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Admin/Form/PdfFormats.tpl b/templates/CRM/Admin/Form/PdfFormats.tpl index 623c025254..7b6386d5d1 100644 --- a/templates/CRM/Admin/Form/PdfFormats.tpl +++ b/templates/CRM/Admin/Form/PdfFormats.tpl @@ -30,7 +30,7 @@ {if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$formatName}WARNING: You are about to delete the PDF Page Format titled %1.{/ts}

{ts}This will remove the format from all Message Templates that use it. Do you want to continue?{/ts}

{else} diff --git a/templates/CRM/Admin/Form/Preferences/Display.tpl b/templates/CRM/Admin/Form/Preferences/Display.tpl index 049a5ebf99..44ff0ba7aa 100644 --- a/templates/CRM/Admin/Form/Preferences/Display.tpl +++ b/templates/CRM/Admin/Form/Preferences/Display.tpl @@ -161,8 +161,8 @@ {$form.editor_id.html}   - - + + {$form.ckeditor_config.html} diff --git a/templates/CRM/Admin/Form/ScheduleReminders.tpl b/templates/CRM/Admin/Form/ScheduleReminders.tpl index 0807cfffd2..b8bb250a0f 100644 --- a/templates/CRM/Admin/Form/ScheduleReminders.tpl +++ b/templates/CRM/Admin/Form/ScheduleReminders.tpl @@ -13,7 +13,7 @@ {if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1=$reminderName}WARNING: You are about to delete the Reminder titled %1.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Admin/Page/APIExplorer.tpl b/templates/CRM/Admin/Page/APIExplorer.tpl index 1e1b8eb497..9c127fcb6f 100644 --- a/templates/CRM/Admin/Page/APIExplorer.tpl +++ b/templates/CRM/Admin/Page/APIExplorer.tpl @@ -285,8 +285,8 @@
- - + +
diff --git a/templates/CRM/Admin/Page/CKEditorConfig.tpl b/templates/CRM/Admin/Page/CKEditorConfig.tpl index 14af566be2..ad179df5ab 100644 --- a/templates/CRM/Admin/Page/CKEditorConfig.tpl +++ b/templates/CRM/Admin/Page/CKEditorConfig.tpl @@ -99,10 +99,10 @@
- + - +
diff --git a/templates/CRM/Admin/Page/EventTemplate.tpl b/templates/CRM/Admin/Page/EventTemplate.tpl index 72d50d871f..b7a091b847 100644 --- a/templates/CRM/Admin/Page/EventTemplate.tpl +++ b/templates/CRM/Admin/Page/EventTemplate.tpl @@ -56,7 +56,7 @@ {else}
-
+ {icon icon="fa-info-circle"}{/icon} {capture assign=crmURL}{crmURL p='civicrm/event/add' q="action=add&is_template=1&reset=1"}{/capture} {ts 1=$crmURL}There are no Event Templates present. You can add one.{/ts}
diff --git a/templates/CRM/Admin/Page/Extensions/AddNew.tpl b/templates/CRM/Admin/Page/Extensions/AddNew.tpl index 3e025ac6a3..04df4239eb 100644 --- a/templates/CRM/Admin/Page/Extensions/AddNew.tpl +++ b/templates/CRM/Admin/Page/Extensions/AddNew.tpl @@ -41,7 +41,7 @@ Depends: CRM/common/enableDisableApi.tpl and CRM/common/jsortable.tpl
{else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}There are no extensions to display. Please click "Refresh" to update information about available extensions.{/ts}
{/if} diff --git a/templates/CRM/Admin/Page/Extensions/AddNewReq.tpl b/templates/CRM/Admin/Page/Extensions/AddNewReq.tpl index 1afe59b4f2..c400f940b5 100644 --- a/templates/CRM/Admin/Page/Extensions/AddNewReq.tpl +++ b/templates/CRM/Admin/Page/Extensions/AddNewReq.tpl @@ -1,7 +1,7 @@
{foreach from=$extAddNewReqs item=req}
-
+ {icon icon="fa-info-circle"}{/icon} {$req.title}
{$req.message}
diff --git a/templates/CRM/Admin/Page/Extensions/Main.tpl b/templates/CRM/Admin/Page/Extensions/Main.tpl index e23977a984..88875c8573 100644 --- a/templates/CRM/Admin/Page/Extensions/Main.tpl +++ b/templates/CRM/Admin/Page/Extensions/Main.tpl @@ -45,7 +45,7 @@ Depends: CRM/common/enableDisableApi.tpl and CRM/common/jsortable.tpl
{else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts 1="https://civicrm.org/extensions"}There are no extensions to display. Click the "Add New" tab to browse and install extensions posted on the public CiviCRM Extensions Directory. If you have downloaded extensions manually and don't see them here, try clicking the "Refresh" button.{/ts}
{/if} diff --git a/templates/CRM/Admin/Page/Job.tpl b/templates/CRM/Admin/Page/Job.tpl index cb2d310b7c..2738a86b94 100644 --- a/templates/CRM/Admin/Page/Job.tpl +++ b/templates/CRM/Admin/Page/Job.tpl @@ -63,7 +63,7 @@
{elseif $action ne 1}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}There are no jobs configured.{/ts}
{elseif $action ne 1}
-
  + {icon icon="fa-info-circle"}{/icon} {if $jobId} {ts}This scheduled job does not have any log entries.{/ts} {else} diff --git a/templates/CRM/Admin/Page/LabelFormats.tpl b/templates/CRM/Admin/Page/LabelFormats.tpl index d2dd488eea..e2ddf676bd 100644 --- a/templates/CRM/Admin/Page/LabelFormats.tpl +++ b/templates/CRM/Admin/Page/LabelFormats.tpl @@ -68,7 +68,7 @@
{else}
-
+ {icon icon="fa-info-circle"}{/icon} {capture assign=crmURL}{crmURL p='civicrm/admin/labelFormats' q="action=add&reset=1"}{/capture} {ts 1=$crmURL}There are no Label Formats configured. You canadd one.{/ts}
diff --git a/templates/CRM/Admin/Page/MessageTemplates.tpl b/templates/CRM/Admin/Page/MessageTemplates.tpl index 2d704fdd96..b60355dede 100644 --- a/templates/CRM/Admin/Page/MessageTemplates.tpl +++ b/templates/CRM/Admin/Page/MessageTemplates.tpl @@ -147,7 +147,7 @@ {if empty( $template_row) }
-
  + {icon icon="fa-info-circle"}{/icon} {ts 1=$crmURL}There are no User-driven Message Templates entered. You can add one.{/ts}
{/if} diff --git a/templates/CRM/Admin/Page/PaymentProcessor.tpl b/templates/CRM/Admin/Page/PaymentProcessor.tpl index ba6d55da20..2323dffa9c 100644 --- a/templates/CRM/Admin/Page/PaymentProcessor.tpl +++ b/templates/CRM/Admin/Page/PaymentProcessor.tpl @@ -58,7 +58,7 @@
{elseif $action ne 1}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}There are no Payment Processors entered.{/ts}
{else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}None found.{/ts}
{/if} diff --git a/templates/CRM/Admin/Page/ScheduleReminders.tpl b/templates/CRM/Admin/Page/ScheduleReminders.tpl index b5c9506544..0e004c2c88 100644 --- a/templates/CRM/Admin/Page/ScheduleReminders.tpl +++ b/templates/CRM/Admin/Page/ScheduleReminders.tpl @@ -29,7 +29,7 @@
{else}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}None found.{/ts}
{/if} diff --git a/templates/CRM/Badge/Form/Layout.tpl b/templates/CRM/Badge/Form/Layout.tpl index 3aeda9184f..00805e2e23 100644 --- a/templates/CRM/Badge/Form/Layout.tpl +++ b/templates/CRM/Badge/Form/Layout.tpl @@ -13,7 +13,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Batch/Form/Batch.tpl b/templates/CRM/Batch/Form/Batch.tpl index 56e8d5bd9d..df8b072089 100644 --- a/templates/CRM/Batch/Form/Batch.tpl +++ b/templates/CRM/Batch/Form/Batch.tpl @@ -19,7 +19,7 @@
{include file="CRM/common/formButtons.tpl" location="top"}
{if $action eq 8}
-
+ {icon icon="fa-info-circle"}{/icon} {ts}WARNING: Deleting this batch will result in the loss of all data entered for the batch.{/ts} {ts}This may mean the loss of a substantial amount of data, and the action cannot be undone.{/ts} {ts}Do you want to continue?{/ts}
{else} diff --git a/templates/CRM/Campaign/Form/Campaign.tpl b/templates/CRM/Campaign/Form/Campaign.tpl index ba17c99e82..a56b9df3b7 100644 --- a/templates/CRM/Campaign/Form/Campaign.tpl +++ b/templates/CRM/Campaign/Form/Campaign.tpl @@ -14,7 +14,7 @@
-
 {ts}Are you sure you want to delete this Campaign?{/ts}
+
{icon icon="fa-info-circle"}{/icon}{ts}Are you sure you want to delete this Campaign?{/ts}
diff --git a/templates/CRM/Campaign/Form/Gotv.tpl b/templates/CRM/Campaign/Form/Gotv.tpl index daa157e60d..c11c028724 100644 --- a/templates/CRM/Campaign/Form/Gotv.tpl +++ b/templates/CRM/Campaign/Form/Gotv.tpl @@ -10,7 +10,7 @@ {if $errorMessages}
-
+ {icon icon="fa-info-circle"}{/icon}
    {foreach from=$errorMessages item=errorMsg}
  • {ts}{$errorMsg}{/ts}
  • diff --git a/templates/CRM/Campaign/Form/Petition.tpl b/templates/CRM/Campaign/Form/Petition.tpl index 2220c9d1f9..8b60e03238 100644 --- a/templates/CRM/Campaign/Form/Petition.tpl +++ b/templates/CRM/Campaign/Form/Petition.tpl @@ -15,7 +15,7 @@
    -
    + {icon icon="fa-info-circle"}{/icon}  {ts}Are you sure you want to delete this Petition?{/ts}
    diff --git a/templates/CRM/Campaign/Form/Search/Campaign.tpl b/templates/CRM/Campaign/Form/Search/Campaign.tpl index ec68d2770f..f9fc2a79d4 100644 --- a/templates/CRM/Campaign/Form/Search/Campaign.tpl +++ b/templates/CRM/Campaign/Form/Search/Campaign.tpl @@ -10,7 +10,7 @@ {if !$hasCampaigns}
    -
    + {icon icon="fa-info-circle"}{/icon}   {ts}None found.{/ts}
    diff --git a/templates/CRM/Campaign/Form/Search/EmptyResults.tpl b/templates/CRM/Campaign/Form/Search/EmptyResults.tpl index 0b2ef45f21..bfeb44999d 100644 --- a/templates/CRM/Campaign/Form/Search/EmptyResults.tpl +++ b/templates/CRM/Campaign/Form/Search/EmptyResults.tpl @@ -9,7 +9,7 @@ *} {* No matches for submitted search request. *}
    -
    + {icon icon="fa-info-circle"}{/icon} {if $qill}{ts}No matches found for:{/ts} {include file="CRM/common/displaySearchCriteria.tpl"} {else} diff --git a/templates/CRM/Campaign/Form/Search/Petition.tpl b/templates/CRM/Campaign/Form/Search/Petition.tpl index 3541466891..afb84dde62 100644 --- a/templates/CRM/Campaign/Form/Search/Petition.tpl +++ b/templates/CRM/Campaign/Form/Search/Petition.tpl @@ -10,7 +10,7 @@ {if !$hasPetitions}
    -
      + {icon icon="fa-info-circle"}{/icon} {ts}None found.{/ts}
    diff --git a/templates/CRM/Campaign/Form/Search/Survey.tpl b/templates/CRM/Campaign/Form/Search/Survey.tpl index 09a53a4ece..324f403a04 100644 --- a/templates/CRM/Campaign/Form/Search/Survey.tpl +++ b/templates/CRM/Campaign/Form/Search/Survey.tpl @@ -10,7 +10,7 @@ {if !$hasSurveys}
    -
      + {icon icon="fa-info-circle"}{/icon} {ts}None found.{/ts}
    diff --git a/templates/CRM/Campaign/Form/Survey/Delete.tpl b/templates/CRM/Campaign/Form/Survey/Delete.tpl index 1510b7bb4d..9c879e4098 100644 --- a/templates/CRM/Campaign/Form/Survey/Delete.tpl +++ b/templates/CRM/Campaign/Form/Survey/Delete.tpl @@ -11,7 +11,7 @@
    -
     {ts 1=$surveyTitle}Are you sure you want to delete the %1 survey?{/ts}
    +
    {icon icon="fa-info-circle"}{/icon}{ts 1=$surveyTitle}Are you sure you want to delete the %1 survey?{/ts}
    diff --git a/templates/CRM/Campaign/Form/Task/Interview.tpl b/templates/CRM/Campaign/Form/Task/Interview.tpl index 9c927d7b4c..2987636b71 100644 --- a/templates/CRM/Campaign/Form/Task/Interview.tpl +++ b/templates/CRM/Campaign/Form/Task/Interview.tpl @@ -9,7 +9,7 @@ *} {if $votingTab and $errorMessages}
    -
    + {icon icon="fa-info-circle"}{/icon}
      {foreach from=$errorMessages item=errorMsg}
    • {ts}{$errorMsg}{/ts}
    • diff --git a/templates/CRM/Campaign/Form/Task/Print.tpl b/templates/CRM/Campaign/Form/Task/Print.tpl index fd17756a31..9c7023da1e 100644 --- a/templates/CRM/Campaign/Form/Task/Print.tpl +++ b/templates/CRM/Campaign/Form/Task/Print.tpl @@ -21,7 +21,7 @@ {else}
      -
      + {icon icon="fa-info-circle"}{/icon} {ts}There are no records selected for Print.{/ts}
      {/if} diff --git a/templates/CRM/Campaign/Form/Task/Release.tpl b/templates/CRM/Campaign/Form/Task/Release.tpl index cd99249430..618af12e3f 100644 --- a/templates/CRM/Campaign/Form/Task/Release.tpl +++ b/templates/CRM/Campaign/Form/Task/Release.tpl @@ -16,7 +16,7 @@
      -
       {ts 1=$surveyTitle}Do you want to release respondents for '%1' ?{/ts} + {icon icon="fa-info-circle"}{/icon}{ts 1=$surveyTitle}Do you want to release respondents for '%1' ?{/ts}
      diff --git a/templates/CRM/Campaign/Form/Task/Reserve.tpl b/templates/CRM/Campaign/Form/Task/Reserve.tpl index a4ccaf412e..38fe35ac7e 100644 --- a/templates/CRM/Campaign/Form/Task/Reserve.tpl +++ b/templates/CRM/Campaign/Form/Task/Reserve.tpl @@ -16,7 +16,7 @@
      -
       {ts 1=$surveyTitle}Do you want to reserve respondents for '%1' ?{/ts} + {icon icon="fa-info-circle"}{/icon}{ts 1=$surveyTitle}Do you want to reserve respondents for '%1' ?{/ts}
      diff --git a/templates/CRM/Campaign/Page/Petition.tpl b/templates/CRM/Campaign/Page/Petition.tpl index 595c8582e7..160ae236ed 100644 --- a/templates/CRM/Campaign/Page/Petition.tpl +++ b/templates/CRM/Campaign/Page/Petition.tpl @@ -50,7 +50,7 @@ {else}
      -
       {ts}None found.{/ts} + {icon icon="fa-info-circle"}{/icon}{ts}None found.{/ts}
      {/if}