}
/**
- * Checks if user has permissions to edit inbound e-mails, either bsic info
+ * Checks if user has permissions to edit inbound e-mails, either basic info
* or both basic information and content.
*
* @return bool
public function addAttachment(&$attachments, $contacts) {
// Check preferences setting
if (Civi::settings()->get('activity_assignee_notification_ics')) {
- $config = &CRM_Core_Config::singleton();
- $this->icsfile = tempnam($config->customFileUploadDir, 'ics');
+ $this->icsfile = tempnam(CRM_Core_Config::singleton()->customFileUploadDir, 'ics');
if ($this->icsfile !== FALSE) {
rename($this->icsfile, $this->icsfile . '.ics');
$this->icsfile .= '.ics';
$url = 'civicrm/contact/view/activity';
$qsView = "atype={$activityTypeId}&action=view&reset=1&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}";
- if (CRM_Core_Permission::check('edit inbound email basic information')
- || CRM_Core_Permission::check('edit inbound email basic information and content')
- ) {
+ if (CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()) {
$showDelete = $showUpdate = TRUE;
$qsUpdate = "atype={$activityTypeId}&action=update&reset=1&id=%%id%%&cid=%%cid%%&context=%%cxt%%{$extraParams}";
}
if (empty($params['id'])) {
// Set name on created but don't update on update as the machine name is not exposed.
- $params['name_b_a'] = CRM_Utils_String::munge($params['label_b_a']);
- $params['name_a_b'] = CRM_Utils_String::munge($params['label_a_b']);
+ $params['name_b_a'] = $params['label_b_a'];
+ $params['name_a_b'] = $params['label_a_b'];
}
$result = civicrm_api3('RelationshipType', 'create', $params);
while ($dao->fetch()) {
$totals[$dao->batch_id] = (array) $dao;
}
- $dao->free();
}
return $totals;
}
*/
class CRM_Campaign_Form_Search_Campaign extends CRM_Core_Form {
+ /**
+ * Explicitly declare the entity api name.
+ *
+ * @return string
+ */
+ public function getDefaultEntity() {
+ return 'Campaign';
+ }
+
/**
* Are we forced to run a search.
*
//campaign description.
$this->add('text', 'description', ts('Description'), $attributes['description']);
- //campaign start date.
- $this->addDate('start_date', ts('From'), FALSE, ['formatType' => 'searchDate']);
-
- //campaign end date.
- $this->addDate('end_date', ts('To'), FALSE, ['formatType' => 'searchDate']);
+ $this->add('datepicker', 'start_date', ts('Campaign Start Date'), [], FALSE, ['time' => FALSE]);
+ $this->add('datepicker', 'end_date', ts('Campaign End Date'), [], FALSE, ['time' => FALSE]);
//campaign type.
$campaignTypes = CRM_Campaign_PseudoConstant::campaignType();
'civicrm_phone.phone as phone',
'civicrm_contact.contact_type as contact_type',
'civicrm_contact.contact_sub_type as contact_sub_type',
- 't_act.activity_type_id',
- 'c_type.title as case_type',
+ 't_act.activity_type_id as activity_type_id',
'civicrm_case.case_type_id as case_type_id',
- 'cov_status.label as case_status',
- 'cov_status.label as case_status_name',
- 't_act.status_id',
+ 'civicrm_case.status_id as case_status_id',
+ 't_act.status_id as status_id',
'civicrm_case.start_date as case_start_date',
'case_relation_type.label_b_a as case_role',
+ 't_act.activity_date_time as activity_date_time',
+ 't_act.id as activity_id',
);
- if ($type == 'upcoming') {
- $selectClauses = array_merge($selectClauses, array(
- 't_act.desired_date as case_scheduled_activity_date',
- 't_act.id as case_scheduled_activity_id',
- 't_act.act_type_name as case_scheduled_activity_type_name',
- 't_act.act_type AS case_scheduled_activity_type',
- ));
- }
- elseif ($type == 'recent') {
- $selectClauses = array_merge($selectClauses, array(
- 't_act.desired_date as case_recent_activity_date',
- 't_act.id as case_recent_activity_id',
- 't_act.act_type_name as case_recent_activity_type_name',
- 't_act.act_type AS case_recent_activity_type',
- ));
- }
- elseif ($type == 'any') {
- $selectClauses = array_merge($selectClauses, array(
- 't_act.desired_date as case_activity_date',
- 't_act.id as case_activity_id',
- 't_act.act_type_name as case_activity_type_name',
- 't_act.act_type AS case_activity_type',
- ));
- }
-
$query = CRM_Contact_BAO_Query::appendAnyValueToSelect($selectClauses, 'case_id');
- $query .= " FROM civicrm_case
- INNER JOIN civicrm_case_contact ON civicrm_case.id = civicrm_case_contact.case_id
- INNER JOIN civicrm_contact ON civicrm_case_contact.contact_id = civicrm_contact.id ";
-
- if ($type == 'upcoming') {
- // This gets the earliest activity per case that's scheduled within 14 days from now.
- // Note we have an inner select to get the min activity id in order to remove duplicates in case there are two with the same datetime.
- // In this case we don't really care which one, so min(id) works.
- // optimized in CRM-11837
- $query .= " INNER JOIN
-(
- SELECT case_id, act.id, activity_date_time AS desired_date, activity_type_id, status_id, aov.name AS act_type_name, aov.label AS act_type
- FROM (
- SELECT *
- FROM (
- SELECT *
- FROM civicrm_view_case_activity_upcoming
- ORDER BY activity_date_time ASC, id ASC
- ) AS upcomingOrdered
- ) AS act
- LEFT JOIN civicrm_option_group aog ON aog.name='activity_type'
- LEFT JOIN civicrm_option_value aov ON ( aov.option_group_id = aog.id AND aov.value = act.activity_type_id )
-) AS t_act
-";
- }
- elseif ($type == 'recent') {
- // Similarly, the most recent activity in the past 14 days, and exclude scheduled.
- //improve query performance - CRM-10598
- $query .= " INNER JOIN
-(
- SELECT case_id, act.id, activity_date_time AS desired_date, activity_type_id, status_id, aov.name AS act_type_name, aov.label AS act_type
- FROM (
- SELECT *
- FROM (
- SELECT *
- FROM civicrm_view_case_activity_recent
- ORDER BY activity_date_time DESC, id ASC
- ) AS recentOrdered
- ) AS act
-LEFT JOIN civicrm_option_group aog ON aog.name='activity_type'
- LEFT JOIN civicrm_option_value aov ON ( aov.option_group_id = aog.id AND aov.value = act.activity_type_id )
-) AS t_act ";
- }
- elseif ($type == 'any') {
- $query .= " LEFT JOIN
-(
- SELECT ca4.case_id, act4.id AS id, act4.activity_date_time AS desired_date, act4.activity_type_id, act4.status_id, aov.name AS act_type_name, aov.label AS act_type
- FROM civicrm_activity act4
- LEFT JOIN civicrm_case_activity ca4
- ON ca4.activity_id = act4.id
- AND act4.is_current_revision = 1
- LEFT JOIN civicrm_option_group aog
- ON aog.name='activity_type'
- LEFT JOIN civicrm_option_value aov
- ON aov.option_group_id = aog.id
- AND aov.value = act4.activity_type_id
-) AS t_act";
- }
-
- $query .= "
- ON t_act.case_id = civicrm_case.id
- LEFT JOIN civicrm_phone ON (civicrm_phone.contact_id = civicrm_contact.id AND civicrm_phone.is_primary=1)
- LEFT JOIN civicrm_relationship case_relationship
- ON ( case_relationship.contact_id_a = civicrm_case_contact.contact_id AND case_relationship.contact_id_b = {$userID} AND case_relationship.is_active AND case_relationship.case_id = civicrm_case.id )
- LEFT JOIN civicrm_relationship_type case_relation_type
- ON ( case_relation_type.id = case_relationship.relationship_type_id
- AND case_relation_type.id = case_relationship.relationship_type_id )
-
- LEFT JOIN civicrm_case_type c_type
- ON civicrm_case.case_type_id = c_type.id
-
- LEFT JOIN civicrm_option_group cog_status
- ON cog_status.name = 'case_status'
-
- LEFT JOIN civicrm_option_value cov_status
- ON ( civicrm_case.status_id = cov_status.value
- AND cog_status.id = cov_status.option_group_id )
-";
+ $query .= <<<HERESQL
+ FROM civicrm_case
+ INNER JOIN civicrm_case_contact ON civicrm_case.id = civicrm_case_contact.case_id
+ INNER JOIN civicrm_contact ON civicrm_case_contact.contact_id = civicrm_contact.id
+HERESQL;
+
+ switch ($type) {
+ case 'upcoming':
+ case 'recent':
+ // civicrm_view_case_activity_upcoming and
+ // civicrm_view_case_activity_recent are views that show the next
+ // scheduled and most recent not-scheduled activity on each case,
+ // respectively.
+ $query .= <<<HERESQL
+ INNER JOIN civicrm_view_case_activity_$type t_act
+ ON t_act.case_id = civicrm_case.id
+HERESQL;
+ break;
+
+ case 'any':
+ $query .= <<<HERESQL
+ LEFT JOIN civicrm_case_activity ca4
+ ON civicrm_case.id = ca4.case_id
+ LEFT JOIN civicrm_activity t_act
+ ON t_act.id = ca4.activity_id
+ AND t_act.is_current_revision = 1
+HERESQL;
+ }
+
+ $query .= <<<HERESQL
+ LEFT JOIN civicrm_phone
+ ON civicrm_phone.contact_id = civicrm_contact.id
+ AND civicrm_phone.is_primary = 1
+ LEFT JOIN civicrm_relationship case_relationship
+ ON case_relationship.contact_id_a = civicrm_case_contact.contact_id
+ AND case_relationship.contact_id_b = {$userID}
+ AND case_relationship.is_active
+ AND case_relationship.case_id = civicrm_case.id
+ LEFT JOIN civicrm_relationship_type case_relation_type
+ ON case_relation_type.id = case_relationship.relationship_type_id
+ AND case_relation_type.id = case_relationship.relationship_type_id
+HERESQL;
if ($condition) {
// CRM-8749 backwards compatibility - callers of this function expect to start $condition with "AND"
}
$query .= " GROUP BY case_id ";
- if ($order) {
- $query .= $order;
- }
- else {
- if ($type == 'upcoming') {
- $query .= " ORDER BY case_scheduled_activity_date ASC ";
- }
- elseif ($type == 'recent') {
- $query .= " ORDER BY case_recent_activity_date ASC ";
- }
- elseif ($type == 'any') {
- $query .= " ORDER BY case_activity_date ASC ";
- }
- }
+ $query .= ($order) ?: ' ORDER BY activity_date_time ASC';
if ($limit) {
$query .= $limit;
$type = CRM_Utils_Array::value('type', $params, 'upcoming');
$userID = CRM_Core_Session::singleton()->get('userID');
- $caseActivityTypeColumn = 'case_activity_type_name';
- $caseActivityDateColumn = 'case_activity_date';
- $caseActivityIDColumn = 'case_activity_id';
- if ($type == 'upcoming') {
- $caseActivityDateColumn = 'case_scheduled_activity_date';
- $caseActivityTypeColumn = 'case_scheduled_activity_type';
- $caseActivityIDColumn = 'case_scheduled_activity_id';
- }
- elseif ($type == 'recent') {
- $caseActivityDateColumn = 'case_recent_activity_date';
- $caseActivityTypeColumn = 'case_recent_activity_type';
- $caseActivityIDColumn = 'case_recent_activity_id';
- }
-
// validate access for all cases.
if ($allCases && !CRM_Core_Permission::check('access all cases and activities')) {
$allCases = FALSE;
$order = NULL;
if (!empty($params['sortBy'])) {
if (strstr($params['sortBy'], 'date ')) {
- $params['sortBy'] = str_replace('date', $caseActivityDateColumn, $params['sortBy']);
+ $params['sortBy'] = str_replace('date', 'activity_date_time', $params['sortBy']);
}
$order = "ORDER BY " . $params['sortBy'];
}
$query = self::getCaseActivityQuery($type, $userID, $condition, $limit, $order);
$result = CRM_Core_DAO::executeQuery($query);
- $caseStatus = CRM_Core_OptionGroup::values('case_status', FALSE, FALSE, FALSE, " AND v.name = 'Urgent' ");
-
// we're going to use the usual actions, so doesn't make sense to duplicate definitions
$actions = CRM_Case_Selector_Search::links();
}
$mask = CRM_Core_Action::mask($permissions);
+ // Pseudoconstants to populate labels
+ $caseStatuses = CRM_Case_PseudoConstant::caseStatus('label', FALSE);
$caseTypes = CRM_Case_PseudoConstant::caseType('name');
+ $caseTypeTitles = CRM_Case_PseudoConstant::caseType('title', FALSE);
+ $activityTypeLabels = CRM_Activity_BAO_Activity::buildOptions('activity_type_id');
+
foreach ($result->fetchAll() as $case) {
$key = $case['case_id'];
$casesList[$key] = array();
$case['case_id']
);
$casesList[$key]['subject'] = $case['case_subject'];
- $casesList[$key]['case_status'] = in_array($case['case_status'], $caseStatus) ? sprintf('<strong>%s</strong>', strtoupper($case['case_status'])) : $case['case_status'];
- $casesList[$key]['case_type'] = $case['case_type'];
+ $casesList[$key]['case_status'] = CRM_Utils_Array::value($case['case_status_id'], $caseStatuses);
+ if ($case['case_status_id'] == CRM_Case_PseudoConstant::getKey('CRM_Case_BAO_Case', 'case_status_id', 'Urgent')) {
+ $casesList[$key]['case_status'] = sprintf('<strong>%s</strong>', strtoupper($casesList[$key]['case_status']));
+ }
+ $casesList[$key]['case_type'] = CRM_Utils_Array::value($case['case_type_id'], $caseTypeTitles);
$casesList[$key]['case_role'] = CRM_Utils_Array::value('case_role', $case, '---');
$casesList[$key]['manager'] = self::getCaseManagerContact($caseTypes[$case['case_type_id']], $case['case_id']);
- $casesList[$key]['date'] = $case[$caseActivityTypeColumn];
- if (($actId = CRM_Utils_Array::value('case_scheduled_activity_id', $case)) ||
- ($actId = CRM_Utils_Array::value('case_recent_activity_id', $case))
- ) {
+ $casesList[$key]['date'] = CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels);
+ if ($actId = CRM_Utils_Array::value('activity_id', $case)) {
if (self::checkPermission($actId, 'view', $case['activity_type_id'], $userID)) {
if ($type == 'recent') {
$casesList[$key]['date'] = sprintf('<a class="action-item crm-hover-button" href="%s" title="%s">%s</a>',
- CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case[$caseActivityIDColumn])),
+ CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case['activity_id'])),
ts('View activity'),
- $case[$caseActivityTypeColumn]
+ CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels)
);
}
else {
- $status = CRM_Utils_Date::overdue($case[$caseActivityDateColumn]) ? 'status-overdue' : 'status-scheduled';
+ $status = CRM_Utils_Date::overdue($case['activity_date_time']) ? 'status-overdue' : 'status-scheduled';
$casesList[$key]['date'] = sprintf('<a class="crm-popup %s" href="%s" title="%s">%s</a> ',
$status,
- CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case[$caseActivityIDColumn])),
+ CRM_Utils_System::url('civicrm/case/activity/view', array('reset' => 1, 'cid' => $case['contact_id'], 'aid' => $case['activity_id'])),
ts('View activity'),
- $case[$caseActivityTypeColumn]
+ CRM_Utils_Array::value($case['activity_type_id'], $activityTypeLabels)
);
}
}
);
}
}
- $casesList[$key]['date'] .= "<br/>" . CRM_Utils_Date::customFormat($case[$caseActivityDateColumn]);
+ $casesList[$key]['date'] .= "<br/>" . CRM_Utils_Date::customFormat($case['activity_date_time']);
$casesList[$key]['links'] = CRM_Core_Action::formLink($actions['primaryActions'], $mask,
array(
'id' => $case['case_id'],
// Activity Status Label for Case activities list
$caseActivities[$caseActivityId]['status_id'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_status_id', $dao->status);
+ $deleted = '';
+ if ($dao->deleted) {
+ $deleted = '<br /> ' . ts('(deleted)');
+ }
+ $caseActivities[$caseActivityId]['status_id'] = CRM_Core_PseudoConstant::getLabel('CRM_Activity_BAO_Activity', 'activity_status_id', $dao->status) . $deleted;
+ // if there are file attachments we will return how many
+ if (!empty($dao->attachment_ids)) {
+ $attachmentIDs = array_unique(explode(',', $dao->attachment_ids));
+ $caseActivity['no_attachments'] = count($attachmentIDs);
+ }
- $caseActivities[$caseActivityId]
- = self::addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao, $caseActivities[$caseActivityId]);
+ $caseActivities[$caseActivityId]['links'] = self::addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao);
}
$caseActivitiesDT = array();
* @param int $userID
* @param string $context
* @param \CRM_Core_DAO $dao
- * @param array $caseActivity
*
- * @return array caseActivity
+ * @return string
+ * HTML formatted Link
*/
- public static function addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao, $caseActivity) {
+ private static function addCaseActivityLinks($caseID, $contactID, $userID, $context, $dao) {
// FIXME: Why are we not using CRM_Core_Action for these links? This is too much manual work and likely to get out-of-sync with core markup.
$caseActivityId = $dao->id;
$allowView = self::checkPermission($caseActivityId, 'view', $dao->activity_type_id, $userID);
}
elseif (!$caseDeleted) {
$url = ' <a ' . $css . ' href="' . $restoreUrl . $additionalUrl . '">' . ts('Restore') . '</a>';
- $caseActivity['status_id'] = $caseActivity['status_id'] . '<br /> (deleted)';
}
//check for operations.
}
// if there are file attachments we will return how many and, if only one, add a link to it
if (!empty($dao->attachment_ids)) {
- $attachmentIDs = array_unique(explode(',', $dao->attachment_ids));
- $caseActivity['no_attachments'] = count($attachmentIDs);
$url .= implode(' ', CRM_Core_BAO_File::paperIconAttachment('civicrm_activity', $caseActivityId));
}
- $caseActivity['links'] = $url;
- return $caseActivity;
+
+ return $url;
}
/**
AND civicrm_case.is_deleted = {$cases['case_deleted']}";
$query = self::getCaseActivityQuery($type, $userID, $condition);
+ $activityTypes = CRM_Activity_BAO_Activity::buildOptions('activity_type_id');
$res = CRM_Core_DAO::executeQuery($query);
$activityInfo = array();
while ($res->fetch()) {
if ($type == 'upcoming') {
- $activityInfo[$res->case_id]['date'] = $res->case_scheduled_activity_date;
- $activityInfo[$res->case_id]['type'] = $res->case_scheduled_activity_type;
+ $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
+ $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
}
else {
- $activityInfo[$res->case_id]['date'] = $res->case_recent_activity_date;
- $activityInfo[$res->case_id]['type'] = $res->case_recent_activity_type;
+ $activityInfo[$res->case_id]['date'] = $res->activity_date_time;
+ $activityInfo[$res->case_id]['type'] = CRM_Utils_Array::value($res->activity_type_id, $activityTypes);
}
}
//allow edit operation.
$allowEditNames = array('Open Case');
- if (CRM_Core_Permission::check('edit inbound email basic information') ||
- CRM_Core_Permission::check('edit inbound email basic information and content')
- ) {
+ if (CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions()) {
$allowEditNames[] = 'Inbound Email';
}
AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
FROM civicrm_case_activity ca
INNER JOIN civicrm_activity a ON ca.activity_id=a.id
- WHERE a.activity_date_time =
-(SELECT b.activity_date_time FROM civicrm_case_activity bca
+ WHERE a.id =
+(SELECT b.id FROM civicrm_case_activity bca
INNER JOIN civicrm_activity b ON bca.activity_id=b.id
WHERE b.activity_date_time <= DATE_ADD( NOW(), INTERVAL 14 DAY )
AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id = $scheduled_id
AS SELECT ca.case_id, a.id, a.activity_date_time, a.status_id, a.activity_type_id
FROM civicrm_case_activity ca
INNER JOIN civicrm_activity a ON ca.activity_id=a.id
- WHERE a.activity_date_time =
-(SELECT b.activity_date_time FROM civicrm_case_activity bca
+ WHERE a.id =
+(SELECT b.id FROM civicrm_case_activity bca
INNER JOIN civicrm_activity b ON bca.activity_id=b.id
WHERE b.activity_date_time >= DATE_SUB( NOW(), INTERVAL 14 DAY )
AND b.is_current_revision = 1 AND b.is_deleted=0 AND b.status_id <> $scheduled_id
'type' => 'upload',
'name' => ts('Save and New'),
'subName' => 'new',
- 'submitOnce' => TRUE,
],
[
'type' => 'cancel',
$isDeleted = FALSE;
if ($result->case_deleted) {
$isDeleted = TRUE;
- $row['case_status_id'] = empty($row['case_status_id']) ? "" : $row['case_status_id'] . '<br />(deleted)';
+ $row['case_status_id'] = empty($row['case_status_id']) ? "" : $row['case_status_id'] . '<br />' . ts('(deleted)');
}
$scheduledInfo['case_id'][] = $result->case_id;
*/
public static function getdefaults() {
return array(
- 'is_active' => 0,
+ 'is_active' => 1,
'is_permission_a_b' => self::NONE,
'is_permission_b_a' => self::NONE,
'description' => '',
*
* Generated from xml/schema/CRM/Contact/Group.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:48373e283a62b36aa59ac812f8cb4134)
+ * (GenCodeChecksum:21b3e21494b0831994f860083cd82e24)
*/
/**
'entity' => 'Group',
'bao' => 'CRM_Contact_BAO_Group',
'localizable' => 1,
+ 'html' => [
+ 'type' => 'Text',
+ ],
],
'description' => [
'name' => 'description',
);
$this->addField('used', ['label' => ts('Usage')], TRUE);
- $disabled = [];
$reserved = $this->addField('is_reserved', ['label' => ts('Reserved?')]);
if (!empty($this->_defaults['is_reserved'])) {
$reserved->freeze();
}
$attributes = ['class' => 'two'];
- if (!empty($disabled)) {
- $attributes = array_merge($attributes, $disabled);
- }
for ($count = 0; $count < self::RULES_COUNT; $count++) {
$this->add('select', "where_$count", ts('Field'),
- [
- NULL => ts('- none -'),
- ] + $this->_fields, FALSE, $disabled
+ $this->_fields, FALSE, ['class' => 'crm-select2', 'placeholder' => ts('Select Field')]
);
$this->addField("length_$count", ['entity' => 'Rule', 'name' => 'rule_length'] + $attributes);
$this->addField("weight_$count", ['entity' => 'Rule', 'name' => 'rule_weight'] + $attributes);
}
public function buildTempTable() {
- $table = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory()->setUtf8();
+ $table = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory();
$this->_tableName = $table->getName();
$this->_tableFields = [
";
$table->createWithColumns($sql);
- $entityIdTable = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory()->setUtf8();
+ $entityIdTable = CRM_Utils_SQL_TempTable::build()->setCategory('custom')->setMemory();
$this->_entityIDTableName = $entityIdTable->getName();
$sql = "
id int unsigned NOT NULL AUTO_INCREMENT,
$form->assign('taskName', CRM_Utils_Array::value($form->_task, $crmContactTaskTasks));
if ($useTable) {
- $tempTable = CRM_Utils_SQL_TempTable::build()->setCategory('tskact')->setDurable()->setId($qfKey)->setUtf8();
+ $tempTable = CRM_Utils_SQL_TempTable::build()->setCategory('tskact')->setDurable()->setId($qfKey);
$form->_componentTable = $tempTable->getName();
$tempTable->drop();
$tempTable->createWithColumns('contact_id int primary key');
}
// Get Task name
- $modeValue = CRM_Contact_Form_Search::getModeValue($values['component_mode']);
+ $modeValue = CRM_Contact_Form_Search::getModeValue(CRM_Utils_Array::value('component_mode', $values, CRM_Contact_BAO_Query::MODE_CONTACTS));
$className = $modeValue['taskClassName'];
$taskList = $className::taskTitles();
$this->_task = CRM_Utils_Array::value('task', $values);
$result = CRM_Core_DAO::executeQuery($query, array($database));
$incompleteImportTables = array();
while ($importTable = $result->fetch()) {
- if (!$this->isComplete($importTable)) {
+ if (!self::isComplete($importTable)) {
$incompleteImportTables[] = $importTable;
}
}
$fields[$block] = $this->getMetadataForEntity($block);
- $blockCnt = count($params[$blockFieldName]);
-
// copy value to dao field name.
if ($blockFieldName == 'im') {
$values['name'] = $values[$blockFieldName];
}
_civicrm_api3_store_values($fields[$block], $values,
- $params[$blockFieldName][++$blockCnt]
+ $params[$blockFieldName][$values['location_type_id']]
);
- if ($values['location_type_id'] === 'Primary') {
- if (!empty($params['id'])) {
- $primary = civicrm_api3($block, 'get', [
- 'return' => 'location_type_id',
- 'contact_id' => $params['id'],
- 'is_primary' => 1,
- 'sequential' => 1
- ]);
- }
- $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
- $values['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id;
- $values['is_primary'] = 1;
- }
+ $this->fillPrimary($params[$blockFieldName][$values['location_type_id']], $values, $block, CRM_Utils_Array::value('id', $params));
- if (empty($params['id']) && ($blockCnt == 1)) {
- $params[$blockFieldName][$blockCnt]['is_primary'] = TRUE;
+ if (empty($params['id']) && (count($params[$blockFieldName]) == 1)) {
+ $params[$blockFieldName][$values['location_type_id']]['is_primary'] = TRUE;
}
// we only process single block at a time.
}
}
- if ($values['location_type_id'] === 'Primary') {
- if (!empty($params['id'])) {
- $primary = civicrm_api3('Address', 'get', [
- 'return' => 'location_type_id',
- 'contact_id' => $params['id'],
- 'is_primary' => 1,
- 'sequential' => 1
- ]);
- }
- $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
- $params['address'][$values['location_type_id']]['location_type_id'] = (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id;
- $params['address'][$values['location_type_id']]['is_primary'] = 1;
-
- }
+ $this->fillPrimary($params['address'][$values['location_type_id']], $values, 'address', CRM_Utils_Array::value('id', $params));
return TRUE;
}
return $this->fieldMetadata[$entity];
}
+ /**
+ * Fill in the primary location.
+ *
+ * If the contact has a primary address we update it. Otherwise
+ * we add an address of the default location type.
+ *
+ * @param array $params
+ * Address block parameters
+ * @param array $values
+ * Input values
+ * @param string $entity
+ * - address, email, phone
+ * @param int|NULL $contactID
+ */
+ protected function fillPrimary(&$params, $values, $entity, $contactID) {
+ if ($values['location_type_id'] === 'Primary') {
+ if ($contactID) {
+ $primary = civicrm_api3($entity, 'get', [
+ 'return' => 'location_type_id',
+ 'contact_id' => $contactID,
+ 'is_primary' => 1,
+ 'sequential' => 1
+ ]);
+ }
+ $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
+ $params['location_type_id'] = (int) (isset($primary) && $primary['count']) ? $primary['values'][0]['location_type_id'] : $defaultLocationType->id;
+ $params['is_primary'] = 1;
+ }
+ }
+
}
$inputParams['id'] = $participantId;
$values = [];
$ids = [];
- $component = 'event';
$entityObj = CRM_Event_BAO_Participant::getValues($inputParams, $values, $ids);
$entityObj = $entityObj[$participantId];
+ $title = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_Event', $entityObj->event_id, 'title');
}
else {
$entityObj = new CRM_Contribute_BAO_Contribution();
$entityObj->id = $contributionId;
$entityObj->find(TRUE);
- $component = 'contribution';
+ $title = ts('Contribution');
}
- self::addActivityForPayment($entityObj, $financialTrxn, $activityType, $component, $contributionId);
+ self::addActivityForPayment($entityObj->contact_id, $financialTrxn, $activityType, $title, $contributionId);
}
/**
])['values'];
}
+ /**
+ * Cancel contribution.
+ *
+ * This function should only be called from transitioncomponents - it is an interim step in refactoring.
+ *
+ * @param $processContributionObject
+ * @param $memberships
+ * @param $contributionId
+ * @param $membershipStatuses
+ * @param $updateResult
+ * @param $participant
+ * @param $oldStatus
+ * @param $pledgePayment
+ * @param $pledgeID
+ * @param $pledgePaymentIDs
+ * @param $contributionStatusId
+ * @return array
+ */
+ protected static function cancel($processContributionObject, $memberships, $contributionId, $membershipStatuses, $updateResult, $participant, $oldStatus, $pledgePayment, $pledgeID, $pledgePaymentIDs, $contributionStatusId) {
+ $processContribution = FALSE;
+ $participantStatuses = CRM_Event_PseudoConstant::participantStatus();
+ if (is_array($memberships)) {
+ foreach ($memberships as $membership) {
+ $update = TRUE;
+ //Update Membership status if there is no other completed contribution associated with the membership.
+ $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE);
+ foreach ($relatedContributions as $contriId) {
+ if ($contriId == $contributionId) {
+ continue;
+ }
+ $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id');
+ if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') {
+ $update = FALSE;
+ }
+ }
+ if ($membership && $update) {
+ $newStatus = array_search('Cancelled', $membershipStatuses);
+
+ // Create activity
+ $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get');
+ $activityParam = [
+ 'subject' => "Status changed from {$allStatus[$membership->status_id]} to {$allStatus[$newStatus]}",
+ 'source_contact_id' => CRM_Core_Session::singleton()->get('userID'),
+ 'target_contact_id' => $membership->contact_id,
+ 'source_record_id' => $membership->id,
+ 'activity_type_id' => 'Change Membership Status',
+ 'status_id' => 'Completed',
+ 'priority_id' => 'Normal',
+ 'activity_date_time' => 'now',
+ ];
+
+ $membership->status_id = $newStatus;
+ $membership->is_override = TRUE;
+ $membership->status_override_end_date = 'null';
+ $membership->save();
+ civicrm_api3('activity', 'create', $activityParam);
+
+ $updateResult['updatedComponents']['CiviMember'] = $membership->status_id;
+ if ($processContributionObject) {
+ $processContribution = TRUE;
+ }
+ }
+ }
+ }
+
+ if ($participant) {
+ $updatedStatusId = array_search('Cancelled', $participantStatuses);
+ CRM_Event_BAO_Participant::updateParticipantStatus($participant->id, $oldStatus, $updatedStatusId, TRUE);
+
+ $updateResult['updatedComponents']['CiviEvent'] = $updatedStatusId;
+ if ($processContributionObject) {
+ $processContribution = TRUE;
+ }
+ }
+
+ if ($pledgePayment) {
+ CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID, $pledgePaymentIDs, $contributionStatusId);
+
+ $updateResult['updatedComponents']['CiviPledge'] = $contributionStatusId;
+ if ($processContributionObject) {
+ $processContribution = TRUE;
+ }
+ }
+ return [$updateResult, $processContribution];
+ }
+
/**
* @inheritDoc
*/
$participant = &$objects['participant'];
$pledgePayment = &$objects['pledge_payment'];
$contribution = &$objects['contribution'];
-
+ $pledgeID = $oldStatus = NULL;
+ $pledgePaymentIDs = [];
if ($pledgePayment) {
- $pledgePaymentIDs = array();
foreach ($pledgePayment as $key => $object) {
$pledgePaymentIDs[] = $object->id;
}
// we might want to process contribution object.
$processContribution = FALSE;
if ($contributionStatusId == array_search('Cancelled', $contributionStatuses)) {
- if (is_array($memberships)) {
- foreach ($memberships as $membership) {
- $update = TRUE;
- //Update Membership status if there is no other completed contribution associated with the membership.
- $relatedContributions = CRM_Member_BAO_Membership::getMembershipContributionId($membership->id, TRUE);
- foreach ($relatedContributions as $contriId) {
- if ($contriId == $contributionId) {
- continue;
- }
- $statusId = CRM_Core_DAO::getFieldValue('CRM_Contribute_BAO_Contribution', $contriId, 'contribution_status_id');
- if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_Contribution', 'contribution_status_id', $statusId) === 'Completed') {
- $update = FALSE;
- }
- }
- if ($membership && $update) {
- $newStatus = array_search('Cancelled', $membershipStatuses);
-
- // Create activity
- $allStatus = CRM_Member_BAO_Membership::buildOptions('status_id', 'get');
- $activityParam = array(
- 'subject' => "Status changed from {$allStatus[$membership->status_id]} to {$allStatus[$newStatus]}",
- 'source_contact_id' => CRM_Core_Session::singleton()->get('userID'),
- 'target_contact_id' => $membership->contact_id,
- 'source_record_id' => $membership->id,
- 'activity_type_id' => 'Change Membership Status',
- 'status_id' => 'Completed',
- 'priority_id' => 'Normal',
- 'activity_date_time' => 'now',
- );
-
- $membership->status_id = $newStatus;
- $membership->is_override = TRUE;
- $membership->status_override_end_date = 'null';
- $membership->save();
- civicrm_api3('activity', 'create', $activityParam);
-
- $updateResult['updatedComponents']['CiviMember'] = $membership->status_id;
- if ($processContributionObject) {
- $processContribution = TRUE;
- }
- }
- }
- }
-
- if ($participant) {
- $updatedStatusId = array_search('Cancelled', $participantStatuses);
- CRM_Event_BAO_Participant::updateParticipantStatus($participant->id, $oldStatus, $updatedStatusId, TRUE);
-
- $updateResult['updatedComponents']['CiviEvent'] = $updatedStatusId;
- if ($processContributionObject) {
- $processContribution = TRUE;
- }
- }
-
- if ($pledgePayment) {
- CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeID, $pledgePaymentIDs, $contributionStatusId);
-
- $updateResult['updatedComponents']['CiviPledge'] = $contributionStatusId;
- if ($processContributionObject) {
- $processContribution = TRUE;
- }
- }
+ // Call interim cancel function - with a goal to cleaning up the signature on it and switching to a tested api Contribution.cancel function.
+ list($updateResult, $processContribution) = self::cancel($processContributionObject, $memberships, $contributionId, $membershipStatuses, $updateResult, $participant, $oldStatus, $pledgePayment, $pledgeID, $pledgePaymentIDs, $contributionStatusId);
}
elseif ($contributionStatusId == array_search('Failed', $contributionStatuses)) {
if (is_array($memberships)) {
'net_amount' => CRM_Utils_Array::value('net_amount', $params, $totalAmount),
'currency' => $params['contribution']->currency,
'trxn_id' => $params['contribution']->trxn_id,
+ // @todo - this is getting the status id from the contribution - that is BAD - ie the contribution could be partially
+ // paid but each payment is completed. The work around is to pass in the status_id in the trxn_params but
+ // this should really default to completed (after discussion).
'status_id' => $statusId,
'payment_instrument_id' => CRM_Utils_Array::value('payment_instrument_id', $params, $params['contribution']->payment_instrument_id),
'check_number' => CRM_Utils_Array::value('check_number', $params),
$financialTrxn = CRM_Financial_BAO_Payment::recordPayment($contributionId, $trxnsData, $participantId);
}
elseif ($paymentType == 'refund') {
+ $trxnsData['total_amount'] = -$trxnsData['total_amount'];
$financialTrxn = CRM_Financial_BAO_Payment::recordRefundPayment($contributionId, $trxnsData, $updateStatus);
if ($participantId) {
// update participant status
}
/**
- * @param $entityObj
+ * @param int $targetCid
* @param $trxnObj
* @param $activityType
- * @param $component
+ * @param string $title
* @param int $contributionId
*
* @throws CRM_Core_Exception
*/
- public static function addActivityForPayment($entityObj, $trxnObj, $activityType, $component, $contributionId) {
- if ($component == 'event') {
- $title = CRM_Core_DAO::getFieldValue('CRM_Event_BAO_Event', $entityObj->event_id, 'title');
- }
- else {
- $title = ts('Contribution');
- }
+ public static function addActivityForPayment($targetCid, $trxnObj, $activityType, $title, $contributionId) {
$paymentAmount = CRM_Utils_Money::format($trxnObj->total_amount, $trxnObj->currency);
$subject = "{$paymentAmount} - Offline {$activityType} for {$title}";
$date = CRM_Utils_Date::isoToMysql($trxnObj->trxn_date);
- $targetCid = $entityObj->contact_id;
// source record id would be the contribution id
$srcRecId = $contributionId;
'title' => ts('Copy of') . ' ',
],
];
- $copy = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_ContributionPage', [
+ $copy = CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_ContributionPage', [
'id' => $id,
], NULL, $fieldsFix);
//copying all the blocks pertaining to the contribution page
- $copyPledgeBlock = &CRM_Core_DAO::copyGeneric('CRM_Pledge_DAO_PledgeBlock', [
+ $copyPledgeBlock = CRM_Core_DAO::copyGeneric('CRM_Pledge_DAO_PledgeBlock', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
'entity_id' => $copy->id,
]);
- $copyMembershipBlock = &CRM_Core_DAO::copyGeneric('CRM_Member_DAO_MembershipBlock', [
+ $copyMembershipBlock = CRM_Core_DAO::copyGeneric('CRM_Member_DAO_MembershipBlock', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
'entity_id' => $copy->id,
]);
- $copyUFJoin = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin', [
+ $copyUFJoin = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
'entity_id' => $copy->id,
]);
- $copyWidget = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Widget', [
+ $copyWidget = CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Widget', [
'contribution_page_id' => $id,
], [
'contribution_page_id' => $copy->id,
//copy price sets
CRM_Price_BAO_PriceSet::copyPriceSet('civicrm_contribution_page', $id, $copy->id);
- $copyTellFriend = &CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend', [
+ $copyTellFriend = CRM_Core_DAO::copyGeneric('CRM_Friend_DAO_Friend', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
'entity_id' => $copy->id,
]);
- $copyPersonalCampaignPages = &CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock', [
+ $copyPersonalCampaignPages = CRM_Core_DAO::copyGeneric('CRM_PCP_DAO_PCPBlock', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
'target_entity_id' => $copy->id,
]);
- $copyPremium = &CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Premium', [
+ $copyPremium = CRM_Core_DAO::copyGeneric('CRM_Contribute_DAO_Premium', [
'entity_id' => $id,
'entity_table' => 'civicrm_contribution_page',
], [
* @return bool
*/
public static function cancelRecurContribution($params) {
- if (is_int($params)) {
+ if (is_numeric($params)) {
CRM_Core_Error::deprecatedFunctionWarning('You are using a BAO function whose signature has changed. Please use the ContributionRecur.cancel api');
$params = ['id' => $params];
}
* Associative array of contribution fields
*/
public static function getFields($checkPermission = TRUE) {
- if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['fields']) || !isset(\Civi::$statics[__CLASS__]['contribution'])) {
- $fields = CRM_Contribute_BAO_Contribution::exportableFields($checkPermission);
+ if (!isset(\Civi::$statics[__CLASS__]) || !isset(\Civi::$statics[__CLASS__]['fields']) || !isset(\Civi::$statics[__CLASS__]['fields']['contribution'])) {
+ // Adding fields with some care as those without unique names could clobber others.
+ // Refer to CRM_Contribute_Form_SearchTest for existing tests ... and to add more!
+ $testedRecurFields = array_fill_keys(['contribution_recur_trxn_id', 'contribution_recur_processor_id', 'contribution_recur_payment_processor_id'], 1);
+ $recurFields = array_intersect_key(CRM_Contribute_DAO_ContributionRecur::fields(), $testedRecurFields);
+ $fields = array_merge($recurFields, CRM_Contribute_BAO_Contribution::exportableFields($checkPermission));
CRM_Contribute_BAO_Contribution::appendPseudoConstantsToFields($fields);
unset($fields['contribution_contact_id']);
\Civi::$statics[__CLASS__]['fields']['contribution'] = $fields;
case 'contribution_recur_processor_id':
case 'contribution_recur_trxn_id':
- $fieldName = str_replace('contribution_recur_', '', $name);
- $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("civicrm_contribution_recur.{$fieldName}",
+ $spec = $fields[$name];
+ $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause($spec['where'],
$op, $value, "String"
);
- $recurFields = CRM_Contribute_DAO_ContributionRecur::fields();
- $query->_qill[$grouping][] = ts("Recurring Contribution %1 %2 '%3'", [1 => $recurFields[$fieldName]['title'], 2 => $op, 3 => $value]);
- $query->_tables['civicrm_contribution_recur'] = $query->_whereTables['civicrm_contribution_recur'] = 1;
+ $query->_qill[$grouping][] = ts("Recurring Contribution %1 %2 '%3'", [1 => $fields[$name]['title'], 2 => $op, 3 => $value]);
+ $query->_tables[$spec['table_name']] = $query->_whereTables[$spec['table_name']] = 1;
return;
case 'contribution_recur_payment_made':
*
* Generated from xml/schema/CRM/Contribute/ContributionRecur.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:2ccc42487b9e4e5774fcfcde7db9c5ae)
+ * (GenCodeChecksum:9859d3b98d51f0f1df207253199186ab)
*/
/**
public static function &fields() {
if (!isset(Civi::$statics[__CLASS__]['fields'])) {
Civi::$statics[__CLASS__]['fields'] = [
- 'id' => [
+ 'contribution_recur_id' => [
'name' => 'id',
'type' => CRM_Utils_Type::T_INT,
'title' => ts('Recurring Contribution ID'),
'formatType' => 'activityDate',
],
],
- 'processor_id' => [
+ 'contribution_recur_processor_id' => [
'name' => 'processor_id',
'type' => CRM_Utils_Type::T_STRING,
'title' => ts('Processor ID'),
'localizable' => 0,
'FKClassName' => 'CRM_Financial_DAO_PaymentToken',
],
- 'trxn_id' => [
+ 'contribution_recur_trxn_id' => [
'name' => 'trxn_id',
'type' => CRM_Utils_Type::T_STRING,
'title' => ts('Transaction ID'),
'type' => 'Text',
],
],
- 'contribution_status_id' => [
+ 'contribution_recur_contribution_status_id' => [
'name' => 'contribution_status_id',
'type' => CRM_Utils_Type::T_INT,
'title' => ts('Status'),
'type' => 'CheckBox',
],
],
- 'payment_processor_id' => [
+ 'contribution_recur_payment_processor_id' => [
'name' => 'payment_processor_id',
'type' => CRM_Utils_Type::T_INT,
'title' => ts('Payment Processor'),
*/
class CRM_Contribute_Form_ContributionRecur extends CRM_Core_Form {
+ use CRM_Core_Form_EntityFormTrait;
+
/**
* @var int Contribution ID
*/
*/
public $_paymentProcessor = [];
+ /**
+ * Fields for the entity to be assigned to the template.
+ *
+ * Fields may have keys
+ * - name (required to show in tpl from the array)
+ * - description (optional, will appear below the field)
+ * - not-auto-addable - this class will not attempt to add the field using addField.
+ * (this will be automatically set if the field does not have html in it's metadata
+ * or is not a core field on the form's entity).
+ * - help (option) add help to the field - e.g ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact']]
+ * - template - use a field specific template to render this field
+ * @var array
+ */
+ protected $entityFields = [];
+
/**
* Explicitly declare the entity api name.
*/
* Set variables up before form is built.
*/
public function preProcess() {
+ $this->setAction(CRM_Core_Action::UPDATE);
$this->_mid = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE);
$this->_crid = CRM_Utils_Request::retrieve('crid', 'Integer', $this, FALSE);
$this->contributionRecurID = $this->_crid;
return $result;
}
+ /**
+ * Given a set of links and a mask, return a filtered (by mask) array containing the final links with parsed values
+ * and calling hooks as appropriate.
+ * Use this when passing a set of action links to the API or to the form without adding html formatting.
+ *
+ * @param array $links
+ * The set of link items.
+ * @param int $mask
+ * The mask to be used. a null mask means all items.
+ * @param array $values
+ * The array of values for parameter substitution in the link items.
+ * @param null $op
+ * @param null $objectName
+ * @param int $objectId
+ *
+ * @return array|null
+ * The array describing each link
+ */
+ public static function filterLinks(
+ $links,
+ $mask,
+ $values,
+ $op = NULL,
+ $objectName = NULL,
+ $objectId = NULL
+ ) {
+ if (empty($links)) {
+ return NULL;
+ }
+
+ // make links indexed sequentially instead of by bitmask
+ // otherwise it's next to impossible to reliably add new ones
+ $seqLinks = array();
+ foreach ($links as $bit => $link) {
+ $link['bit'] = $bit;
+ $seqLinks[] = $link;
+ }
+
+ if ($op && $objectName && $objectId) {
+ CRM_Utils_Hook::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
+ }
+
+ foreach ($seqLinks as $i => $link) {
+ if (!$mask || !array_key_exists('bit', $link) || ($mask & $link['bit'])) {
+ $seqLinks[$i]['extra'] = isset($link['extra']) ? self::replace($link['extra'], $values) : NULL;
+
+ if (isset($link['qs']) && !CRM_Utils_System::isNull($link['qs'])) {
+ $seqLinks[$i]['url'] = self::replace($link['url'], $values);
+ $seqLinks[$i]['qs'] = self::replace($link['qs'], $values);
+ }
+ }
+ else {
+ unset($seqLinks[$i]);
+ }
+ }
+
+ return $seqLinks;
+ }
+
/**
* Given a string and an array of values, substitute the real values
* in the placeholder in the str in the CiviCRM format
$addressDAO->copyValues($params);
$addressDAO->id = $dao->id;
$addressDAO->save();
- $addressDAO->free();
}
}
$block->is_primary = FALSE;
$block->save();
}
- $block->free();
}
}
}
if (!$dao->fetch()) {
CRM_Core_Error::fatal();
}
- $dao->free();
$fieldValues = array($dao->table_name, $dao->column_name, $dao->id);
$cache->set($cacheKey, $fieldValues);
}
];
}
$sql = "UPDATE `{$customGroup->table_name}` SET `{$customField->column_name}` = REPLACE(`{$customField->column_name}`, %1, %2) WHERE `{$customField->column_name}` LIKE %3";
- $customGroup->free();
CRM_Core_DAO::executeQuery($sql, $params);
}
- $customField->free();
}
}
$entityFileDAO->entity_id = $field['entity_id'];
$entityFileDAO->file_id = $field['file_id'];
$entityFileDAO->save();
- $entityFileDAO->free();
$value = $field['file_id'];
$type = 'String';
break;
$fileDAO = new CRM_Core_DAO_File();
$fileDAO->id = $fileID;
if (!$fileDAO->find(TRUE)) {
- CRM_Core_Error::fatal();
+ throw new CRM_Core_Exception(ts('File not found'));
}
// lets call a pre hook before the delete, so attachments hooks can get the info before things
$entityFileDAO->entity_table = $tableName;
if (!$entityFileDAO->find(TRUE)) {
- CRM_Core_Error::fatal(sprintf('No record found for given file ID - %d and entity ID - %d', $fileID, $entityID));
+ throw new CRM_Core_Exception(sprintf('No record found for given file ID - %d and entity ID - %d', $fileID, $entityID));
}
$entityFileDAO->delete();
],
'replace' => $params,
];
- $copy = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_Job', ['id' => $id], NULL, $fieldsFix);
+ $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_Job', ['id' => $id], NULL, $fieldsFix);
$copy->save();
CRM_Utils_Hook::copy('Job', $copy);
$dao->id = $id;
$dao->find(TRUE);
$dao->delete();
- $dao->free();
}
}
}
* newly created/updated location block id.
*/
public static function copyLocBlock($locBlockId, $updateLocBlockId = NULL) {
+ CRM_Core_Error::deprecatedFunctionWarning('unused function which will be removed');
//get the location info.
$defaults = $updateValues = [];
$locBlock = ['id' => $locBlockId];
}
}
- $copyLocation = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_LocBlock',
+ $copyLocation = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_LocBlock',
['id' => $locBlock['id']],
$copyLocationParams
);
$childNote = new CRM_Core_DAO_Note();
$childNote->id = $childId;
$childNote->delete();
- $childNote->free();
$recent[] = $childId;
}
$return = $note->delete();
- $note->free();
if ($showStatus) {
CRM_Core_Session::setStatus($status, ts('Deleted'), 'success');
}
$optionValue->weight = $opWeight;
$optionValue->save();
}
- $optionValue->free();
}
}
$tags[$tag->id]['color'] = !empty($tag->color) ? $tag->color : NULL;
}
}
- $tag->free();
}
return $tags;
}
}
- $dao->free();
// While we have nodes left to build, shift the first (alphabetically)
// node of the list, place it in our tags list and loop through the
// list of unplaced nodes to find its children. We make a copy to
while ($dao->fetch()) {
$tagSets[$dao->id] = $dao->name;
}
- $dao->free();
return $tagSets;
}
),
);
- $copy = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFGroup',
+ $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFGroup',
array('id' => $id),
NULL,
$fieldsFix
$copy->name = CRM_Utils_String::munge($copy->name, '_', 56) . "_{$copy->id}";
$copy->save();
- $copyUFJoin = &CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
+ $copyUFJoin = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
array('uf_group_id' => $id),
array('uf_group_id' => $copy->id),
NULL,
'entity_table'
);
- $copyUFField = &CRM_Core_DAO::copyGeneric('CRM_Core_BAO_UFField',
+ $copyUFField = CRM_Core_DAO::copyGeneric('CRM_Core_BAO_UFField',
array('uf_group_id' => $id),
array('uf_group_id' => $copy->id)
);
if (!array_key_exists($tableName, $show)) {
$query = "SHOW CREATE TABLE $tableName";
- $dao = CRM_Core_DAO::executeQuery($query);
+ $dao = CRM_Core_DAO::executeQuery($query, [], TRUE, NULL, FALSE, FALSE);
if (!$dao->fetch()) {
CRM_Core_Error::fatal();
foreach ($tables as $tableName) {
if (!array_key_exists($tableName, $show)) {
$query = "SHOW CREATE TABLE $tableName";
- $dao = CRM_Core_DAO::executeQuery($query);
+ $dao = CRM_Core_DAO::executeQuery($query, [], TRUE, NULL, FALSE, FALSE);
if (!$dao->fetch()) {
CRM_Core_Error::fatal();
* Fields that you want to block from.
* getting copied
*
- * @return CRM_Core_DAO
- * the newly created copy of the object
+ * @return CRM_Core_DAO|bool
+ * the newly created copy of the object. False if none created.
*/
- public static function ©Generic($daoName, $criteria, $newData = NULL, $fieldsFix = NULL, $blockCopyOfDependencies = NULL) {
+ public static function copyGeneric($daoName, $criteria, $newData = NULL, $fieldsFix = NULL, $blockCopyOfDependencies = NULL) {
$object = new $daoName();
+ $newObject = FALSE;
if (!$newData) {
$object->id = $criteria['id'];
}
}
}
$newObject->save();
- if (!empty($newData['custom'])) {
- CRM_Core_BAO_CustomValueTable::store($newData['custom'], $newObject::getTableName(), $newObject->id);
- }
+ $newObject->copyCustomFields($object->id, $newObject->id);
CRM_Utils_Hook::post('create', CRM_Core_DAO_AllCoreTables::getBriefName($daoName), $newObject->id, $newObject);
}
return $newObject;
}
+ /**
+ * Method that copies custom fields values from an old entity to a new one.
+ *
+ * Fixes bug CRM-19302,
+ * where if a custom field of File type was present, left both events using the same file,
+ * breaking download URL's for the old event.
+ *
+ * @todo the goal here is to clean this up so that it works for any entity. Copy Generic already DOES some custom field stuff
+ * but it seems to be bypassed & perhaps less good than this (or this just duplicates it...)
+ *
+ * @param int $entityID
+ * @param int $newEntityID
+ */
+ public function copyCustomFields($entityID, $newEntityID) {
+ $entity = CRM_Core_DAO_AllCoreTables::getBriefName(get_class($this));
+ $tableName = CRM_Core_DAO_AllCoreTables::getTableForClass(get_class($this));
+ // Obtain custom values for old event
+ $customParams = $htmlType = [];
+ $customValues = CRM_Core_BAO_CustomValueTable::getEntityValues($entityID, $entity);
+
+ // If custom values present, we copy them
+ if (!empty($customValues)) {
+ // Get Field ID's and identify File type attributes, to handle file copying.
+ $fieldIds = implode(', ', array_keys($customValues));
+ $sql = "SELECT id FROM civicrm_custom_field WHERE html_type = 'File' AND id IN ( {$fieldIds} )";
+ $result = CRM_Core_DAO::executeQuery($sql);
+
+ // Build array of File type fields
+ while ($result->fetch()) {
+ $htmlType[] = $result->id;
+ }
+
+ // Build params array of custom values
+ foreach ($customValues as $field => $value) {
+ if ($value !== NULL) {
+ // Handle File type attributes
+ if (in_array($field, $htmlType)) {
+ $fileValues = CRM_Core_BAO_File::path($value, $entityID);
+ $customParams["custom_{$field}_-1"] = [
+ 'name' => CRM_Utils_File::duplicate($fileValues[0]),
+ 'type' => $fileValues[1],
+ ];
+ }
+ // Handle other types
+ else {
+ $customParams["custom_{$field}_-1"] = $value;
+ }
+ }
+ }
+
+ // Save Custom Fields for new Event
+ CRM_Core_BAO_CustomValueTable::postProcess($customParams, $tableName, $newEntityID, $entity);
+ }
+
+ // copy activity attachments ( if any )
+ CRM_Core_BAO_File::copyEntityFile($tableName, $entityID, $tableName, $newEntityID);
+ }
+
/**
* Cascade update through related entities.
*
if (!empty(\Civi::$statics[__CLASS__]['userFrameworkLogging'])) {
// should call $config->userSystem->logger($message) here - but I got a situation where userSystem was not an object - not sure why
if ($config->userSystem->is_drupal and function_exists('watchdog')) {
- watchdog('civicrm', '%message', ['%message' => $message], WATCHDOG_DEBUG);
+ watchdog('civicrm', '%message', ['%message' => $message], isset($priority) ? $priority : WATCHDOG_DEBUG);
}
}
$label,
$options,
$required,
- NULL
+ ['class' => 'crm-select2']
);
$attributes = ['format' => 'searchDate'];
$extra = ['time' => $isDateTime];
return $this->deleteMessage;
}
+ /**
+ * Set the delete message.
+ *
+ * We do this from the constructor in order to do a translation.
+ */
+ public function setDeleteMessage() {
+ }
+
+ /**
+ * Set entity fields to be assigned to the form.
+ */
+ protected function setEntityFields() {
+ }
+
/**
* Get the entity id being edited.
*
/**
* Calling this from outside the payment subsystem is deprecated - use doPayment.
- *
+ * @deprecated
* Does a server to server payment transaction.
*
* @param array $params
/**
* Process payment - this function wraps around both doTransferCheckout and doDirectPayment.
+ * Any processor that still implements the deprecated doTransferCheckout() or doDirectPayment() should be updated to use doPayment().
*
- * The function ensures an exception is thrown & moves some of this logic out of the form layer and makes the forms
- * more agnostic.
- *
- * Payment processors should set payment_status_id. This function adds some historical defaults ie. the
- * assumption that if a 'doDirectPayment' processors comes back it completed the transaction & in fact
- * doTransferCheckout would not traditionally come back.
- *
- * doDirectPayment does not do an immediate payment for Authorize.net or Paypal so the default is assumed
- * to be Pending.
+ * This function adds some historical defaults ie. the assumption that if a 'doDirectPayment' processors comes back it completed
+ * the transaction & in fact doTransferCheckout would not traditionally come back.
+ * Payment processors should throw exceptions and not return Error objects as they may have done with the old functions.
*
- * Once this function is fully rolled out then it will be preferred for processors to throw exceptions than to
- * return Error objects
+ * Payment processors should set payment_status_id (which is really contribution_status_id) in the returned array. The default is assumed to be Pending.
+ * In some cases the IPN will set the payment to "Completed" some time later.
*
- * Usage:
- * Payment processors should override this function directly instead of using doDirectPayment/doTransferCheckout which are deprecated.
- * Payment processors should set and return payment_status_id (Pending if the IPN will complete it, Completed if successful).
- * @fixme For the contribution workflow we have a contributionID, but for the event and membership workflow the contribution has not yet been created
- * so we can't update params directly on the contribution. However if you return trxn_id, fee_amount, net_amount they will be set on the contribution
- * by those workflows. Ideally all workflows would create a pending contribution BEFORE calling doPayment (eg. https://github.com/civicrm/civicrm-core/pull/13763 for events)
+ * @fixme Creating a contribution record is inconsistent! We should always create a contribution BEFORE calling doPayment...
+ * For the current status see: https://lab.civicrm.org/dev/financial/issues/53
+ * If we DO have a contribution ID, then the payment processor can (and should) update parameters on the contribution record as necessary.
*
* @param array $params
*
* @param string $component
*
* @return array
- * Result array
+ * Result array (containing at least the key payment_status_id)
*
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
if ($options && $flip) {
$options = array_flip($options);
}
- $customField->free();
return $options;
}
// Core field: load schema
$dao = new $daoName();
$fieldSpec = $dao->getFieldSpec($fieldName);
- $dao->free();
// Ensure we have the canonical name for this field
$fieldName = CRM_Utils_Array::value('name', $fieldSpec, $fieldName);
// Get list of fields for the option table
$dao = new $daoName();
$availableFields = array_keys($dao->fieldKeys());
- $dao->free();
$select = "SELECT %1 AS id, %2 AS label";
$from = "FROM %3";
while ($dao->fetch()) {
$output[$dao->id] = $dao->label;
}
- $dao->free();
// Localize results
if (!empty($params['localize']) || $pseudoconstant['table'] == 'civicrm_country' || $pseudoconstant['table'] == 'civicrm_state_province') {
$I18nParams = [];
$items[] = 'js/crm.menubar.js';
$items[] = Civi::service('asset_builder')->getUrl('crm-menubar.css', [
'color' => Civi::settings()->get('menubar_color'),
+ 'height' => 40,
+ 'breakpoint' => 768,
+ 'opacity' => .88,
]);
$items[] = [
'menubar' => [
}
$vars = [
'resourceBase' => rtrim($config->resourceBase, '/'),
+ 'menubarHeight' => $e->params['height'] . 'px',
+ 'breakMin' => $e->params['breakpoint'] . 'px',
+ 'breakMax' => ($e->params['breakpoint'] - 1) . 'px',
'menubarColor' => $color,
- 'semiTransparentMenuColor' => 'rgba(' . implode(', ', CRM_Utils_Color::getRgb($color)) . ', .85)',
+ 'menuItemColor' => 'rgba(' . implode(', ', CRM_Utils_Color::getRgb($color)) . ", {$e->params['opacity']})",
'highlightColor' => CRM_Utils_Color::getHighlight($color),
'textColor' => CRM_Utils_Color::getContrast($color, '#333', '#ddd'),
];
* - Does a force merge otherwise (aggressive mode).
*
* @param array $conflicts
+ * An empty array to be filed with conflict information.
*
* @return bool
*/
public static function skipMerge($mainId, $otherId, &$migrationInfo, $mode = 'safe', &$conflicts = []) {
- $originalMigrationInfo = $migrationInfo;
- foreach ($migrationInfo as $key => $val) {
- if ($val === "null") {
- // Rule: Never overwrite with an empty value (in any mode)
- unset($migrationInfo[$key]);
- continue;
- }
- elseif ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) or
- substr($key, 0, 12) == 'move_custom_'
- ) and $val != NULL
- ) {
- // Rule: If both main-contact, and other-contact have a field with a
- // different value, then let $mode decide if to merge it or not
- if (
- (!empty($migrationInfo['rows'][$key]['main'])
- // For custom fields a 0 (e.g in an int field) could be a true conflict. This
- // is probably true for other fields too - e.g. 'do_not_email' but
- // leaving that investigation as a @todo - until tests can be written.
- // Note the handling of this has test coverage - although the data-typing
- // of '0' feels flakey we have insurance.
- || ($migrationInfo['rows'][$key]['main'] === '0' && substr($key, 0, 12) == 'move_custom_')
- )
- && $migrationInfo['rows'][$key]['main'] != $migrationInfo['rows'][$key]['other']
- ) {
-
- // note it down & lets wait for response from the hook.
- // For no response $mode will decide if to skip this merge
- $conflicts[$key] = NULL;
- }
- }
- elseif (substr($key, 0, 14) == 'move_location_' and $val != NULL) {
- $locField = explode('_', $key);
- $fieldName = $locField[2];
- $fieldCount = $locField[3];
-
- // Rule: Catch address conflicts (same address type on both contacts)
- if (
- isset($migrationInfo['main_details']['location_blocks'][$fieldName]) &&
- !empty($migrationInfo['main_details']['location_blocks'][$fieldName])
- ) {
-
- // Load the address we're inspecting from the 'other' contact
- $addressRecord = $migrationInfo['other_details']['location_blocks'][$fieldName][$fieldCount];
- $addressRecordLocTypeId = CRM_Utils_Array::value('location_type_id', $addressRecord);
-
- // 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::locationIsSame($addressRecord, $mainAddressRecord)) {
- unset($migrationInfo[$key]);
- break;
- }
- elseif ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) {
- $conflicts[$key] = NULL;
- break;
- }
- }
- }
-
- // For other locations, don't merge/add if the values are the same
- elseif (CRM_Utils_Array::value('main', $migrationInfo['rows'][$key]) == $migrationInfo['rows'][$key]['other']) {
- unset($migrationInfo[$key]);
- }
- }
- }
-
- // A hook to implement other algorithms for choosing which contact to bias to when
- // there's a conflict (to handle "gotchas"). fields_in_conflict could be modified here
- // merge happens with new values filled in here. For a particular field / row not to be merged
- // field should be unset from fields_in_conflict.
- $migrationData = [
- 'old_migration_info' => $originalMigrationInfo,
- 'mode' => $mode,
- 'fields_in_conflict' => $conflicts,
- 'merge_mode' => $mode,
- 'migration_info' => $migrationInfo,
- ];
- CRM_Utils_Hook::merge('batch', $migrationData, $mainId, $otherId);
- $conflicts = $migrationData['fields_in_conflict'];
- // allow hook to override / manipulate migrationInfo as well
- $migrationInfo = $migrationData['migration_info'];
+ $conflicts = self::getConflicts($migrationInfo, $mainId, $otherId, $mode);
if (!empty($conflicts)) {
foreach ($conflicts as $key => $val) {
}
}
// if there are conflicts and mode is aggressive, allow hooks to decide if to skip merges
- if (array_key_exists('skip_merge', $migrationData)) {
- return (bool) $migrationData['skip_merge'];
- }
+ return (bool) $migrationInfo['skip_merge'];
}
return FALSE;
}
return FALSE;
}
- $qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
+ $contactType = $migrationInfo['main_details']['contact_type'];
$relTables = CRM_Dedupe_Merger::relTables();
- $submittedCustomFields = $moveTables = $locationMigrationInfo = $tableOperations = $removeTables = [];
+ $submittedCustomFields = $moveTables = $tableOperations = $removeTables = [];
+ self::swapOutFieldsAffectedByQFZeroBug($migrationInfo);
foreach ($migrationInfo as $key => $value) {
- if ($value == $qfZeroBug) {
- $value = '0';
- }
if (substr($key, 0, 12) == 'move_custom_' && $value != NULL) {
$submitted[substr($key, 5)] = $value;
elseif (in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) && $value != NULL) {
$submitted[substr($key, 5)] = $value;
}
- // Set up initial information for handling migration of location blocks
- elseif (substr($key, 0, 14) == 'move_location_' and $value != NULL) {
- $locationMigrationInfo[$key] = $value;
- }
elseif (substr($key, 0, 15) == 'move_rel_table_' and $value == '1') {
$moveTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']);
if (array_key_exists('operation', $migrationInfo)) {
$removeTables = array_merge($moveTables, $relTables[substr($key, 5)]['tables']);
}
}
- self::mergeLocations($mainId, $otherId, $locationMigrationInfo, $migrationInfo);
+ self::mergeLocations($mainId, $otherId, $migrationInfo);
// **** Do contact related migrations
$customTablesToCopyValues = self::getAffectedCustomTables($submittedCustomFields);
'groupName' => 'postal_greeting',
];
CRM_Core_OptionGroup::lookupValues($submitted, $names, TRUE);
-
// fix custom fields so they're edible by createProfileContact()
- $treeCache = [];
- if (!array_key_exists($migrationInfo['main_details']['contact_type'], $treeCache)) {
- $treeCache[$migrationInfo['main_details']['contact_type']] = CRM_Core_BAO_CustomGroup::getTree(
- $migrationInfo['main_details']['contact_type'],
- NULL,
- NULL,
- -1,
- [],
- NULL,
- TRUE,
- NULL,
- FALSE,
- FALSE
- );
- }
-
- $cFields = [];
- foreach ($treeCache[$migrationInfo['main_details']['contact_type']] as $key => $group) {
- if (!isset($group['fields'])) {
- continue;
- }
- foreach ($group['fields'] as $fid => $field) {
- $cFields[$fid]['attributes'] = $field;
- }
- }
+ $cFields = self::getCustomFieldMetadata($contactType);
if (!isset($submitted)) {
$submitted = [];
}
+ $customFiles = [];
foreach ($submitted as $key => $value) {
- if (substr($key, 0, 7) == 'custom_') {
- $fid = (int) substr($key, 7);
- if (empty($cFields[$fid])) {
- continue;
- }
- $htmlType = $cFields[$fid]['attributes']['html_type'];
- switch ($htmlType) {
- case 'File':
- $customFiles[] = $fid;
- unset($submitted["custom_$fid"]);
- break;
-
- case 'Select Country':
- case 'Select State/Province':
- $submitted[$key] = CRM_Core_BAO_CustomField::displayValue($value, $fid);
- break;
-
- case 'Select Date':
- if ($cFields[$fid]['attributes']['is_view']) {
- $submitted[$key] = date('YmdHis', strtotime($submitted[$key]));
- }
- break;
-
- case 'CheckBox':
- case 'Multi-Select':
- case 'Multi-Select Country':
- case 'Multi-Select State/Province':
- // Merge values from both contacts for multivalue fields, CRM-4385
- // get the existing custom values from db.
- $customParams = ['entityID' => $mainId, $key => TRUE];
- $customfieldValues = CRM_Core_BAO_CustomValueTable::getValues($customParams);
- if (!empty($customfieldValues[$key])) {
- $existingValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $customfieldValues[$key]);
- if (is_array($existingValue) && !empty($existingValue)) {
- $mergeValue = $submittedCustomFields = [];
- if ($value == 'null') {
- // CRM-19074 if someone has deliberately chosen to overwrite with 'null', respect it.
- $submitted[$key] = $value;
- }
- else {
- if ($value) {
- $submittedCustomFields = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
- }
-
- // CRM-19653: overwrite or add the existing custom field value with dupicate contact's
- // custom field value stored at $submittedCustomValue.
- foreach ($submittedCustomFields as $k => $v) {
- if ($v != '' && !in_array($v, $mergeValue)) {
- $mergeValue[] = $v;
- }
- }
-
- //keep state and country as array format.
- //for checkbox and m-select format w/ VALUE_SEPARATOR
- if (in_array($htmlType, [
- 'CheckBox',
- 'Multi-Select',
- ])) {
- $submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
- $mergeValue
- ) . CRM_Core_DAO::VALUE_SEPARATOR;
- }
- else {
- $submitted[$key] = $mergeValue;
- }
- }
- }
- }
- elseif (in_array($htmlType, [
- 'Multi-Select Country',
- 'Multi-Select State/Province',
- ])) {
- //we require submitted values should be in array format
- if ($value) {
- $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
- //hack to remove null values from array.
- $mergeValue = [];
- foreach ($mergeValueArray as $k => $v) {
- if ($v != '') {
- $mergeValue[] = $v;
- }
- }
- $submitted[$key] = $mergeValue;
- }
- }
- break;
-
- default:
- break;
- }
- }
+ list($cFields, $customFiles, $submitted) = self::processCustomFields($mainId, $key, $cFields, $customFiles, $submitted, $value);
}
- // **** Do file custom fields related migrations
- // FIXME: move this someplace else (one of the BAOs) after discussing
- // where to, and whether CRM_Core_BAO_File::deleteFileReferences() shouldn't actually,
- // like, delete a file...
-
- if (!isset($customFiles)) {
- $customFiles = [];
- }
- foreach ($customFiles as $customId) {
- list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField::getTableColumnGroup($customId);
-
- // get the contact_id -> file_id mapping
- $fileIds = [];
- $sql = "SELECT entity_id, {$columnName} AS file_id FROM {$tableName} WHERE entity_id IN ({$mainId}, {$otherId})";
- $dao = CRM_Core_DAO::executeQuery($sql);
- while ($dao->fetch()) {
- $fileIds[$dao->entity_id] = $dao->file_id;
- if ($dao->entity_id == $mainId) {
- CRM_Core_BAO_File::deleteFileReferences($fileIds[$mainId], $mainId, $customId);
- }
- }
-
- // move the other contact's file to main contact
- //NYSS need to INSERT or UPDATE depending on whether main contact has an existing record
- if (CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}")) {
- $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
- }
- else {
- $sql = "INSERT INTO {$tableName} ( entity_id, {$columnName} ) VALUES ( {$mainId}, {$fileIds[$otherId]} )";
- }
- CRM_Core_DAO::executeQuery($sql);
-
- if (CRM_Core_DAO::singleValueQuery("
- SELECT id
- FROM civicrm_entity_file
- WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}")
- ) {
- $sql = "
- UPDATE civicrm_entity_file
- SET entity_id = {$mainId}
- WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}";
- }
- else {
- $sql = "
- INSERT INTO civicrm_entity_file ( entity_table, entity_id, file_id )
- VALUES ( '{$tableName}', {$mainId}, {$fileIds[$otherId]} )";
- }
- CRM_Core_DAO::executeQuery($sql);
- }
+ self::processCustomFieldFiles($mainId, $otherId, $customFiles);
// move view only custom fields CRM-5362
$viewOnlyCustomFields = [];
*
* @param int $mainId
* @param int $otherId
- * @param array $locationMigrationInfo
- * Portion of the migration_info that holds location migration information.
*
* @param array $migrationInfo
* Migration info for the merge. This is passed to the hook as informational only.
*/
- public static function mergeLocations($mainId, $otherId, $locationMigrationInfo, $migrationInfo) {
- foreach ($locationMigrationInfo as $key => $value) {
+ public static function mergeLocations($mainId, $otherId, $migrationInfo) {
+ foreach ($migrationInfo as $key => $value) {
+ $isLocationField = (substr($key, 0, 14) == 'move_location_' and $value != NULL);
+ if (!$isLocationField) {
+ continue;
+ }
$locField = explode('_', $key);
$fieldName = $locField[2];
$fieldCount = $locField[3];
}
}
+ /**
+ * Replace the pseudo QFKey with zero if it is present.
+ *
+ * @todo - on the slim chance this is still relevant it should be moved to the form layer.
+ *
+ * Details about this bug are somewhat obscured by the move from svn but perhaps JIRA
+ * can still help.
+ *
+ * @param array $migrationInfo
+ */
+ protected static function swapOutFieldsAffectedByQFZeroBug(&$migrationInfo) {
+ $qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
+ foreach ($migrationInfo as $key => &$value) {
+ if ($value == $qfZeroBug) {
+ $value = '0';
+ }
+ }
+ }
+
+ /**
+ * Honestly - what DOES this do - hopefully some refactoring will reveal it's purpose.
+ *
+ * @param $mainId
+ * @param $key
+ * @param $cFields
+ * @param $customFiles
+ * @param $submitted
+ * @param $value
+ *
+ * @return array
+ */
+ protected static function processCustomFields($mainId, $key, $cFields, $customFiles, $submitted, $value) {
+ if (substr($key, 0, 7) == 'custom_') {
+ $fid = (int) substr($key, 7);
+ if (empty($cFields[$fid])) {
+ return [$cFields, $customFiles, $submitted];
+ }
+ $htmlType = $cFields[$fid]['attributes']['html_type'];
+ switch ($htmlType) {
+ case 'File':
+ $customFiles[] = $fid;
+ unset($submitted["custom_$fid"]);
+ break;
+
+ case 'Select Country':
+ case 'Select State/Province':
+ $submitted[$key] = CRM_Core_BAO_CustomField::displayValue($value, $fid);
+ break;
+
+ case 'Select Date':
+ if ($cFields[$fid]['attributes']['is_view']) {
+ $submitted[$key] = date('YmdHis', strtotime($submitted[$key]));
+ }
+ break;
+
+ case 'CheckBox':
+ case 'Multi-Select':
+ case 'Multi-Select Country':
+ case 'Multi-Select State/Province':
+ // Merge values from both contacts for multivalue fields, CRM-4385
+ // get the existing custom values from db.
+ $customParams = ['entityID' => $mainId, $key => TRUE];
+ $customfieldValues = CRM_Core_BAO_CustomValueTable::getValues($customParams);
+ if (!empty($customfieldValues[$key])) {
+ $existingValue = explode(CRM_Core_DAO::VALUE_SEPARATOR, $customfieldValues[$key]);
+ if (is_array($existingValue) && !empty($existingValue)) {
+ $mergeValue = $submittedCustomFields = [];
+ if ($value == 'null') {
+ // CRM-19074 if someone has deliberately chosen to overwrite with 'null', respect it.
+ $submitted[$key] = $value;
+ }
+ else {
+ if ($value) {
+ $submittedCustomFields = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
+ }
+
+ // CRM-19653: overwrite or add the existing custom field value with dupicate contact's
+ // custom field value stored at $submittedCustomValue.
+ foreach ($submittedCustomFields as $k => $v) {
+ if ($v != '' && !in_array($v, $mergeValue)) {
+ $mergeValue[] = $v;
+ }
+ }
+
+ //keep state and country as array format.
+ //for checkbox and m-select format w/ VALUE_SEPARATOR
+ if (in_array($htmlType, [
+ 'CheckBox',
+ 'Multi-Select',
+ ])) {
+ $submitted[$key] = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR,
+ $mergeValue
+ ) . CRM_Core_DAO::VALUE_SEPARATOR;
+ }
+ else {
+ $submitted[$key] = $mergeValue;
+ }
+ }
+ }
+ }
+ elseif (in_array($htmlType, [
+ 'Multi-Select Country',
+ 'Multi-Select State/Province',
+ ])) {
+ //we require submitted values should be in array format
+ if ($value) {
+ $mergeValueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
+ //hack to remove null values from array.
+ $mergeValue = [];
+ foreach ($mergeValueArray as $k => $v) {
+ if ($v != '') {
+ $mergeValue[] = $v;
+ }
+ }
+ $submitted[$key] = $mergeValue;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return [$cFields, $customFiles, $submitted];
+ }
+
+ /**
+ * Get metadata for the custom fields for the merge.
+ *
+ * @param string $contactType
+ *
+ * @return array
+ */
+ protected static function getCustomFieldMetadata($contactType) {
+ $treeCache = [];
+ if (!array_key_exists($contactType, $treeCache)) {
+ $treeCache[$contactType] = CRM_Core_BAO_CustomGroup::getTree(
+ $contactType,
+ NULL,
+ NULL,
+ -1,
+ [],
+ NULL,
+ TRUE,
+ NULL,
+ FALSE,
+ FALSE
+ );
+ }
+
+ $cFields = [];
+ foreach ($treeCache[$contactType] as $key => $group) {
+ if (!isset($group['fields'])) {
+ continue;
+ }
+ foreach ($group['fields'] as $fid => $field) {
+ $cFields[$fid]['attributes'] = $field;
+ }
+ }
+ return $cFields;
+ }
+
+ /**
+ * Get conflicts for proposed merge pair.
+ *
+ * @param array $migrationInfo
+ * This is primarily to inform hooks. The can also modify it which feels
+ * pretty fragile to do it here - but it is historical.
+ * @param int $mainId
+ * Main contact with whom merge has to happen.
+ * @param int $otherId
+ * Duplicate contact which would be deleted after merge operation.
+ * @param string $mode
+ * Helps decide how to behave when there are conflicts.
+ * - A 'safe' value skips the merge if there are any un-resolved conflicts.
+ * - Does a force merge otherwise (aggressive mode).
+ *
+ * @return array
+ */
+ public static function getConflicts(&$migrationInfo, $mainId, $otherId, $mode) {
+ $conflicts = [];
+ $originalMigrationInfo = $migrationInfo;
+ foreach ($migrationInfo as $key => $val) {
+ if ($val === "null") {
+ // Rule: Never overwrite with an empty value (in any mode)
+ unset($migrationInfo[$key]);
+ continue;
+ }
+ elseif ((in_array(substr($key, 5), CRM_Dedupe_Merger::getContactFields()) or
+ substr($key, 0, 12) == 'move_custom_'
+ ) and $val != NULL
+ ) {
+ // Rule: If both main-contact, and other-contact have a field with a
+ // different value, then let $mode decide if to merge it or not
+ if (
+ (!empty($migrationInfo['rows'][$key]['main'])
+ // For custom fields a 0 (e.g in an int field) could be a true conflict. This
+ // is probably true for other fields too - e.g. 'do_not_email' but
+ // leaving that investigation as a @todo - until tests can be written.
+ // Note the handling of this has test coverage - although the data-typing
+ // of '0' feels flakey we have insurance.
+ || ($migrationInfo['rows'][$key]['main'] === '0' && substr($key, 0, 12) == 'move_custom_')
+ )
+ && $migrationInfo['rows'][$key]['main'] != $migrationInfo['rows'][$key]['other']
+ ) {
+
+ // note it down & lets wait for response from the hook.
+ // For no response $mode will decide if to skip this merge
+ $conflicts[$key] = NULL;
+ }
+ }
+ elseif (substr($key, 0, 14) == 'move_location_' and $val != NULL) {
+ $locField = explode('_', $key);
+ $fieldName = $locField[2];
+ $fieldCount = $locField[3];
+
+ // Rule: Catch address conflicts (same address type on both contacts)
+ if (
+ isset($migrationInfo['main_details']['location_blocks'][$fieldName]) &&
+ !empty($migrationInfo['main_details']['location_blocks'][$fieldName])
+ ) {
+
+ // Load the address we're inspecting from the 'other' contact
+ $addressRecord = $migrationInfo['other_details']['location_blocks'][$fieldName][$fieldCount];
+ $addressRecordLocTypeId = CRM_Utils_Array::value('location_type_id', $addressRecord);
+
+ // 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::locationIsSame($addressRecord, $mainAddressRecord)) {
+ unset($migrationInfo[$key]);
+ break;
+ }
+ elseif ($addressRecordLocTypeId == $mainAddressRecord['location_type_id']) {
+ $conflicts[$key] = NULL;
+ break;
+ }
+ }
+ }
+
+ // For other locations, don't merge/add if the values are the same
+ elseif (CRM_Utils_Array::value('main', $migrationInfo['rows'][$key]) == $migrationInfo['rows'][$key]['other']) {
+ unset($migrationInfo[$key]);
+ }
+ }
+ }
+
+ // A hook to implement other algorithms for choosing which contact to bias to when
+ // there's a conflict (to handle "gotchas"). fields_in_conflict could be modified here
+ // merge happens with new values filled in here. For a particular field / row not to be merged
+ // field should be unset from fields_in_conflict.
+ $migrationData = [
+ 'old_migration_info' => $originalMigrationInfo,
+ 'mode' => $mode,
+ 'fields_in_conflict' => $conflicts,
+ 'merge_mode' => $mode,
+ 'migration_info' => $migrationInfo,
+ ];
+ CRM_Utils_Hook::merge('batch', $migrationData, $mainId, $otherId);
+ $conflicts = $migrationData['fields_in_conflict'];
+ // allow hook to override / manipulate migrationInfo as well
+ $migrationInfo = $migrationData['migration_info'];
+ $migrationInfo['skip_merge'] = CRM_Utils_Array::value('skip_merge', $migrationData);
+ return $conflicts;
+ }
+
+ /**
+ * Do file custom fields related migrations.
+ * FIXME: move this someplace else (one of the BAOs) after discussing
+ * where to, and whether CRM_Core_BAO_File::deleteFileReferences() shouldn't actually,
+ * like, delete a file...
+ *
+ * Note outstanding bug https://lab.civicrm.org/dev/core/issues/723
+ * relates to this code....
+ *
+ * @param $mainId
+ * @param $otherId
+ * @param $customFiles
+ */
+ protected static function processCustomFieldFiles($mainId, $otherId, $customFiles) {
+ foreach ($customFiles as $customId) {
+ list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField::getTableColumnGroup($customId);
+
+ // get the contact_id -> file_id mapping
+ $fileIds = [];
+ $sql = "SELECT entity_id, {$columnName} AS file_id FROM {$tableName} WHERE entity_id IN ({$mainId}, {$otherId})";
+ $dao = CRM_Core_DAO::executeQuery($sql);
+ while ($dao->fetch()) {
+ $fileIds[$dao->entity_id] = $dao->file_id;
+ if ($dao->entity_id == $mainId) {
+ CRM_Core_BAO_File::deleteFileReferences($fileIds[$mainId], $mainId, $customId);
+ }
+ }
+
+ // move the other contact's file to main contact
+ //NYSS need to INSERT or UPDATE depending on whether main contact has an existing record
+ if (CRM_Core_DAO::singleValueQuery("SELECT id FROM {$tableName} WHERE entity_id = {$mainId}")) {
+ $sql = "UPDATE {$tableName} SET {$columnName} = {$fileIds[$otherId]} WHERE entity_id = {$mainId}";
+ }
+ else {
+ $sql = "INSERT INTO {$tableName} ( entity_id, {$columnName} ) VALUES ( {$mainId}, {$fileIds[$otherId]} )";
+ }
+ CRM_Core_DAO::executeQuery($sql);
+
+ if (CRM_Core_DAO::singleValueQuery("
+ SELECT id
+ FROM civicrm_entity_file
+ WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}")
+ ) {
+ $sql = "
+ UPDATE civicrm_entity_file
+ SET entity_id = {$mainId}
+ WHERE entity_table = '{$tableName}' AND file_id = {$fileIds[$otherId]}";
+ }
+ else {
+ $sql = "
+ INSERT INTO civicrm_entity_file ( entity_table, entity_id, file_id )
+ VALUES ( '{$tableName}', {$mainId}, {$fileIds[$otherId]} )";
+ }
+ CRM_Core_DAO::executeQuery($sql);
+ }
+ }
+
}
['entity_value' => $id, 'mapping_id' => $oldMapping->getId()],
['entity_value' => $copyEvent->id, 'mapping_id' => $copyMapping->getId()]
);
- self::copyCustomFields($id, $copyEvent->id);
$copyEvent->save();
return $copyEvent;
}
- /**
- * Method that copies custom fields values from an old event to a new one. Fixes bug CRM-19302,
- * where if a custom field of File type was present, left both events using the same file,
- * breaking download URL's for the old event.
- *
- * @param int $oldEventID
- * @param int $newCopyID
- */
- public static function copyCustomFields($oldEventID, $newCopyID) {
- // Obtain custom values for old event
- $customParams = $htmlType = [];
- $customValues = CRM_Core_BAO_CustomValueTable::getEntityValues($oldEventID, 'Event');
-
- // If custom values present, we copy them
- if (!empty($customValues)) {
- // Get Field ID's and identify File type attributes, to handle file copying.
- $fieldIds = implode(', ', array_keys($customValues));
- $sql = "SELECT id FROM civicrm_custom_field WHERE html_type = 'File' AND id IN ( {$fieldIds} )";
- $result = CRM_Core_DAO::executeQuery($sql);
-
- // Build array of File type fields
- while ($result->fetch()) {
- $htmlType[] = $result->id;
- }
-
- // Build params array of custom values
- foreach ($customValues as $field => $value) {
- if ($value !== NULL) {
- // Handle File type attributes
- if (in_array($field, $htmlType)) {
- $fileValues = CRM_Core_BAO_File::path($value, $oldEventID);
- $customParams["custom_{$field}_-1"] = [
- 'name' => CRM_Utils_File::duplicate($fileValues[0]),
- 'type' => $fileValues[1],
- ];
- }
- // Handle other types
- else {
- $customParams["custom_{$field}_-1"] = $value;
- }
- }
- }
-
- // Save Custom Fields for new Event
- CRM_Core_BAO_CustomValueTable::postProcess($customParams, 'civicrm_event', $newCopyID, 'Event');
- }
-
- // copy activity attachments ( if any )
- CRM_Core_BAO_File::copyEntityFile('civicrm_event', $oldEventID, 'civicrm_event', $newCopyID);
- }
-
/**
* This is sometimes called in a loop (during event search).
*
}
}
+ // Validate if participant is already registered
+ if ($event_in_cart->event->allow_same_participant_emails) {
+ continue;
+ }
+
foreach ($event_in_cart->participants as $mer_participant) {
$participant_fields = $fields['event'][$event_in_cart->event_id]['participant'][$mer_participant->id];
//TODO what to do when profile responses differ for the same contact?
);
$this->assign('pay_later_instructions', $this->pay_later_receipt);
}
+
+ // Event Cart does not support multiple payment processors
+ // so we cannot call $this->preProcessPaymentOptions();
+ CRM_Financial_Form_Payment::addCreditCardJs($this->_paymentProcessor['id']);
}
/**
$send_template_params = [
'table' => 'civicrm_msg_template',
'contactId' => $this->payer_contact_id,
- 'from' => CRM_Core_BAO_Domain::getNameAndEmail(TRUE, TRUE),
+ 'from' => current(CRM_Core_BAO_Domain::getNameAndEmail(TRUE, TRUE)),
'groupName' => 'msg_tpl_workflow_event',
'isTest' => FALSE,
'toEmail' => $contact_details[1],
$ctype,
TRUE
);
+
+ $params['contact_id'] = $this->payer_contact_id;
}
$params['now'] = date('YmdHis');
$this->_searchResult = CRM_Utils_Request::retrieve('searchResult', 'Boolean', $this);
$whereClause = $this->whereClause($params, FALSE, $this->_force);
- $this->pagerAToZ($whereClause, $params);
+
+ if (CRM_Core_Config::singleton()->includeAlphabeticalPager) {
+ $this->pagerAToZ($whereClause, $params);
+ }
$params = [];
$whereClause = $this->whereClause($params, TRUE, $this->_force);
*/
public static function createTempTable($sqlColumns) {
//creating a temporary table for the search result that need be exported
- $exportTempTable = CRM_Utils_SQL_TempTable::build()->setDurable()->setCategory('export')->setUtf8();
+ $exportTempTable = CRM_Utils_SQL_TempTable::build()->setDurable()->setCategory('export');
// also create the sql table
$exportTempTable->drop();
'currency' => $this->format($itemDAO->currency),
];
} // end items loop
- $itemDAO->free();
}
else {
// In this case, split record just uses the FROM account from the trxn, and there's only one record here
// should be handled through Payment.create.
$isSkipRecordingPaymentHereForLegacyHandlingReasons = ($contributionStatus == 'Pending' && $isPaymentCompletesContribution);
- if (!$isSkipRecordingPaymentHereForLegacyHandlingReasons) {
+ if (!$isSkipRecordingPaymentHereForLegacyHandlingReasons && $params['total_amount'] > 0) {
$trxn = CRM_Contribute_BAO_Contribution::recordPartialPayment($contribution, $params);
if (CRM_Utils_Array::value('line_item', $params) && !empty($trxn)) {
CRM_Contribute_BAO_Contribution::assignProportionalLineItems($params, $trxn->id, $contribution['total_amount']);
}
}
+ elseif ($params['total_amount'] < 0) {
+ $trxn = self::recordRefundPayment($params['contribution_id'], $params, FALSE);
+ }
if ($isPaymentCompletesContribution) {
civicrm_api3('Contribution', 'completetransaction', ['id' => $contribution['id']]);
$arAccountId = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($contributionDAO->financial_type_id, 'Accounts Receivable Account is');
$completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
- $trxnData['total_amount'] = $trxnData['net_amount'] = -$trxnData['total_amount'];
+ $trxnData['total_amount'] = $trxnData['net_amount'] = $trxnData['total_amount'];
$trxnData['from_financial_account_id'] = $arAccountId;
$trxnData['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Refunded');
// record the entry
*/
class CRM_Group_Form_Edit extends CRM_Core_Form {
+ use CRM_Core_Form_EntityFormTrait;
+
/**
* The group id, used when editing a group
*
*/
protected $_groupOrganizationID;
+ /**
+ * Set entity fields to be assigned to the form.
+ */
+ protected function setEntityFields() {
+ $this->entityFields = [
+ 'title' => [
+ 'name' => 'title',
+ 'required' => TRUE,
+ ],
+ 'description' => ['name' => 'description'],
+ ];
+ }
+
+ /**
+ * Set the delete message.
+ *
+ * We do this from the constructor in order to do a translation.
+ */
+ public function setDeleteMessage() {
+ $this->deleteMessage = '';
+ }
+
+ /**
+ * Explicitly declare the entity api name.
+ */
+ public function getDefaultEntity() {
+ return 'Group';
+ }
+
/**
* Set up variables to build the form.
*/
* Build the form object.
*/
public function buildQuickForm() {
- if ($this->_action == CRM_Core_Action::DELETE) {
- $this->addButtons(array(
- array(
- 'type' => 'next',
- 'name' => ts('Delete Group'),
- 'isDefault' => TRUE,
- ),
- array(
- 'type' => 'cancel',
- 'name' => ts('Cancel'),
- ),
- ));
+ self::buildQuickEntityForm();
+ if ($this->_action & CRM_Core_Action::DELETE) {
return;
}
$this->preventAjaxSubmit();
}
- $this->applyFilter('__ALL__', 'trim');
- $this->add('text', 'title', ts('Name') . ' ',
- CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title'), TRUE
- );
-
- $this->add('textarea', 'description', ts('Description') . ' ',
- CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'description')
- );
-
$groupTypes = CRM_Core_OptionGroup::values('group_type', TRUE);
if (isset($this->_id) && !empty($this->_groupValues['saved_search_id'])) {
//build custom data
CRM_Custom_Form_CustomData::buildQuickForm($this);
- $this->addButtons(array(
- array(
- 'type' => 'upload',
- 'name' => ($this->_action == CRM_Core_Action::ADD) ? ts('Continue') : ts('Save'),
- 'isDefault' => TRUE,
- ),
- array(
- 'type' => 'cancel',
- 'name' => ts('Cancel'),
- ),
- ));
-
$doParentCheck = FALSE;
if (CRM_Core_Permission::isMultisiteEnabled()) {
$doParentCheck = ($this->_id && CRM_Core_BAO_Domain::isDomainGroup($this->_id)) ? FALSE : TRUE;
* @inheritDoc
*/
public function checkActive(\Civi\Token\TokenProcessor $processor) {
- return !empty($processor->context['mailingId']) || !empty($processor->context['mailing']);
+ return !empty($processor->context['mailingId']) || !empty($processor->context['mailing'])
+ || in_array('mailingId', $processor->context['schema']) || in_array('mailing', $processor->context['schema']);
}
/**
// replaceSubscribeInviteTokens().
if (empty($row->context['mailingJobId']) || empty($row->context['mailingActionTarget']['hash'])) {
- throw new \CRM_Core_Exception("Error: Cannot use action tokens unless context defines mailingJobId and mailingActionTarget.");
+ // Strictly speaking, it doesn't make much sense to generate action-tokens when there's no job ID, but traditional CiviMail
+ // does this in v5.6+ for "Preview" functionality. Relaxing this strictness check ensures parity between newer+older styles.
+ // throw new \CRM_Core_Exception("Error: Cannot use action tokens unless context defines mailingJobId and mailingActionTarget.");
}
if ($field === 'eventQueueId') {
$numSlices = count($embed_data);
$url = '';
for ($i = 0; $i < $numSlices; $i++) {
+ $embed_url_data = parse_url($embed_data[$i]);
+ if (!empty($embed_url_data['scheme'])) {
+ $token_a['embed_parts'][$i] = preg_replace("/href=\"(https*:\/\/)/", "href=\"", $token_a['embed_parts'][$i]);
+ }
$url .= "{$token_a['embed_parts'][$i]}{$embed_data[$i]}";
}
if (isset($token_a['embed_parts'][$numSlices])) {
* A page for mailing preview.
*/
class CRM_Mailing_Page_View extends CRM_Core_Page {
+
+ /**
+ * @var Signal to Flexmailer that this version of the class is usable.
+ */
+ const USES_MAILING_PREVIEW_API = 1;
+
protected $_mailingID;
protected $_mailing;
protected $_contactID;
return NULL;
}
- CRM_Mailing_BAO_Mailing::tokenReplace($this->_mailing);
-
- // get and format attachments
- $attachments = CRM_Core_BAO_File::getEntityFile('civicrm_mailing',
- $this->_mailing->id
- );
-
- // get contact detail and compose if contact id exists
- $returnProperties = $this->_mailing->getReturnProperties();
- if (isset($this->_contactID)) {
- // get details of contact with token value including Custom Field Token Values.CRM-3734
- $params = ['contact_id' => $this->_contactID];
- $details = CRM_Utils_Token::getTokenDetails($params,
- $returnProperties,
- FALSE, TRUE, NULL,
- $this->_mailing->getFlattenedTokens(),
- get_class($this)
- );
- $details = $details[0][$this->_contactID];
- $contactId = $this->_contactID;
- }
- else {
- // get tokens that are not contact specific resolved
- $params = ['contact_id' => 0];
- $details = CRM_Utils_Token::getAnonymousTokenDetails($params,
- $returnProperties,
- TRUE, TRUE, NULL,
- $this->_mailing->getFlattenedTokens(),
- get_class($this)
- );
-
- $details = CRM_Utils_Array::value(0, $details[0]);
- $contactId = 0;
- }
- $mime = $this->_mailing->compose(NULL, NULL, NULL, $contactId,
- $this->_mailing->from_email,
- $this->_mailing->from_email,
- TRUE, $details, $attachments
- );
+ $contactId = isset($this->_contactID) ? $this->_contactID : 0;
+
+ $result = civicrm_api3('Mailing', 'preview', [
+ 'id' => $this->_mailingID,
+ 'contact_id' => $contactId,
+ ]);
+ $mailing = \CRM_Utils_Array::value('values', $result);
$title = NULL;
- if (isset($this->_mailing->body_html) && empty($_GET['text'])) {
+ if (isset($mailing['body_html']) && empty($_GET['text'])) {
$header = 'text/html; charset=utf-8';
- $content = $mime->getHTMLBody();
+ $content = $mailing['body_html'];
if (strpos($content, '<head>') === FALSE && strpos($content, '<title>') === FALSE) {
- $title = '<head><title>' . $this->_mailing->subject . '</title></head>';
+ $title = '<head><title>' . $mailing['subject'] . '</title></head>';
}
}
else {
$header = 'text/plain; charset=utf-8';
- $content = $mime->getTXTBody();
+ $content = $mailing['body_text'];
}
- CRM_Utils_System::setTitle($this->_mailing->subject);
+ CRM_Utils_System::setTitle($mailing['subject']);
if (CRM_Utils_Array::value('snippet', $_GET) === 'json') {
CRM_Core_Page_AJAX::returnJsonResponse($content);
* the column headers that need to be displayed
*/
public function &getColumnHeaders($action = NULL, $output = NULL) {
+
if (!isset(self::$_columnHeaders)) {
- self::$_columnHeaders = [
+ $isMultiLingual = CRM_Core_I18n::isMultiLingual();
+ $headers = [
['desc' => ts('Contact Type')],
[
'name' => ts('Name'),
'sort' => 'mailing_name',
'direction' => CRM_Utils_Sort::DONTCARE,
],
- [
+ ];
+
+ // Check to see if languages column should be displayed.
+ if ($isMultiLingual) {
+ $headers[] = [
'name' => ts('Language'),
'sort' => 'language',
'direction' => CRM_Utils_Sort::DONTCARE,
- ],
+ ];
+ }
+ self::$_columnHeaders = array_merge($headers, [
[
'name' => ts('Mailing Subject'),
'sort' => 'mailing_subject',
'direction' => CRM_Utils_Sort::DONTCARE,
],
['desc' => ts('Actions')],
- ];
+ ]);
}
return self::$_columnHeaders;
}
* @inheritDoc
*/
public function checkActive(\Civi\Token\TokenProcessor $processor) {
- return !empty($processor->context['mailingId']) || !empty($processor->context['mailing']);
+ return !empty($processor->context['mailingId']) || !empty($processor->context['mailing'])
+ || in_array('mailingId', $processor->context['schema']) || in_array('mailing', $processor->context['schema']);
}
/**
CRM_Price_BAO_PriceSet::addTo($baoName, $newId, $copyPriceSet->id);
}
else {
- $copyPriceSet = &CRM_Core_DAO::copyGeneric('CRM_Price_DAO_PriceSetEntity',
+ $copyPriceSet = CRM_Core_DAO::copyGeneric('CRM_Price_DAO_PriceSetEntity',
[
'entity_id' => $id,
'entity_table' => $baoName,
* @return string
*/
public function createTemporaryTable($identifier, $sql, $isColumns = FALSE, $isMemory = FALSE) {
- $tempTable = CRM_Utils_SQL_TempTable::build()->setUtf8();
+ $tempTable = CRM_Utils_SQL_TempTable::build();
if ($isMemory) {
$tempTable->setMemory();
}
$fieldValueMap[$responseField->option_group_id][$responseField->value] = $value;
}
}
- $responseField->free();
//actual data formatting.
$hasData = FALSE;
while ($dao->fetch()) {
$contactIds[] = $dao->cid;
}
- $dao->free();
$this->setPager();
}
$contributionSum = 0;
}
}
- $dao->free();
}
// format result set.
$this->formatDisplay($rows, FALSE);
+++ /dev/null
-<?php
-/*
- +--------------------------------------------------------------------+
- | CiviCRM version 5 |
- +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2019 |
- +--------------------------------------------------------------------+
- | This file is a part of CiviCRM. |
- | |
- | CiviCRM is free software; you can copy, modify, and distribute it |
- | under the terms of the GNU Affero General Public License |
- | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
- | |
- | CiviCRM is distributed in the hope that it will be useful, but |
- | WITHOUT ANY WARRANTY; without even the implied warranty of |
- | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
- | See the GNU Affero General Public License for more details. |
- | |
- | You should have received a copy of the GNU Affero General Public |
- | License and the CiviCRM Licensing Exception along |
- | with this program; if not, contact CiviCRM LLC |
- | at info[AT]civicrm[DOT]org. If you have questions about the |
- | GNU Affero General Public License or the licensing of CiviCRM, |
- | see the CiviCRM license FAQ at http://civicrm.org/licensing |
- +--------------------------------------------------------------------+
- */
-
-/**
- *
- * @package CRM
- * @copyright CiviCRM LLC (c) 2004-2019
- * $Id$
- *
- */
-class CRM_Report_Form_Extended extends CRM_Report_Form {
- protected $_addressField = FALSE;
-
- protected $_emailField = FALSE;
-
- protected $_summary = NULL;
-
- protected $_customGroupExtends = [];
- protected $_baseTable = 'civicrm_contact';
-
- /**
- *
- */
- public function __construct() {
- parent::__construct();
- }
-
- /**
- *
- */
- public function preProcess() {
- parent::preProcess();
- }
-
- /**
- *
- */
- public function select() {
- parent::select();
- }
-
- /**
- * From clause build where baseTable & fromClauses are defined
- */
- public function from() {
- if (!empty($this->_baseTable)) {
- $this->buildACLClause($this->_aliases['civicrm_contact']);
- $this->_from = "FROM {$this->_baseTable} {$this->_aliases[$this->_baseTable]}";
- $availableClauses = $this->getAvailableJoins();
- foreach ($this->fromClauses() as $fromClause) {
- $fn = $availableClauses[$fromClause]['callback'];
- $this->$fn();
- }
- if (strstr($this->_from, 'civicrm_contact')) {
- $this->_from .= $this->_aclFrom;
- }
- }
- }
-
- /**
- * Define any from clauses in use (child classes to override)
- *
- * @return array
- */
- public function fromClauses() {
- return [];
- }
-
- public function groupBy() {
- parent::groupBy();
- //@todo - need to re-visit this - bad behaviour from pa
- if ($this->_groupBy == 'GROUP BY') {
- $this->_groupBY = NULL;
- }
- // if a stat field has been selected the do a group by
- if (!empty($this->_statFields) && empty($this->_groupBy)) {
- $this->_groupBy[] = $this->_aliases[$this->_baseTable] . ".id";
- }
- //@todo - this should be in the parent function or at parent level - perhaps build query should do this?
- if (!empty($this->_groupBy) && is_array($this->_groupBy)) {
- $this->_groupBy = 'GROUP BY ' . implode(',', $this->_groupBy);
- }
- }
-
- public function orderBy() {
- parent::orderBy();
- }
-
- /**
- * @param array $rows
- *
- * @return array
- */
- public function statistics(&$rows) {
- return parent::statistics($rows);
- }
-
- public function postProcess() {
- if (!empty($this->_aclTable) && !empty($this->_aliases[$this->_aclTable])) {
- $this->buildACLClause($this->_aliases[$this->_aclTable]);
- }
- parent::postProcess();
- }
-
- /**
- * Alter display of rows.
- *
- * Iterate through the rows retrieved via SQL and make changes for display purposes,
- * such as rendering contacts as links.
- *
- * @param array $rows
- * Rows generated by SQL, with an array for each row.
- */
- public function alterDisplay(&$rows) {
- parent::alterDisplay($rows);
-
- //THis is all generic functionality which can hopefully go into the parent class
- // it introduces the option of defining an alter display function as part of the column definition
- // @tod tidy up the iteration so it happens in this function
- list($firstRow) = $rows;
- // no result to alter
- if (empty($firstRow)) {
- return;
- }
- $selectedFields = array_keys($firstRow);
-
- $alterfunctions = $altermap = [];
- foreach ($this->_columns as $tablename => $table) {
- if (array_key_exists('fields', $table)) {
- foreach ($table['fields'] as $field => $specs) {
- if (in_array($tablename . '_' . $field, $selectedFields) &&
- array_key_exists('alter_display', $specs)
- ) {
- $alterfunctions[$tablename . '_' .
- $field] = $specs['alter_display'];
- $altermap[$tablename . '_' . $field] = $field;
- }
- }
- }
- }
- if (empty($alterfunctions)) {
- // - no manipulation to be done
- return;
- }
-
- foreach ($rows as $index => & $row) {
- foreach ($row as $selectedfield => $value) {
- if (array_key_exists($selectedfield, $alterfunctions)) {
- $rows[$index][$selectedfield] = $this->{$alterfunctions[$selectedfield]}($value, $row, $selectedfield, $altermap[$selectedfield]);
- }
- }
- }
- }
-
- /**
- * @return array
- */
- public function getLineItemColumns() {
- return [
- 'civicrm_line_item' => [
- 'dao' => 'CRM_Price_BAO_LineItem',
- 'fields' => [
- 'qty' => [
- 'title' => ts('Quantity'),
- 'type' => CRM_Utils_Type::T_INT,
- 'statistics' => ['sum' => ts('Total Quantity Selected')],
- ],
- 'unit_price' => [
- 'title' => ts('Unit Price'),
- ],
- 'line_total' => [
- 'title' => ts('Line Total'),
- 'type' => CRM_Utils_Type::T_MONEY,
- 'statistics' => ['sum' => ts('Total of Line Items')],
- ],
- ],
- 'participant_count' => [
- 'title' => ts('Participant Count'),
- 'statistics' => ['sum' => ts('Total Participants')],
- ],
- 'filters' => [
- 'qty' => [
- 'title' => ts('Quantity'),
- 'type' => CRM_Utils_Type::T_INT,
- 'operator' => CRM_Report_Form::OP_INT,
- ],
- ],
- 'group_bys' => [
- 'price_field_id' => [
- 'title' => ts('Price Field'),
- ],
- 'price_field_value_id' => [
- 'title' => ts('Price Field Option'),
- ],
- 'line_item_id' => [
- 'title' => ts('Individual Line Item'),
- 'name' => 'id',
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getPriceFieldValueColumns() {
- return [
- 'civicrm_price_field_value' => [
- 'dao' => 'CRM_Price_BAO_PriceFieldValue',
- 'fields' => [
- 'price_field_value_label' => [
- 'title' => ts('Price Field Value Label'),
- 'name' => 'label',
- ],
- ],
- 'filters' => [
- 'price_field_value_label' => [
- 'title' => ts('Price Fields Value Label'),
- 'type' => CRM_Utils_Type::T_STRING,
- 'operator' => 'like',
- 'name' => 'label',
- ],
- ],
- 'order_bys' => [
- 'label' => [
- 'title' => ts('Price Field Value Label'),
- ],
- ],
- //note that we have a requirement to group by label such that all 'Promo book' lines
- 'group_bys' =>
- // are grouped together across price sets but there may be a separate need to group
- // by id so that entries in one price set are distinct from others. Not quite sure what
- // to call the distinction for end users benefit
- [
- 'price_field_value_label' => [
- 'title' => ts('Price Field Value Label'),
- 'name' => 'label',
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getPriceFieldColumns() {
- return [
- 'civicrm_price_field' => [
- 'dao' => 'CRM_Price_BAO_PriceField',
- 'fields' => [
- 'price_field_label' => [
- 'title' => ts('Price Field Label'),
- 'name' => 'label',
- ],
- ],
- 'filters' => [
- 'price_field_label' => [
- 'title' => ts('Price Field Label'),
- 'type' => CRM_Utils_Type::T_STRING,
- 'operator' => 'like',
- 'name' => 'label',
- ],
- ],
- 'order_bys' => [
- 'price_field_label' => [
- 'title' => ts('Price Field Label'),
- 'name' => 'label',
- ],
- ],
- 'group_bys' => [
- 'price_field_label' => [
- 'title' => ts('Price Field Label'),
- 'name' => 'label',
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getParticipantColumns() {
- static $_events = [];
- if (!isset($_events['all'])) {
- CRM_Core_PseudoConstant::populate($_events['all'], 'CRM_Event_DAO_Event', FALSE, 'title', 'is_active', "is_template IS NULL OR is_template = 0", 'end_date DESC');
- }
- return [
- 'civicrm_participant' => [
- 'dao' => 'CRM_Event_DAO_Participant',
- 'fields' => [
- 'participant_id' => ['title' => ts('Participant ID')],
- 'participant_record' => [
- 'name' => 'id',
- 'title' => ts('Participant ID'),
- ],
- 'event_id' => [
- 'title' => ts('Event ID'),
- 'type' => CRM_Utils_Type::T_STRING,
- 'alter_display' => 'alterEventID',
- ],
- 'status_id' => [
- 'title' => ts('Status'),
- 'alter_display' => 'alterParticipantStatus',
- ],
- 'role_id' => [
- 'title' => ts('Role'),
- 'alter_display' => 'alterParticipantRole',
- ],
- 'participant_fee_level' => NULL,
- 'participant_fee_amount' => NULL,
- 'participant_register_date' => ['title' => ts('Registration Date')],
- ],
- 'grouping' => 'event-fields',
- 'filters' => [
- 'event_id' => [
- 'name' => 'event_id',
- 'title' => ts('Event'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => $_events['all'],
- ],
- 'sid' => [
- 'name' => 'status_id',
- 'title' => ts('Participant Status'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label'),
- ],
- 'rid' => [
- 'name' => 'role_id',
- 'title' => ts('Participant Role'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Event_PseudoConstant::participantRole(),
- ],
- 'participant_register_date' => [
- 'title' => ts('Registration Date'),
- 'operatorType' => CRM_Report_Form::OP_DATE,
- ],
- ],
- 'order_bys' => [
- 'event_id' => [
- 'title' => ts('Event'),
- 'default_weight' => '1',
- 'default_order' => 'ASC',
- ],
- ],
- 'group_bys' => [
- 'event_id' => ['title' => ts('Event')],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getMembershipColumns() {
- return [
- 'civicrm_membership' => [
- 'dao' => 'CRM_Member_DAO_Membership',
- 'grouping' => 'member-fields',
- 'fields' => [
- 'membership_type_id' => [
- 'title' => ts('Membership Type'),
- 'required' => TRUE,
- 'alter_display' => 'alterMembershipTypeID',
- ],
- 'status_id' => [
- 'title' => ts('Membership Status'),
- 'required' => TRUE,
- 'alter_display' => 'alterMembershipStatusID',
- ],
- 'join_date' => NULL,
- 'start_date' => [
- 'title' => ts('Current Cycle Start Date'),
- ],
- 'end_date' => [
- 'title' => ts('Current Membership Cycle End Date'),
- ],
- ],
- 'group_bys' => [
- 'membership_type_id' => [
- 'title' => ts('Membership Type'),
- ],
- ],
- 'filters' => [
- 'join_date' => [
- 'type' => CRM_Utils_Type::T_DATE,
- 'operatorType' => CRM_Report_Form::OP_DATE,
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getMembershipTypeColumns() {
- return [
- 'civicrm_membership_type' => [
- 'dao' => 'CRM_Member_DAO_MembershipType',
- 'grouping' => 'member-fields',
- 'filters' => [
- 'gid' => [
- 'name' => 'id',
- 'title' => ts('Membership Types'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'type' => CRM_Utils_Type::T_INT + CRM_Utils_Type::T_ENUM,
- 'options' => CRM_Member_PseudoConstant::membershipType(),
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getEventColumns() {
- return [
- 'civicrm_event' => [
- 'dao' => 'CRM_Event_DAO_Event',
- 'fields' => [
- 'id' => [
- 'no_display' => TRUE,
- 'required' => TRUE,
- ],
- 'title' => [
- 'title' => ts('Event Title'),
- 'required' => TRUE,
- ],
- 'event_type_id' => [
- 'title' => ts('Event Type'),
- 'required' => TRUE,
- 'alter_display' => 'alterEventType',
- ],
- 'fee_label' => ['title' => ts('Fee Label')],
- 'event_start_date' => [
- 'title' => ts('Event Start Date'),
- ],
- 'event_end_date' => ['title' => ts('Event End Date')],
- 'max_participants' => [
- 'title' => ts('Capacity'),
- 'type' => CRM_Utils_Type::T_INT,
- ],
- ],
- 'grouping' => 'event-fields',
- 'filters' => [
- 'event_type_id' => [
- 'name' => 'event_type_id',
- 'title' => ts('Event Type'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Core_OptionGroup::values('event_type'),
- ],
- 'event_title' => [
- 'name' => 'title',
- 'title' => ts('Event Title'),
- 'operatorType' => CRM_Report_Form::OP_STRING,
- ],
- ],
- 'order_bys' => [
- 'event_type_id' => [
- 'title' => ts('Event Type'),
- 'default_weight' => '2',
- 'default_order' => 'ASC',
- ],
- ],
- 'group_bys' => [
- 'event_type_id' => [
- 'title' => ts('Event Type'),
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getContributionColumns() {
- return [
- 'civicrm_contribution' => [
- 'dao' => 'CRM_Contribute_DAO_Contribution',
- 'fields' => [
- 'contribution_id' => [
- 'name' => 'id',
- ],
- 'financial_type_id' => [
- 'title' => ts('Financial Type'),
- 'default' => TRUE,
- 'alter_display' => 'alterContributionType',
- ],
- 'payment_instrument_id' => [
- 'title' => ts('Payment Method'),
- 'alter_display' => 'alterPaymentType',
- ],
- 'source' => ['title' => ts('Contribution Source')],
- 'trxn_id' => NULL,
- 'receive_date' => ['default' => TRUE],
- 'receipt_date' => NULL,
- 'fee_amount' => NULL,
- 'net_amount' => NULL,
- 'total_amount' => [
- 'title' => ts('Amount'),
- 'statistics' => ['sum' => ts('Total Amount')],
- 'type' => CRM_Utils_Type::T_MONEY,
- ],
- ],
- 'filters' => [
- 'receive_date' => ['operatorType' => CRM_Report_Form::OP_DATE],
- 'financial_type_id' => [
- 'title' => ts('Financial Type'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Contribute_PseudoConstant::financialType(),
- ],
- 'payment_instrument_id' => [
- 'title' => ts('Payment Type'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Contribute_PseudoConstant::paymentInstrument(),
- ],
- 'contribution_status_id' => [
- 'title' => ts('Contribution Status'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Contribute_PseudoConstant::contributionStatus(),
- ],
- 'total_amount' => ['title' => ts('Contribution Amount')],
- ],
- 'order_bys' => [
- 'payment_instrument_id' => [
- 'title' => ts('Payment Method'),
- ],
- 'financial_type_id' => [
- 'title' => ts('Financial Type'),
- ],
- ],
- 'group_bys' => [
- 'financial_type_id' => ['title' => ts('Financial Type')],
- 'payment_instrument_id' => ['title' => ts('Payment Method')],
- 'contribution_id' => [
- 'title' => ts('Individual Contribution'),
- 'name' => 'id',
- ],
- 'source' => ['title' => ts('Contribution Source')],
- ],
- 'grouping' => 'contribution-fields',
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getContactColumns() {
- return [
- 'civicrm_contact' => [
- 'dao' => 'CRM_Contact_DAO_Contact',
- 'fields' => [
- 'display_name' => [
- 'title' => ts('Contact Name'),
- ],
- 'id' => [
- 'title' => ts('Contact ID'),
- 'alter_display' => 'alterContactID',
- ],
- 'first_name' => [
- 'title' => ts('First Name'),
- ],
- 'last_name' => [
- 'title' => ts('Last Name'),
- ],
- 'nick_name' => [
- 'title' => ts('Nickname'),
- 'alter_display' => 'alterNickname',
- ],
- ],
- 'filters' => [
- 'id' => [
- 'title' => ts('Contact ID'),
- ],
- 'sort_name' => [
- 'title' => ts('Contact Name'),
- ],
- ],
- 'grouping' => 'contact-fields',
- 'order_bys' => [
- 'sort_name' => [
- 'title' => ts('Last Name, First Name'),
- 'default' => '1',
- 'default_weight' => '0',
- 'default_order' => 'ASC',
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getCaseColumns() {
- return [
- 'civicrm_case' => [
- 'dao' => 'CRM_Case_DAO_Case',
- 'fields' => [
- 'id' => [
- 'title' => ts('Case ID'),
- 'required' => FALSE,
- ],
- 'subject' => [
- 'title' => ts('Case Subject'),
- 'default' => TRUE,
- ],
- 'status_id' => [
- 'title' => ts('Status'),
- 'default' => TRUE,
- ],
- 'case_type_id' => [
- 'title' => ts('Case Type'),
- 'default' => TRUE,
- ],
- 'case_start_date' => [
- 'title' => ts('Case Start Date'),
- 'name' => 'start_date',
- 'default' => TRUE,
- ],
- 'case_end_date' => [
- 'title' => ts('Case End Date'),
- 'name' => 'end_date',
- 'default' => TRUE,
- ],
- 'case_duration' => [
- 'name' => 'duration',
- 'title' => ts('Duration (Days)'),
- 'default' => FALSE,
- ],
- 'case_is_deleted' => [
- 'name' => 'is_deleted',
- 'title' => ts('Case Deleted?'),
- 'default' => FALSE,
- 'type' => CRM_Utils_Type::T_INT,
- ],
- ],
- 'filters' => [
- 'case_start_date' => [
- 'title' => ts('Case Start Date'),
- 'operatorType' => CRM_Report_Form::OP_DATE,
- 'type' => CRM_Utils_Type::T_DATE,
- 'name' => 'start_date',
- ],
- 'case_end_date' => [
- 'title' => ts('Case End Date'),
- 'operatorType' => CRM_Report_Form::OP_DATE,
- 'type' => CRM_Utils_Type::T_DATE,
- 'name' => 'end_date',
- ],
- 'case_type_id' => [
- 'title' => ts('Case Type'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => $this->case_types,
- ],
- 'case_status_id' => [
- 'title' => ts('Case Status'),
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => $this->case_statuses,
- 'name' => 'status_id',
- ],
- 'case_is_deleted' => [
- 'title' => ts('Case Deleted?'),
- 'type' => CRM_Report_Form::OP_INT,
- 'operatorType' => CRM_Report_Form::OP_SELECT,
- 'options' => $this->deleted_labels,
- 'default' => 0,
- 'name' => 'is_deleted',
- ],
- ],
- ],
- ];
- }
-
- /**
- * Get address columns to add to array.
- *
- * @param array $options
- * Options for the report.
- * - prefix prefix to add (e.g. 'honor' when getting address details for honor contact
- * - prefix_label optional prefix lable eg. "Honoree " for front end
- * - group_by enable these fields for group by - default false
- * - order_by enable these fields for order by
- * - filters enable these fields for filtering
- * - defaults - (is this working?) values to pre-populate
- *
- * @return array
- * address columns definition
- */
- public function getAddressColumns($options = []) {
- $defaultOptions = [
- 'prefix' => '',
- 'prefix_label' => '',
- 'group_by' => FALSE,
- 'order_by' => TRUE,
- 'filters' => TRUE,
- 'defaults' => [
- 'country_id' => TRUE,
- ],
- ];
-
- $options = array_merge($defaultOptions, $options);
-
- $addressFields = [
- $options['prefix'] . 'civicrm_address' => [
- 'dao' => 'CRM_Core_DAO_Address',
- 'name' => 'civicrm_address',
- 'alias' => $options['prefix'] . 'civicrm_address',
- 'fields' => [
- $options['prefix'] . 'name' => [
- 'title' => ts($options['prefix_label'] . 'Address Name'),
- 'default' => CRM_Utils_Array::value('name', $options['defaults'], FALSE),
- 'name' => 'name',
- ],
- $options['prefix'] . 'street_address' => [
- 'title' => ts($options['prefix_label'] . 'Street Address'),
- 'default' => CRM_Utils_Array::value('street_address', $options['defaults'], FALSE),
- 'name' => 'street_address',
- ],
- $options['prefix'] . 'supplemental_address_1' => [
- 'title' => ts($options['prefix_label'] .
- 'Supplementary Address Field 1'),
- 'default' => CRM_Utils_Array::value('supplemental_address_1', $options['defaults'], FALSE),
- 'name' => 'supplemental_address_1',
- ],
- $options['prefix'] . 'supplemental_address_2' => [
- 'title' => ts($options['prefix_label'] .
- 'Supplementary Address Field 2'),
- 'default' => CRM_Utils_Array::value('supplemental_address_2', $options['defaults'], FALSE),
- 'name' => 'supplemental_address_2',
- ],
- $options['prefix'] . 'supplemental_address_3' => [
- 'title' => ts($options['prefix_label'] .
- 'Supplementary Address Field 3'),
- 'default' => CRM_Utils_Array::value('supplemental_address_3', $options['defaults'], FALSE),
- 'name' => 'supplemental_address_3',
- ],
- $options['prefix'] . 'street_number' => [
- 'name' => 'street_number',
- 'title' => ts($options['prefix_label'] . 'Street Number'),
- 'type' => 1,
- 'default' => CRM_Utils_Array::value('street_number', $options['defaults'], FALSE),
- ],
- $options['prefix'] . 'street_name' => [
- 'name' => 'street_name',
- 'title' => ts($options['prefix_label'] . 'Street Name'),
- 'type' => 1,
- 'default' => CRM_Utils_Array::value('street_name', $options['defaults'], FALSE),
- ],
- $options['prefix'] . 'street_unit' => [
- 'name' => 'street_unit',
- 'title' => ts($options['prefix_label'] . 'Street Unit'),
- 'type' => 1,
- 'default' => CRM_Utils_Array::value('street_unit', $options['defaults'], FALSE),
- ],
- $options['prefix'] . 'city' => [
- 'title' => ts($options['prefix_label'] . 'City'),
- 'default' => CRM_Utils_Array::value('city', $options['defaults'], FALSE),
- 'name' => 'city',
- ],
- $options['prefix'] . 'postal_code' => [
- 'title' => ts($options['prefix_label'] . 'Postal Code'),
- 'default' => CRM_Utils_Array::value('postal_code', $options['defaults'], FALSE),
- 'name' => 'postal_code',
- ],
- $options['prefix'] . 'county_id' => [
- 'title' => ts($options['prefix_label'] . 'County'),
- 'default' => CRM_Utils_Array::value('county_id', $options['defaults'], FALSE),
- 'alter_display' => 'alterCountyID',
- 'name' => 'county_id',
- ],
- $options['prefix'] . 'state_province_id' => [
- 'title' => ts($options['prefix_label'] . 'State/Province'),
- 'default' => CRM_Utils_Array::value('state_province_id', $options['defaults'], FALSE),
- 'alter_display' => 'alterStateProvinceID',
- 'name' => 'state_province_id',
- ],
- $options['prefix'] . 'country_id' => [
- 'title' => ts($options['prefix_label'] . 'Country'),
- 'default' => CRM_Utils_Array::value('country_id', $options['defaults'], FALSE),
- 'alter_display' => 'alterCountryID',
- 'name' => 'country_id',
- ],
- ],
- 'grouping' => 'location-fields',
- ],
- ];
-
- if ($options['filters']) {
- $addressFields[$options['prefix'] . 'civicrm_address']['filters'] = [
- $options['prefix'] . 'street_number' => [
- 'title' => ts($options['prefix_label'] . 'Street Number'),
- 'type' => 1,
- 'name' => 'street_number',
- ],
- $options['prefix'] . 'street_name' => [
- 'title' => ts($options['prefix_label'] . 'Street Name'),
- 'name' => $options['prefix'] . 'street_name',
- 'operator' => 'like',
- ],
- $options['prefix'] . 'postal_code' => [
- 'title' => ts($options['prefix_label'] . 'Postal Code'),
- 'type' => 1,
- 'name' => 'postal_code',
- ],
- $options['prefix'] . 'city' => [
- 'title' => ts($options['prefix_label'] . 'City'),
- 'operator' => 'like',
- 'name' => 'city',
- ],
- $options['prefix'] . 'county_id' => [
- 'name' => 'county_id',
- 'title' => ts($options['prefix_label'] . 'County'),
- 'type' => CRM_Utils_Type::T_INT,
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Core_PseudoConstant::county(),
- ],
- $options['prefix'] . 'state_province_id' => [
- 'name' => 'state_province_id',
- 'title' => ts($options['prefix_label'] . 'State/Province'),
- 'type' => CRM_Utils_Type::T_INT,
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Core_PseudoConstant::stateProvince(),
- ],
- $options['prefix'] . 'country_id' => [
- 'name' => 'country_id',
- 'title' => ts($options['prefix_label'] . 'Country'),
- 'type' => CRM_Utils_Type::T_INT,
- 'operatorType' => CRM_Report_Form::OP_MULTISELECT,
- 'options' => CRM_Core_PseudoConstant::country(),
- ],
- ];
- }
-
- if ($options['order_by']) {
- $addressFields[$options['prefix'] .
- 'civicrm_address']['order_bys'] = [
- $options['prefix'] . 'street_name' => [
- 'title' => ts($options['prefix_label'] . 'Street Name'),
- 'name' => 'street_name',
- ],
- $options['prefix'] . 'street_number' => [
- 'title' => ts($options['prefix_label'] . 'Odd / Even Street Number'),
- 'name' => 'street_number',
- ],
- $options['prefix'] . 'street_address' => [
- 'title' => ts($options['prefix_label'] . 'Street Address'),
- 'name' => 'street_address',
- ],
- $options['prefix'] . 'city' => [
- 'title' => ts($options['prefix_label'] . 'City'),
- 'name' => 'city',
- ],
- $options['prefix'] . 'postal_code' => [
- 'title' => ts($options['prefix_label'] . 'Post Code'),
- 'name' => 'postal_code',
- ],
- ];
- }
-
- if ($options['group_by']) {
- $addressFields['civicrm_address']['group_bys'] = [
- $options['prefix'] . 'street_address' => [
- 'title' => ts($options['prefix_label'] . 'Street Address'),
- 'name' => 'street_address',
- ],
- $options['prefix'] . 'city' => [
- 'title' => ts($options['prefix_label'] . 'City'),
- 'name' => 'city',
- ],
- $options['prefix'] . 'postal_code' => [
- 'title' => ts($options['prefix_label'] . 'Post Code'),
- 'name' => 'postal_code',
- ],
- $options['prefix'] . 'state_province_id' => [
- 'title' => ts($options['prefix_label'] . 'State/Province'),
- 'name' => 'state_province_id',
- ],
- $options['prefix'] . 'country_id' => [
- 'title' => ts($options['prefix_label'] . 'Country'),
- 'name' => 'country_id',
- ],
- $options['prefix'] . 'county_id' => [
- 'title' => ts($options['prefix_label'] . 'County'),
- 'name' => 'county_id',
- ],
- ];
- }
- return $addressFields;
- }
-
- /**
- * Get Information about advertised Joins.
- *
- * @return array
- */
- public function getAvailableJoins() {
- return [
- 'priceFieldValue_from_lineItem' => [
- 'leftTable' => 'civicrm_line_item',
- 'rightTable' => 'civicrm_price_field_value',
- 'callback' => 'joinPriceFieldValueFromLineItem',
- ],
- 'priceField_from_lineItem' => [
- 'leftTable' => 'civicrm_line_item',
- 'rightTable' => 'civicrm_price_field',
- 'callback' => 'joinPriceFieldFromLineItem',
- ],
- 'participant_from_lineItem' => [
- 'leftTable' => 'civicrm_line_item',
- 'rightTable' => 'civicrm_participant',
- 'callback' => 'joinParticipantFromLineItem',
- ],
- 'contribution_from_lineItem' => [
- 'leftTable' => 'civicrm_line_item',
- 'rightTable' => 'civicrm_contribution',
- 'callback' => 'joinContributionFromLineItem',
- ],
- 'membership_from_lineItem' => [
- 'leftTable' => 'civicrm_line_item',
- 'rightTable' => 'civicrm_membership',
- 'callback' => 'joinMembershipFromLineItem',
- ],
- 'contribution_from_participant' => [
- 'leftTable' => 'civicrm_participant',
- 'rightTable' => 'civicrm_contribution',
- 'callback' => 'joinContributionFromParticipant',
- ],
- 'contribution_from_membership' => [
- 'leftTable' => 'civicrm_membership',
- 'rightTable' => 'civicrm_contribution',
- 'callback' => 'joinContributionFromMembership',
- ],
- 'membership_from_contribution' => [
- 'leftTable' => 'civicrm_contribution',
- 'rightTable' => 'civicrm_membership',
- 'callback' => 'joinMembershipFromContribution',
- ],
- 'membershipType_from_membership' => [
- 'leftTable' => 'civicrm_membership',
- 'rightTable' => 'civicrm_membership_type',
- 'callback' => 'joinMembershipTypeFromMembership',
- ],
- 'lineItem_from_contribution' => [
- 'leftTable' => 'civicrm_contribution',
- 'rightTable' => 'civicrm_line_item',
- 'callback' => 'joinLineItemFromContribution',
- ],
- 'lineItem_from_membership' => [
- 'leftTable' => 'civicrm_membership',
- 'rightTable' => 'civicrm_line_item',
- 'callback' => 'joinLineItemFromMembership',
- ],
- 'contact_from_participant' => [
- 'leftTable' => 'civicrm_participant',
- 'rightTable' => 'civicrm_contact',
- 'callback' => 'joinContactFromParticipant',
- ],
- 'contact_from_membership' => [
- 'leftTable' => 'civicrm_membership',
- 'rightTable' => 'civicrm_contact',
- 'callback' => 'joinContactFromMembership',
- ],
- 'contact_from_contribution' => [
- 'leftTable' => 'civicrm_contribution',
- 'rightTable' => 'civicrm_contact',
- 'callback' => 'joinContactFromContribution',
- ],
- 'event_from_participant' => [
- 'leftTable' => 'civicrm_participant',
- 'rightTable' => 'civicrm_event',
- 'callback' => 'joinEventFromParticipant',
- ],
- 'address_from_contact' => [
- 'leftTable' => 'civicrm_contact',
- 'rightTable' => 'civicrm_address',
- 'callback' => 'joinAddressFromContact',
- ],
- ];
- }
-
- /**
- * Add join from contact table to address. Prefix will be added to both tables
- * as it's assumed you are using it to get address of a secondary contact
- *
- * @param string $prefix
- */
- public function joinAddressFromContact($prefix = '') {
- $this->_from .= " LEFT JOIN civicrm_address {$this->_aliases[$prefix .
- 'civicrm_address']}
- ON {$this->_aliases[$prefix .
- 'civicrm_address']}.contact_id = {$this->_aliases[$prefix .
- 'civicrm_contact']}.id";
- }
-
- public function joinPriceFieldValueFromLineItem() {
- $this->_from .= " LEFT JOIN civicrm_price_field_value {$this->_aliases['civicrm_price_field_value']}
- ON {$this->_aliases['civicrm_line_item']}.price_field_value_id = {$this->_aliases['civicrm_price_field_value']}.id";
- }
-
- public function joinPriceFieldFromLineItem() {
- $this->_from .= "
- LEFT JOIN civicrm_price_field {$this->_aliases['civicrm_price_field']}
- ON {$this->_aliases['civicrm_line_item']}.price_field_id = {$this->_aliases['civicrm_price_field']}.id
- ";
- }
-
- /**
- * Define join from line item table to participant table.
- */
- public function joinParticipantFromLineItem() {
- $this->_from .= " LEFT JOIN civicrm_participant {$this->_aliases['civicrm_participant']}
- ON ( {$this->_aliases['civicrm_line_item']}.entity_id = {$this->_aliases['civicrm_participant']}.id
- AND {$this->_aliases['civicrm_line_item']}.entity_table = 'civicrm_participant')
- ";
- }
-
- /**
- * Define join from line item table to Membership table. Seems to be still via contribution
- * as the entity. Have made 'inner' to restrict does that make sense?
- */
- public function joinMembershipFromLineItem() {
- $this->_from .= " INNER JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
- ON ( {$this->_aliases['civicrm_line_item']}.entity_id = {$this->_aliases['civicrm_contribution']}.id
- AND {$this->_aliases['civicrm_line_item']}.entity_table = 'civicrm_contribution')
- LEFT JOIN civicrm_membership_payment pp
- ON {$this->_aliases['civicrm_contribution']}.id = pp.contribution_id
- LEFT JOIN civicrm_membership {$this->_aliases['civicrm_membership']}
- ON pp.membership_id = {$this->_aliases['civicrm_membership']}.id
- ";
- }
-
- /**
- * Define join from Participant to Contribution table.
- */
- public function joinContributionFromParticipant() {
- $this->_from .= " LEFT JOIN civicrm_participant_payment pp
- ON {$this->_aliases['civicrm_participant']}.id = pp.participant_id
- LEFT JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
- ON pp.contribution_id = {$this->_aliases['civicrm_contribution']}.id
- ";
- }
-
- /**
- * Define join from Membership to Contribution table.
- */
- public function joinContributionFromMembership() {
- $this->_from .= " LEFT JOIN civicrm_membership_payment pp
- ON {$this->_aliases['civicrm_membership']}.id = pp.membership_id
- LEFT JOIN civicrm_contribution {$this->_aliases['civicrm_contribution']}
- ON pp.contribution_id = {$this->_aliases['civicrm_contribution']}.id
- ";
- }
-
- public function joinParticipantFromContribution() {
- $this->_from .= " LEFT JOIN civicrm_participant_payment pp
- ON {$this->_aliases['civicrm_contribution']}.id = pp.contribution_id
- LEFT JOIN civicrm_participant {$this->_aliases['civicrm_participant']}
- ON pp.participant_id = {$this->_aliases['civicrm_participant']}.id";
- }
-
- public function joinMembershipFromContribution() {
- $this->_from .= "
- LEFT JOIN civicrm_membership_payment pp
- ON {$this->_aliases['civicrm_contribution']}.id = pp.contribution_id
- LEFT JOIN civicrm_membership {$this->_aliases['civicrm_membership']}
- ON pp.membership_id = {$this->_aliases['civicrm_membership']}.id";
- }
-
- public function joinMembershipTypeFromMembership() {
- $this->_from .= "
- LEFT JOIN civicrm_membership_type {$this->_aliases['civicrm_membership_type']}
- ON {$this->_aliases['civicrm_membership']}.membership_type_id = {$this->_aliases['civicrm_membership_type']}.id
- ";
- }
-
- public function joinContributionFromLineItem() {
-
- // this can be stored as a temp table & indexed for more speed. Not done at this state.
- // another option is to cache it but I haven't tried to put that code in yet (have used it before for one hour caching
- $this->_from .= " LEFT JOIN (SELECT line_item_civireport.id as lid, contribution_civireport_direct.*
-FROM civicrm_line_item line_item_civireport
-LEFT JOIN civicrm_contribution contribution_civireport_direct
- ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = contribution_civireport_direct.id AND line_item_civireport.entity_table = 'civicrm_contribution')
-
-
-WHERE contribution_civireport_direct.id IS NOT NULL
-
-UNION SELECT line_item_civireport.id as lid, contribution_civireport.*
- FROM civicrm_line_item line_item_civireport
- LEFT JOIN civicrm_participant participant_civireport
- ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = participant_civireport.id AND line_item_civireport.entity_table = 'civicrm_participant')
-
-LEFT JOIN civicrm_participant_payment pp
- ON participant_civireport.id = pp.participant_id
- LEFT JOIN civicrm_contribution contribution_civireport
- ON pp.contribution_id = contribution_civireport.id
-
-UNION SELECT line_item_civireport.id as lid,contribution_civireport.*
- FROM civicrm_line_item line_item_civireport
- LEFT JOIN civicrm_membership membership_civireport
- ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id =membership_civireport.id AND line_item_civireport.entity_table = 'civicrm_membership')
-
-LEFT JOIN civicrm_membership_payment pp
- ON membership_civireport.id = pp.membership_id
- LEFT JOIN civicrm_contribution contribution_civireport
- ON pp.contribution_id = contribution_civireport.id
-) as {$this->_aliases['civicrm_contribution']}
- ON {$this->_aliases['civicrm_contribution']}.lid = {$this->_aliases['civicrm_line_item']}.id
- ";
- }
-
- public function joinLineItemFromContribution() {
-
- // this can be stored as a temp table & indexed for more speed. Not done at this stage.
- // another option is to cache it but I haven't tried to put that code in yet (have used it before for one hour caching
- $this->_from .= "
- LEFT JOIN (
-SELECT contribution_civireport_direct.id AS contid, line_item_civireport.*
-FROM civicrm_contribution contribution_civireport_direct
-LEFT JOIN civicrm_line_item line_item_civireport ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = contribution_civireport_direct.id AND line_item_civireport.entity_table = 'civicrm_contribution')
-WHERE line_item_civireport.id IS NOT NULL
-
-UNION
-SELECT contribution_civireport_direct.id AS contid, line_item_civireport.*
-FROM civicrm_contribution contribution_civireport_direct
-LEFT JOIN civicrm_participant_payment pp ON contribution_civireport_direct.id = pp.contribution_id
-LEFT JOIN civicrm_participant p ON pp.participant_id = p.id
-LEFT JOIN civicrm_line_item line_item_civireport ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = p.id AND line_item_civireport.entity_table = 'civicrm_participant')
-WHERE line_item_civireport.id IS NOT NULL
-
-UNION
-
-SELECT contribution_civireport_direct.id AS contid, line_item_civireport.*
-FROM civicrm_contribution contribution_civireport_direct
-LEFT JOIN civicrm_membership_payment pp ON contribution_civireport_direct.id = pp.contribution_id
-LEFT JOIN civicrm_membership p ON pp.membership_id = p.id
-LEFT JOIN civicrm_line_item line_item_civireport ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = p.id AND line_item_civireport.entity_table = 'civicrm_membership')
-WHERE line_item_civireport.id IS NOT NULL
-) as {$this->_aliases['civicrm_line_item']}
- ON {$this->_aliases['civicrm_line_item']}.contid = {$this->_aliases['civicrm_contribution']}.id
-
-
- ";
- }
-
- public function joinLineItemFromMembership() {
-
- // this can be stored as a temp table & indexed for more speed. Not done at this stage.
- // another option is to cache it but I haven't tried to put that code in yet (have used it before for one hour caching
- $this->_from .= "
- LEFT JOIN (
-SELECT contribution_civireport_direct.id AS contid, line_item_civireport.*
-FROM civicrm_contribution contribution_civireport_direct
-LEFT JOIN civicrm_line_item line_item_civireport
-ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = contribution_civireport_direct.id AND line_item_civireport.entity_table = 'civicrm_contribution')
-
-WHERE line_item_civireport.id IS NOT NULL
-
-UNION
-
-SELECT contribution_civireport_direct.id AS contid, line_item_civireport.*
-FROM civicrm_contribution contribution_civireport_direct
-LEFT JOIN civicrm_membership_payment pp ON contribution_civireport_direct.id = pp.contribution_id
-LEFT JOIN civicrm_membership p ON pp.membership_id = p.id
-LEFT JOIN civicrm_line_item line_item_civireport ON (line_item_civireport.line_total > 0 AND line_item_civireport.entity_id = p.id AND line_item_civireport.entity_table = 'civicrm_membership')
-WHERE line_item_civireport.id IS NOT NULL
-) as {$this->_aliases['civicrm_line_item']}
- ON {$this->_aliases['civicrm_line_item']}.contid = {$this->_aliases['civicrm_contribution']}.id
- ";
- }
-
- public function joinContactFromParticipant() {
- $this->_from .= " LEFT JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
- ON {$this->_aliases['civicrm_participant']}.contact_id = {$this->_aliases['civicrm_contact']}.id";
- }
-
- public function joinContactFromMembership() {
- $this->_from .= " LEFT JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
- ON {$this->_aliases['civicrm_membership']}.contact_id = {$this->_aliases['civicrm_contact']}.id";
- }
-
- public function joinContactFromContribution() {
- $this->_from .= " LEFT JOIN civicrm_contact {$this->_aliases['civicrm_contact']}
- ON {$this->_aliases['civicrm_contribution']}.contact_id = {$this->_aliases['civicrm_contact']}.id";
- }
-
- public function joinEventFromParticipant() {
- $this->_from .= " LEFT JOIN civicrm_event {$this->_aliases['civicrm_event']}
- ON ({$this->_aliases['civicrm_event']}.id = {$this->_aliases['civicrm_participant']}.event_id ) AND
- ({$this->_aliases['civicrm_event']}.is_template IS NULL OR
- {$this->_aliases['civicrm_event']}.is_template = 0)";
- }
-
- /**
- * Retrieve text for financial type from pseudoconstant.
- *
- * @param $value
- * @param array $row
- *
- * @return string
- */
- public function alterNickName($value, &$row) {
- if (empty($row['civicrm_contact_id'])) {
- return NULL;
- }
- $contactID = $row['civicrm_contact_id'];
- return "<div id=contact-{$contactID} class='crm-entity'>
- <span class='crm-editable crmf-nick_name crm-editable-enabled'>
- " . $value . "</span></div>";
- }
-
- /**
- * Retrieve text for contribution type from pseudoconstant.
- *
- * @param $value
- * @param array $row
- *
- * @return array|string
- */
- public function alterContributionType($value, &$row) {
- return is_string(CRM_Contribute_PseudoConstant::financialType($value, FALSE)) ? CRM_Contribute_PseudoConstant::financialType($value, FALSE) : '';
- }
-
- /**
- * Retrieve text for contribution status from pseudoconstant.
- *
- * @param $value
- * @param array $row
- *
- * @return array
- */
- public function alterContributionStatus($value, &$row) {
- return CRM_Contribute_PseudoConstant::contributionStatus($value);
- }
-
- /**
- * Retrieve text for payment instrument from pseudoconstant.
- *
- * @param $value
- * @param array $row
- *
- * @return array
- */
- public function alterEventType($value, &$row) {
- return CRM_Event_PseudoConstant::eventType($value);
- }
-
- /**
- * @param $value
- * @param array $row
- *
- * @return array|string
- */
- public function alterEventID($value, &$row) {
- return is_string(CRM_Event_PseudoConstant::event($value, FALSE)) ? CRM_Event_PseudoConstant::event($value, FALSE) : '';
- }
-
- /**
- * @param $value
- * @param array $row
- *
- * @return array|string
- */
- public function alterMembershipTypeID($value, &$row) {
- return is_string(CRM_Member_PseudoConstant::membershipType($value, FALSE)) ? CRM_Member_PseudoConstant::membershipType($value, FALSE) : '';
- }
-
- /**
- * @param $value
- * @param array $row
- *
- * @return array|string
- */
- public function alterMembershipStatusID($value, &$row) {
- return is_string(CRM_Member_PseudoConstant::membershipStatus($value, FALSE)) ? CRM_Member_PseudoConstant::membershipStatus($value, FALSE) : '';
- }
-
- /**
- * @param $value
- * @param array $row
- * @param $selectedfield
- * @param string $criteriaFieldName
- *
- * @return array
- */
- public function alterCountryID($value, &$row, $selectedfield, $criteriaFieldName) {
- $url = CRM_Utils_System::url(CRM_Utils_System::currentPath(), "reset=1&force=1&{$criteriaFieldName}_op=in&{$criteriaFieldName}_value={$value}", $this->_absoluteUrl);
- $row[$selectedfield . '_link'] = $url;
- $row[$selectedfield .
- '_hover'] = ts("%1 for this country.", [1 => $value]);
- $countries = CRM_Core_PseudoConstant::country($value, FALSE);
- if (!is_array($countries)) {
- return $countries;
- }
- }
-
- /**
- * @param $value
- * @param array $row
- * @param $selectedfield
- * @param string $criteriaFieldName
- *
- * @return array
- */
- public function alterCountyID($value, &$row, $selectedfield, $criteriaFieldName) {
- $url = CRM_Utils_System::url(CRM_Utils_System::currentPath(), "reset=1&force=1&{$criteriaFieldName}_op=in&{$criteriaFieldName}_value={$value}", $this->_absoluteUrl);
- $row[$selectedfield . '_link'] = $url;
- $row[$selectedfield .
- '_hover'] = ts("%1 for this county.", [1 => $value]);
- $counties = CRM_Core_PseudoConstant::county($value, FALSE);
- if (!is_array($counties)) {
- return $counties;
- }
- }
-
- /**
- * @param $value
- * @param array $row
- * @param $selectedfield
- * @param string $criteriaFieldName
- *
- * @return array
- */
- public function alterStateProvinceID($value, &$row, $selectedfield, $criteriaFieldName) {
- $url = CRM_Utils_System::url(CRM_Utils_System::currentPath(), "reset=1&force=1&{$criteriaFieldName}_op=in&{$criteriaFieldName}_value={$value}", $this->_absoluteUrl);
- $row[$selectedfield . '_link'] = $url;
- $row[$selectedfield .
- '_hover'] = ts("%1 for this state.", [1 => $value]);
-
- $states = CRM_Core_PseudoConstant::stateProvince($value, FALSE);
- if (!is_array($states)) {
- return $states;
- }
- }
-
- /**
- * @param $value
- * @param array $row
- * @param string $fieldname
- *
- * @return mixed
- */
- public function alterContactID($value, &$row, $fieldname) {
- $row[$fieldname . '_link'] = CRM_Utils_System::url("civicrm/contact/view",
- 'reset=1&cid=' . $value, $this->_absoluteUrl);
- return $value;
- }
-
- /**
- * @param $value
- *
- * @return array
- */
- public function alterParticipantStatus($value) {
- if (empty($value)) {
- return NULL;
- }
- return CRM_Event_PseudoConstant::participantStatus($value, FALSE, 'label');
- }
-
- /**
- * @param $value
- *
- * @return string|void
- */
- public function alterParticipantRole($value) {
- if (empty($value)) {
- return NULL;
- }
- $roles = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value);
- $value = [];
- foreach ($roles as $role) {
- $value[$role] = CRM_Event_PseudoConstant::participantRole($role, FALSE);
- }
- return implode(', ', $value);
- }
-
- /**
- * @param $value
- *
- * @return mixed
- */
- public function alterPaymentType($value) {
- $paymentInstruments = CRM_Contribute_PseudoConstant::paymentInstrument();
- return $paymentInstruments[$value];
- }
-
-}
$domain = new CRM_Core_DAO_Domain();
$domain->find(TRUE);
$queries = [];
- if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, $column)) {
+ if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, $column, FALSE)) {
if ($domain->locales) {
if ($localizable) {
$locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
foreach ($locales as $locale) {
- if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, "{$column}_{$locale}")) {
+ if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, "{$column}_{$locale}", FALSE)) {
$queries[] = "ALTER TABLE `$table` ADD COLUMN `{$column}_{$locale}` $properties";
}
}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2019 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License along with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Upgrade logic for FiveFifteen */
+class CRM_Upgrade_Incremental_php_FiveFifteen extends CRM_Upgrade_Incremental_Base {
+
+ /**
+ * Compute any messages which should be displayed beforeupgrade.
+ *
+ * Note: This function is called iteratively for each upcoming
+ * revision to the database.
+ *
+ * @param string $preUpgradeMessage
+ * @param string $rev
+ * a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
+ * @param null $currentVer
+ */
+ public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
+ // Example: Generate a pre-upgrade message.
+ // if ($rev == '5.12.34') {
+ // $preUpgradeMessage .= '<p>' . 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'))) . '</p>';
+ // }
+ }
+
+ /**
+ * Compute any messages which should be displayed after upgrade.
+ *
+ * @param string $postUpgradeMessage
+ * alterable.
+ * @param string $rev
+ * 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 .= '<br /><br />' . 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'.");
+ // }
+ }
+
+ /*
+ * Important! All upgrade functions MUST add a 'runSql' task.
+ * Uncomment and use the following template for a new upgrade version
+ * (change the x in the function name):
+ */
+
+ // /**
+ // * Upgrade function.
+ // *
+ // * @param string $rev
+ // */
+ // public function upgrade_5_0_x($rev) {
+ // $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
+ // $this->addTask('Do the foo change', 'taskFoo', ...);
+ // // Additional tasks here...
+ // // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
+ // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
+ // }
+
+ // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) {
+ // return TRUE;
+ // }
+
+}
* (change the x in the function name):
*/
- // /**
- // * Upgrade function.
- // *
- // * @param string $rev
- // */
- // public function upgrade_5_0_x($rev) {
- // $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
- // $this->addTask('Do the foo change', 'taskFoo', ...);
- // // Additional tasks here...
- // // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
- // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
- // }
+ /**
+ * Upgrade function.
+ *
+ * @param string $rev
+ */
+ public function upgrade_5_14_alpha1($rev) {
+ $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
+
+ // Only need to rebuild view if CiviCase is enabled: otherwise will be
+ // rebuilt when component is enabled
+ $config = CRM_Core_Config::singleton();
+ if (in_array('CiviCase', $config->enableComponents)) {
+ $this->addTask('Rebuild case activity views', 'rebuildCaseActivityView', $rev);
+ }
+ // Additional tasks here...
+ // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
+ // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
+ }
- // public static function taskFoo(CRM_Queue_TaskContext $ctx, ...) {
- // return TRUE;
- // }
+ /**
+ * Rebuild the view of recent and upcoming case activities
+ *
+ * See https://github.com/civicrm/civicrm-core/pull/14086 and
+ * https://lab.civicrm.org/dev/core/issues/832
+ *
+ * @param CRM_Queue_TaskContext $ctx
+ * @return bool
+ */
+ public static function rebuildCaseActivityView($ctx) {
+ if (!CRM_Case_BAO_Case::createCaseViews()) {
+ CRM_Core_Error::debug_log_message(ts("Could not create the MySQL views for CiviCase. Your mysql user needs to have the 'CREATE VIEW' permission"));
+ return FALSE;
+ }
+ return TRUE;
+ }
}
--- /dev/null
+{* file to handle db changes in 5.14.beta1 during upgrade *}
--- /dev/null
+{* file to handle db changes in 5.15.alpha1 during upgrade *}
public static function checkFinancialAclReport() {
$messages = [];
$ftAclSetting = Civi::settings()->get('acl_financial_type');
- $financialAclExtension = civicrm_api3('extension', 'get', ['key' => 'biz.jmaconsulting.financialaclreport']);
- if ($ftAclSetting && (($financialAclExtension['count'] == 1 && $financialAclExtension['status'] != 'Installed') || $financialAclExtension['count'] !== 1)) {
+ $financialAclExtension = civicrm_api3('extension', 'get', ['key' => 'biz.jmaconsulting.financialaclreport', 'sequential' => 1]);
+ if ($ftAclSetting && (($financialAclExtension['count'] == 1 && $financialAclExtension['values'][0]['status'] != 'Installed') || $financialAclExtension['count'] !== 1)) {
$messages[] = new CRM_Utils_Check_Message(
__FUNCTION__,
ts('CiviCRM will in the future require the extension %1 for CiviCRM Reports to work correctly with the Financial Type ACLs. The extension can be downloaded <a href="%2">here</a>', [
Civi::dispatcher()->dispatch('hook_civicrm_alterAngular', $event);
}
+ /**
+ * This hook is called when building a link to a semi-static asset.
+ *
+ * @param string $asset
+ * The name of the asset.
+ * Ex: 'angular.json'
+ * @param array $params
+ * List of optional arguments which influence the content.
+ * @return null
+ * the return value is ignored
+ */
+ public static function getAssetUrl(&$asset, &$params) {
+ return self::singleton()->invoke(['asset', 'params'],
+ $asset, $params, self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
+ 'civicrm_getAssetUrl'
+ );
+ }
+
/**
* This hook is called whenever the system builds a new copy of
* semi-static asset.
* @param string $region
*/
public static function coreResourceList(&$list, $region) {
- // First allow the cms integration to add to the list
- CRM_Core_Config::singleton()->userSystem->appendCoreResources($list);
-
self::singleton()->invoke(['list', 'region'], $list, $region,
self::$_nullObject, self::$_nullObject, self::$_nullObject, self::$_nullObject,
'civicrm_coreResourceList'
*
* Example 3: Create an empty temp table with list of columns.
*
- * $tmpTbl = CRM_Utils_SQL_TempTable::build()->setDurable()->setUtf8()->createWithColumns('id int(10, name varchar(64)');
+ * $tmpTbl = CRM_Utils_SQL_TempTable::build()->setDurable()->createWithColumns('id int(10, name varchar(64)');
*
* Example 4: Drop a table that you previously created.
*
$t->id = md5(uniqid('', TRUE));
// The constant CIVICRM_TEMP_FORCE_DURABLE is for local debugging.
$t->durable = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_DURABLE', FALSE);
- // @deprecated This constant is deprecated and will be removed.
- $t->utf8 = CRM_Utils_Constant::value('CIVICRM_TEMP_FORCE_UTF8', TRUE);
+ $t->utf8 = TRUE;
$t->autodrop = FALSE;
$t->memory = FALSE;
return $t;
/**
* Set table collation to UTF8.
*
- * This would make sense as a default but cautiousness during phasing in has made it opt-in.
+ * @deprecated This method is deprecated as tables should be assumed to have
+ * UTF-8 as the default character set and collation; some other character set
+ * or collation may be specified in the column definition.
*
* @param bool $value
*
* @method static int getLoggedInUfID() Get current logged in user id.
* @method static setHttpHeader(string $name, string $value) Set http header.
* @method static array synchronizeUsers() Create CRM contacts for all existing CMS users.
+ * @method static appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) Callback for hook_civicrm_coreResourceList.
+ * @method static alterAssetUrl(\Civi\Core\Event\GenericHookEvent $e) Callback for hook_civicrm_getAssetUrl.
*/
class CRM_Utils_System {
$print = FALSE,
$maintenance = FALSE
) {
- $config = &CRM_Core_Config::singleton();
- return $config->userSystem->theme($content, $print, $maintenance);
+ return CRM_Core_Config::singleton()->userSystem->theme($content, $print, $maintenance);
}
/**
$addLanguagePart = TRUE,
$removeLanguagePart = FALSE
) {
- $config = &CRM_Core_Config::singleton();
- return $config->userSystem->languageNegotiationURL($url, $addLanguagePart, $removeLanguagePart);
+ return CRM_Core_Config::singleton()->userSystem->languageNegotiationURL($url, $addLanguagePart, $removeLanguagePart);
}
/**
/**
* Append Backdrop CSS and JS to coreResourcesList.
*
- * @param array $list
+ * @param \Civi\Core\Event\GenericHookEvent $e
*/
- public function appendCoreResources(&$list) {
- $list[] = 'css/backdrop.css';
- $list[] = 'js/crm.backdrop.js';
+ public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
+ $e->list[] = 'css/backdrop.css';
+ $e->list[] = 'js/crm.backdrop.js';
}
}
}
$out = $content;
- $config = &CRM_Core_Config::singleton();
if (
!$print &&
- $config->userFramework == 'WordPress'
+ CRM_Core_Config::singleton()->userFramework == 'WordPress'
) {
if (!function_exists('is_admin')) {
throw new \Exception('Function "is_admin()" is missing, even though WordPress is the user framework.');
/**
* Append to coreResourcesList.
*
- * @param array $list
+ * @param \Civi\Core\Event\GenericHookEvent $e
*/
- public function appendCoreResources(&$list) {
+ public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
+ }
+
+ /**
+ * Modify dynamic assets.
+ *
+ * @param \Civi\Core\Event\GenericHookEvent $e
+ */
+ public function alterAssetUrl(\Civi\Core\Event\GenericHookEvent $e) {
}
/**
/**
* Append Drupal8 js to coreResourcesList.
*
- * @param array $list
+ * @param \Civi\Core\Event\GenericHookEvent $e
*/
- public function appendCoreResources(&$list) {
- $list[] = 'js/crm.drupal8.js';
+ public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
+ $e->list[] = 'js/crm.drupal8.js';
}
}
$name = CRM_Utils_Array::value('name', $params);
$pass = CRM_Utils_Array::value('pass', $params);
- if (isset($params['uid'])) {
- throw new \RuntimeException("Not implemented WordPress::loadBootStrap([uid=>\$num]))");
- }
if (!defined('WP_USE_THEMES')) {
define('WP_USE_THEMES', FALSE);
CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
}
require_once $cmsRootPath . DIRECTORY_SEPARATOR . 'wp-includes/pluggable.php';
- $uid = CRM_Utils_Array::value('uid', $name);
+ $uid = CRM_Utils_Array::value('uid', $params);
if (!$uid) {
$name = $name ? $name : trim(CRM_Utils_Array::value('name', $_REQUEST));
$pass = $pass ? $pass : trim(CRM_Utils_Array::value('pass', $_REQUEST));
/**
* Append WP js to coreResourcesList.
*
- * @param array $list
+ * @param \Civi\Core\Event\GenericHookEvent $e
*/
- public function appendCoreResources(&$list) {
- $list[] = 'js/crm.wordpress.js';
+ public function appendCoreResources(\Civi\Core\Event\GenericHookEvent $e) {
+ $e->list[] = 'js/crm.wordpress.js';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function alterAssetUrl(\Civi\Core\Event\GenericHookEvent $e) {
+ // Set menubar breakpoint to match WP admin theme
+ if ($e->asset == 'crm-menubar.css') {
+ $e->params['breakpoint'] = 783;
+ }
}
/**
* Ex: 'http://example.org/files/civicrm/dyn/angular.abcd1234abcd1234.json'.
*/
public function getUrl($name, $params = []) {
+ \CRM_Utils_Hook::getAssetUrl($name, $params);
+
if (!$this->isValidName($name)) {
throw new \RuntimeException("Invalid dynamic asset name");
}
$dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetJs']);
$dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Utils_VisualBundle', 'buildAssetCss']);
$dispatcher->addListener('hook_civicrm_buildAsset', ['\CRM_Core_Resources', 'renderMenubarStylesheet']);
+ $dispatcher->addListener('hook_civicrm_coreResourceList', ['\CRM_Utils_System', 'appendCoreResources']);
+ $dispatcher->addListener('hook_civicrm_getAssetUrl', ['\CRM_Utils_System', 'alterAssetUrl']);
$dispatcher->addListener('civi.dao.postInsert', ['\CRM_Core_BAO_RecurringEntity', 'triggerInsert']);
$dispatcher->addListener('civi.dao.postUpdate', ['\CRM_Core_BAO_RecurringEntity', 'triggerUpdate']);
$dispatcher->addListener('civi.dao.postDelete', ['\CRM_Core_BAO_RecurringEntity', 'triggerDelete']);
return $results;
}
+ mysqli_query($conn, 'DROP TABLE IF EXISTS civicrm_utf8mb4_test');
$r = mysqli_query($conn, 'CREATE TABLE civicrm_utf8mb4_test (id VARCHAR(255), PRIMARY KEY(id(255))) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC ENGINE=INNODB');
if (!$r) {
$results['severity'] = $this::REQUIREMENT_WARNING;
mysqli_close($conn);
return $results;
}
- mysqli_query('DROP TABLE civicrm_utf8mb4_test');
+ mysqli_query($conn, 'DROP TABLE civicrm_utf8mb4_test');
// Ensure that the MySQL driver supports utf8mb4 encoding.
$version = mysqli_get_client_info($conn);
text: ts('Submit final mailing'),
icons: {primary: 'fa-paper-plane'},
click: function() {
- crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings[mailingName], [
- 'name',
- 'recipients',
- 'scheduled_date'
- ]);
- crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
- .then(function() {
- return crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
- abtest.submitFinal().then(function(r) {
- delete abtest.$CrmMailingABReportCnt;
- return r;
- }));
- })
- .then(function() {
+ crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
+ abtest.submitFinal(abtest.mailings[mailingName].id).then(function (r) {
+ delete abtest.$CrmMailingABReportCnt;
+ }))
+ .then(function () {
dialogService.close('selectWinnerDialog', abtest);
});
}
// Schedule the final mailing
// @return Promise CrmMailingAB
// Note: Submission may cause the server state to change. Consider abtest.submit().then(...abtest.load()...)
- submitFinal: function submitFinal() {
+ submitFinal: function submitFinal(winner_id) {
var crmMailingAB = this;
var params = {
id: this.ab.id,
status: 'Final',
+ winner_id: winner_id,
approval_date: 'now',
scheduled_date: this.mailings.c.scheduled_date ? this.mailings.c.scheduled_date : 'now'
};
function civicrm_api3_activity_get($params) {
$options = _civicrm_api3_get_options_from_params($params, FALSE, 'Activity', 'get');
$sql = CRM_Utils_SQL_Select::fragment();
+ _civicrm_activity_get_handleSourceContactNameOrderBy($params, $options, $sql);
_civicrm_api3_activity_get_extraFilters($params, $sql);
return civicrm_api3_create_success($activities, $params, 'Activity', 'get');
}
+/**
+ * Handle source_contact_name as a sort parameter.
+ *
+ * This is passed from the activity selector - e.g search results or contact tab.
+ *
+ * It's a non-standard handling but this api already handles variations on handling source_contact
+ * as a filter & as a field so it's in keeping with that. Source contact has a one-one relationship
+ * with activity table.
+ *
+ * Test coverage in CRM_Activity_BAO_ActivtiyTest::testGetActivitiesforContactSummaryWithSortOptions
+ *
+ * @param array $params
+ * @param array $options
+ * @param CRM_Utils_SQL_Select $sql
+ */
+function _civicrm_activity_get_handleSourceContactNameOrderBy(&$params, &$options, $sql) {
+ $sourceContactID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Source');
+ if (!empty($options['sort']) && in_array($options['sort'], [
+ 'source_contact_name',
+ 'source_contact_name desc',
+ 'source_contact_name asc'
+ ])) {
+ $order = substr($options['sort'], -4) === 'desc' ? 'desc' : 'asc';
+ $sql->join(
+ 'source_contact',
+ "LEFT JOIN
+ civicrm_activity_contact ac ON (ac.activity_id = a.id AND record_type_id = #sourceContactID)
+ LEFT JOIN civicrm_contact c ON c.id = ac.contact_id",
+ ['sourceContactID' => $sourceContactID]
+ );
+ $sql->orderBy("c.display_name $order");
+ unset($options['sort'], $params['options']['sort']);
+ }
+}
+
/**
* Support filters beyond what basic_get can do.
*
$returnProperties = $mailing->getReturnProperties();
$contactID = CRM_Utils_Array::value('contact_id', $params);
if (!$contactID) {
- $contactID = $session->get('userID');
+ // If we still don't have a userID in a session because we are annon then set contactID to be 0
+ $contactID = empty($session->get('userID')) ? 0 : $session->get('userID');
}
$mailingParams = ['contact_id' => $contactID];
- $details = CRM_Utils_Token::getTokenDetails($mailingParams, $returnProperties, TRUE, TRUE, NULL, $mailing->getFlattenedTokens());
+ if (!$contactID) {
+ $details = CRM_Utils_Token::getAnonymousTokenDetails($mailingParams, $returnProperties, TRUE, TRUE, NULL, $mailing->getFlattenedTokens());
+ $details = CRM_Utils_Array::value(0, $details[0]);
+ }
+ else {
+ $details = CRM_Utils_Token::getTokenDetails($mailingParams, $returnProperties, TRUE, TRUE, NULL, $mailing->getFlattenedTokens());
+ $details = $details[0][$contactID];
+ }
- $mime = $mailing->compose(NULL, NULL, NULL, $session->get('userID'), $fromEmail, $fromEmail,
- TRUE, $details[0][$contactID], $attachments
+ $mime = $mailing->compose(NULL, NULL, NULL, $contactID, $fromEmail, $fromEmail,
+ TRUE, $details, $attachments
);
return civicrm_api3_create_success([
- 'id' => $params['id'],
+ 'id' => $mailingID,
'contact_id' => $contactID,
'subject' => $mime->headers()['Subject'],
'body_html' => $mime->getHTMLBody(),
$spec['approval_date'] = $mailingFields['approval_date'];
$spec['approval_status_id'] = $mailingFields['approval_status_id'];
$spec['approval_note'] = $mailingFields['approval_note'];
+ $spec['winner_id'] = [
+ 'name' => 'winner_id',
+ 'type' => 1,
+ 'title' => 'Winner ID',
+ 'description' => 'The experimental mailing with the best results. If specified, values are copied to the final mailing.',
+ 'localizable' => 0,
+ ];
// Note: we'll pass through approval_* fields to the underlying mailing, but they may be ignored
// if the user doesn't have suitable permission. If separate approvals are required, they must be provided
// outside the A/B Test UI.
if ($dao->status != 'Testing') {
throw new API_Exception("Cannot transition to state 'Final'");
}
+ if (!empty($params['winner_id'])) {
+ _civicrm_api3_mailing_a_b_fill_winner($params['winner_id'], $dao->mailing_id_c);
+ }
civicrm_api3('Mailing', 'submit', $submitParams + [
'id' => $dao->mailing_id_c,
'_skip_evil_bao_auto_recipients_' => 1,
]);
}
+/**
+ * @param int $winner_id
+ * The experimental mailing chosen as the "winner".
+ * @param int $final_id
+ * The final mailing which should imitate the "winner".
+ * @throws \API_Exception
+ */
+function _civicrm_api3_mailing_a_b_fill_winner($winner_id, $final_id) {
+ $copyFields = [
+ // 'id',
+ // 'name',
+ 'campaign_id',
+ 'from_name',
+ 'from_email',
+ 'replyto_email',
+ 'subject',
+ 'dedupe_email',
+ // 'recipients',
+ 'body_html',
+ 'body_text',
+ 'footer_id',
+ 'header_id',
+ 'visibility',
+ 'url_tracking',
+ 'dedupe_email',
+ 'forward_replies',
+ 'auto_responder',
+ 'open_tracking',
+ 'override_verp',
+ 'optout_id',
+ 'reply_id',
+ 'resubscribe_id',
+ 'unsubscribe_id'
+ ];
+ $f = CRM_Utils_SQL_Select::from('civicrm_mailing')
+ ->where('id = #id', ['id' => $winner_id])
+ ->select($copyFields)
+ ->execute()
+ ->fetchAll();
+ if (count($f) !== 1) {
+ throw new API_Exception('Invalid winner_id');
+ }
+ foreach ($f as $winner) {
+ civicrm_api3('Mailing', 'create', $winner + [
+ 'id' => $final_id,
+ '_skip_evil_bao_auto_recipients_' => 1,
+ ]);
+ }
+}
+
/**
* Adjust Metadata for graph_stats action.
*
'financial_trxn_id' => $params['id'],
];
$entity = civicrm_api3('EntityFinancialTrxn', 'getsingle', $eftParams);
- $contributionId = $entity['entity_id'];
- $params['total_amount'] = $entity['amount'];
- unset($params['id']);
- $trxn = CRM_Contribute_BAO_Contribution::recordAdditionalPayment($contributionId, $params, 'refund', NULL, FALSE);
+ $paymentParams = [
+ 'total_amount' => -$entity['amount'],
+ 'contribution_id' => $entity['entity_id'],
+ 'trxn_date' => CRM_Utils_Array::value('trxn_date', $params, 'now'),
+ ];
- $values = [];
- _civicrm_api3_object_to_array_unique_fields($trxn, $values[$trxn->id]);
- return civicrm_api3_create_success($values, $params, 'Payment', 'cancel', $trxn);
+ foreach (['trxn_id', 'payment_instrument_id'] as $permittedParam) {
+ if (isset($params[$permittedParam])) {
+ $paymentParams[$permittedParam] = $params[$permittedParam];
+ }
+ }
+ $result = civicrm_api3('Payment', 'create', $paymentParams);
+ return civicrm_api3_create_success($result['values'], $params, 'Payment', 'cancel');
}
/**
'type' => CRM_Utils_Type::T_INT,
'api.aliases' => ['payment_id'],
],
+ 'trxn_date' => [
+ 'title' => 'Cancel Date',
+ 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
+ ],
];
}
'type' => CRM_Utils_Type::T_INT,
'api.aliases' => ['payment_id'],
],
+ 'trxn_date' => [
+ 'title' => 'Cancel Date',
+ 'type' => CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME,
+ ],
];
}
// of this script
array_shift($args);
- while (list($k, $arg) = each($args)) {
+ foreach ($args as $k => $arg) {
// sanitize all user input
$arg = $this->_sanitize($arg);
- github : demeritcowboy
name : Dave D
- jira : demeritcowboy
+ jira : Dave D
- github : dereklewis123
name : Derek Lewis
organization: Greenleaf Advancement
jira : guyiac
+- github : GValFr35
+
+- github : kewljuice
+ name : Wouter Hechtermans
+ organization: Calibrate
+
+- github : khorporative
+ name : Aivars
+
- github : h-c-c
name : Peter Hartmann
organization: Hartmann Computer Consulting
organization: Web Access
jira : pratiksha
+- github : prondubuisi
+ name : Onyemenam Ndubuisi
+
- name : Richard Edgar
jira : redgar
border: 1px solid #ccc;
margin: 4px 4px 0;
padding: 2px 8px;
- height: 30px;
+ height: calc($menubarHeight - 10px);
width: 30px;
transition: width .5s .05s, background-color .3s .05s;
color: black;
transform: rotate(180deg);
}
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
/* Switch to desktop layout
-----------------------------------------------
}
#civicrm-menu li a {
- background-color: $semiTransparentMenuColor;
+ background-color: $menuItemColor;
color: $textColor;
}
#civicrm-menu > li > a {
- height: 40px;
+ height: $menubarHeight;
+ padding: 0 8px;
+ }
+
+ #civicrm-menu > li > a > * {
+ vertical-align: middle;
+ }
+
+ /* Pseudo-element to ensure vertical alignment */
+ #civicrm-menu > li > a:after {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
}
#civicrm-menu > li > a.highlighted {
}
}
-@media (max-width: 768px) {
+@media (max-width: $breakMax) {
/* hide the menu in mobile view */
#crm-menubar-state:not(:checked) ~ #civicrm-menu {
display: none;
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
body.crm-menubar-visible.crm-menubar-over-cms-menu {
border-top: 0 none !important;
- margin-top: 40px;
+ margin-top: $menubarHeight;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped {
- margin-top: 80px;
+ margin-top: calc($menubarHeight * 2);
}
body.crm-menubar-visible.crm-menubar-over-cms-menu #admin-bar {
visibility: hidden;
}
}
-@media (max-width: 768px) {
+@media (max-width: $breakMax) {
body.backdrop-admin-bar-position-absolute #civicrm-menu-nav {
position: absolute;
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar {
display: none;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu {
- padding-top: 40px !important;
+ padding-top: $menubarHeight !important;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped {
- padding-top: 80px !important;
+ padding-top: calc($menubarHeight * 2) !important;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar .toolbar-drawer {
min-height: 30px;
}
body.crm-menubar-visible.crm-menubar-below-cms-menu.admin-menu {
- padding-top: 40px !important;
+ padding-top: $menubarHeight !important;
}
body.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped.admin-menu {
- padding-top: 80px !important;
+ padding-top: calc($menubarHeight * 2) !important;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu #admin-menu {
display: none;
}
/* For adminimal_admin_menu */
-@media (min-width: 768px) and (max-width: 1024px) {
+@media (min-width: $breakMin) and (max-width: 1024px) {
body.crm-menubar-visible.crm-menubar-over-cms-menu.admin-menu.adminimal-menu > .slicknav_menu {
display: none;
}
}
-@media (max-width: 768px) {
+@media (max-width: $breakMax) {
body.toolbar.crm-menubar-visible #toolbar-home {
visibility: hidden;
left: 44px;
}
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
- body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar-administration {
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar-administration {
display: none;
}
body.crm-menubar-visible.crm-menubar-over-cms-menu {
- padding-top: 40px !important;
+ padding-top: $menubarHeight !important;
}
- body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped,
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped {
+ padding-top: calc($menubarHeight * 2) !important;
+ }
+ /* The Drupal menu is 40px tall so we add that to our menubar height */
body.crm-menubar-visible.crm-menubar-below-cms-menu {
- padding-top: 80px !important;
+ padding-top: calc($menubarHeight + 40px) !important;
}
body.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped {
- padding-top: 120px !important;
+ padding-top: calc($menubarHeight * 2 + 40px) !important;
}
body.crm-menubar-below-cms-menu > #civicrm-menu-nav ul#civicrm-menu {
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
body.crm-menubar-over-cms-menu.crm-menubar-visible {
- padding-top: 40px;
+ padding-top: $menubarHeight;
}
body.crm-menubar-over-cms-menu.crm-menubar-visible.crm-menubar-wrapped {
- padding-top: 80px;
+ padding-top: calc($menubarHeight * 2);
}
body.crm-menubar-below-cms-menu.crm-menubar-visible {
- margin-top: 40px;
+ margin-top: $menubarHeight;
}
body.crm-menubar-below-cms-menu.crm-menubar-visible.crm-menubar-wrapped {
- margin-top: 80px;
+ margin-top: calc($menubarHeight * 2);
}
}
-@media (max-width: 768px) {
+@media (max-width: $breakMax) {
body #civicrm-menu-nav {
position: absolute;
-@media (min-width: 768px) {
+@media (min-width: $breakMin) {
body.crm-menubar-over-cms-menu.crm-menubar-visible #wpbody {
padding-top: 8px;
}
body.crm-menubar-below-cms-menu.crm-menubar-visible #wpbody {
- padding-top: 40px;
+ padding-top: $menubarHeight;
}
body.crm-menubar-below-cms-menu.crm-menubar-visible.crm-menubar-wrapped #wpbody {
- padding-top: 80px;
+ padding-top: calc($menubarHeight * 2);
}
body.crm-menubar-over-cms-menu.crm-menubar-visible.crm-menubar-wrapped #adminmenuwrap {
- margin-top: 40px;
+ margin-top: $menubarHeight;
}
}
-@media (min-width: 768px) and (max-width: 960px) {
+@media (min-width: $breakMin) and (max-width: 960px) {
/* For the auto-fold toolbar */
.wp-toolbar body.crm-menubar-below-cms-menu.auto-fold > #civicrm-menu-nav #civicrm-menu {
}
}
-@media (max-width: 768px) {
+@media (max-width: $breakMax) {
body #civicrm-menu-nav .crm-menubar-toggle-btn {
position: absolute;
if (defined('PANTHEON_ENVIRONMENT')) {
ini_set('session.save_handler', 'files');
}
-session_start();
$rest = new CRM_Utils_REST();
// Json-appropriate header will be set by CRM_Utils_Rest
})
.on('show.smapi', function(e, menu) {
// Focus menu when opened with an accesskey
- $(menu).siblings('a[accesskey]:not(:hover)').focus();
+ $(menu).siblings('a[accesskey]').focus();
})
.smartmenus(CRM.menubar.settings);
initialized = true;
* https://github.com/civicrm/civicrm-joomla
* https://github.com/civicrm/civicrm-wordpress
+## CiviCRM 5.13.2
+
+Released May 6, 2019
+
+- **[Synopsis](release-notes/5.13.2.md#synopsis)**
+- **[Features](release-notes/5.13.2.md#features)**
+- **[Bugs resolved](release-notes/5.13.2.md#bugs)**
+- **[Miscellany](release-notes/5.13.2.md#misc)**
+- **[Credits](release-notes/5.13.2.md#credits)**
+- **[Feedback](release-notes/5.13.2.md#feedback)**
+
+## CiviCRM 5.13.1
+
+Released May 2, 2019
+
+- **[Synopsis](release-notes/5.13.1.md#synopsis)**
+- **[Features](release-notes/5.13.1.md#features)**
+- **[Bugs resolved](release-notes/5.13.1.md#bugs)**
+- **[Miscellany](release-notes/5.13.1.md#misc)**
+- **[Credits](release-notes/5.13.1.md#credits)**
+- **[Feedback](release-notes/5.13.1.md#feedback)**
+
+## CiviCRM 5.13.0
+
+Released May 1, 2019
+
+- **[Synopsis](release-notes/5.13.0.md#synopsis)**
+- **[Features](release-notes/5.13.0.md#features)**
+- **[Bugs resolved](release-notes/5.13.0.md#bugs)**
+- **[Miscellany](release-notes/5.13.0.md#misc)**
+- **[Credits](release-notes/5.13.0.md#credits)**
+- **[Feedback](release-notes/5.13.0.md#feedback)**
+
+## CiviCRM 5.12.4
+
+Released April 25, 2019
+
+- **[Synopsis](release-notes/5.12.4.md#synopsis)**
+- **[Bugs resolved](release-notes/5.12.4.md#bugs)**
+- **[Credits](release-notes/5.12.4.md#credits)**
+- **[Feedback](release-notes/5.12.4.md#feedback)**
+
+## CiviCRM 5.12.3
+
+Released April 20, 2019
+
+- **[Synopsis](release-notes/5.12.3.md#synopsis)**
+- **[Bugs resolved](release-notes/5.12.3.md#bugs)**
+- **[Credits](release-notes/5.12.3.md#credits)**
+- **[Feedback](release-notes/5.12.3.md#feedback)**
+
+## CiviCRM 5.12.2
+
+Released April 19, 2019
+
+- **[Synopsis](release-notes/5.12.2.md#synopsis)**
+- **[Bugs resolved](release-notes/5.12.2.md#bugs)**
+- **[Credits](release-notes/5.12.2.md#credits)**
+- **[Feedback](release-notes/5.12.2.md#feedback)**
+
+## CiviCRM 5.12.1
+
+Released April 15, 2019
+
+- **[Synopsis](release-notes/5.12.1.md#synopsis)**
+- **[Bugs resolved](release-notes/5.12.1.md#bugs)**
+- **[Credits](release-notes/5.12.1.md#credits)**
+- **[Feedback](release-notes/5.12.1.md#feedback)**
+
## CiviCRM 5.12.0
Released April 3, 2019
--- /dev/null
+# CiviCRM 5.12.4
+
+Released April 25, 2019
+
+- **[Synopsis](#synopsis)**
+- **[Bugs resolved](#bugs)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?* | |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities? | no |
+| Change the database schema? | no |
+| Alter the API? | no |
+| Require attention to configuration options? | no |
+| Fix problems installing or upgrading to a previous version? | no |
+| Introduce features? | no |
+| **Fix bugs?** | **yes** |
+
+## <a name="bugs"></a>Bugs resolved
+
+- **CiviMail - Fix "Preview" action when using `viewUrl` tokens and mailing hashes. ([dev/core#891](https://lab.civicrm.org/dev/core/issues/891): [14124](https://github.com/civicrm/civicrm-core/pull/14124))**
+
+- **Installer - Fix diagnostic for MySQL `utf8mb4` support. ([dev/core#880](https://lab.civicrm.org/dev/core/issues/880): [14129](https://github.com/civicrm/civicrm-core/pull/14129))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following authors and reviewers:
+
+Wikimedia Foundation - Eileen McNaughton; Australian Greens - Seamus Lee;
+Lighthouse Design and Consulting - Brian Shaughnessy; Electronic Frontier Foundation - Mark Burdett
+
+## <a name="feedback"></a>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`.
--- /dev/null
+# CiviCRM 5.13.0
+
+Released May 1, 2019
+
+- **[Synopsis](#synopsis)**
+- **[Features](#features)**
+- **[Bugs resolved](#bugs)**
+- **[Miscellany](#misc)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?* | |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities? | no |
+| **Change the database schema?** | **yes** |
+| **Alter the API?** | **yes** |
+| **Require attention to configuration options?** | **yes** |
+| **Fix problems installing or upgrading to a previous version?** | **yes** |
+| **Introduce features?** | **yes** |
+| **Fix bugs?** | **yes** |
+
+## <a name="features"></a>Features
+
+### Core CiviCRM
+
+- **Configurable menubar color
+ ([13996](https://github.com/civicrm/civicrm-core/pull/13996))**
+
+ v5.12 introduced a new menu bar. The change also revised the default color
+ scheme (applying a more contemporary palette), but this created stress for
+ some users who were trained to recognize the old menu color. This exposes a
+ new setting for customizing the color scheme, and it changes the default to
+ match previous scheme (be black instead of beige).
+
+- **Menu config screen improvements
+ ([14002](https://github.com/civicrm/civicrm-core/pull/14002))**
+
+ Improves the Menu configuration screen by: making changes to the menu (adding
+ or editing menu items) on the admin screen be reflected immediately, adding
+ links to the related settings pages to reduce confusion about how to configure
+ various aspects of the menu bar and fixing the broken icon picker and missing
+ CiviCRM icon.
+
+- **What to do with the merge screen
+ ([dev/core#824](https://lab.civicrm.org/dev/core/issues/824):
+ [13898](https://github.com/civicrm/civicrm-core/pull/13898) and
+ [13895](https://github.com/civicrm/civicrm-core/pull/13895)) CONTINUES WORK**
+
+ Continues work to clean up the Merge screen by moving toward
+ using contact type icons in a standard way instead of the contact type names
+ and cleaning up alignment.
+
+- **Mutliple activity type filters on activity tab on contact records
+ ([13873](https://github.com/civicrm/civicrm-core/pull/13873))**
+
+ Change include/exclude activity type filters to be multiple select2 widgets so
+ that users can filter my multiple activity types rather than only permitting
+ users to select one activity type with a traditional select field.
+
+- **Added a DB check to prevent deleting exisiting CiviCRM data from database.
+ ([13944](https://github.com/civicrm/civicrm-core/pull/13944))**
+
+ When installing CiviCRM this change adds a check to see if there are existing
+ CiviCRM tables and requires users to manually remove any CiviCRM tables
+ before continuing to prevent users from accidentally deleting tables.
+
+- **Encourage developers to use .then instead of .done
+ ([13982](https://github.com/civicrm/civicrm-core/pull/13982))**
+
+ This change makes it so the Api Explorer generates js code using `.then()`
+ (instead of `.done`) and demonstrates proper errQor handling to encourage
+ developers to use `.then()`.
+
+- **Replace jcalendar instances with datepicker
+ ([dev/core#561](https://lab.civicrm.org/dev/core/issues/561):
+ [13919](https://github.com/civicrm/civicrm-core/pull/13919),
+ [13855](https://github.com/civicrm/civicrm-core/pull/13855),
+ [13965](https://github.com/civicrm/civicrm-core/pull/13965),
+ [13950](https://github.com/civicrm/civicrm-core/pull/13950) and
+ [13918](https://github.com/civicrm/civicrm-core/pull/13918)) CONTINUES WORK**
+
+ Moves from jcalendar to datepicker in the following places: the fulfilled date
+ on the premium tab on back end contribution add/edit screens, the activity
+ tab, the receive_date and the renewal_date fields in Membership forms and the
+ transaction date field on the update pending status task. And updates tests to
+ reflect progress getting rid of jcalendar.
+
+- **Expose sort_name as a dedupe-matchable field
+ ([13864](https://github.com/civicrm/civicrm-core/pull/13864))**
+
+ Allow sort_name to be used in dedupe rules.
+
+- **Standardise setTitle method on forms
+ ([13781](https://github.com/civicrm/civicrm-core/pull/13781))**
+
+ Makes the title of a form available in the buildForm hook.
+
+- **Send action links on any page that extends CRM_Core_Page_Basic thru
+ hook_civicrm_links
+ ([13068](https://github.com/civicrm/civicrm-core/pull/13068))**
+
+ Makes it so extension developers can use hook_civicrm_links to add or remove
+ links from the Relationship Types Administration Page (CiviCRM Navigation
+ Menu->Administer->Customize Data and Screens->Relationship Types) and any
+ other page that extends CRM_Core_Page_Basic.
+
+- **Improve lock handling for mysql 5.7.5+
+ ([CRM-18011](https://issues.civicrm.org/jira/browse/CRM-18011):
+ [13854](https://github.com/civicrm/civicrm-core/pull/13854))**
+
+ Ensures mysql locks are supported on mysql 5.7.5+ and MariaDB '10.0.2'+.
+
+- **Deploy PEAR Log package and upgrade to latest version in the process
+ ([13835](https://github.com/civicrm/civicrm-core/pull/13835))**
+
+ Upgrades the PEAR Log package version and deploys it via
+ composer.
+
+- **Make cacheCode optional in CRM.loadScript
+ ([13824](https://github.com/civicrm/civicrm-core/pull/13824))**
+
+ A cacheCode was recently added to script urls fetched by CRM.getScript(). This
+ is not always desirable e.g. for scripts fetched from an external source, this
+ change makes it so the cacheCode is added by default but can be disabled.
+
+- **Load hooks during upgrade mode
+ ([13551](https://github.com/civicrm/civicrm-core/pull/13551))**
+
+ Allow extensions to load hooks during an upgrade.
+
+- **Checkbox to explicitly change employer when sharing address
+ ([CRM-21008](https://issues.civicrm.org/jira/browse/CRM-21008):
+ [13700](https://github.com/civicrm/civicrm-core/pull/13700))**
+
+ Before this change when an individual was set to share an address with an
+ organization, that organization was set as its current employer. This change
+ adds a checkbox that allows users to explicitly tell civicrm whether the
+ organization is the current employer or not when sharing an address.
+
+- **Report improvements
+ ([CRM-21677](https://issues.civicrm.org/jira/browse/CRM-21677):
+ [13792](https://github.com/civicrm/civicrm-core/pull/13792),
+ [13790](https://github.com/civicrm/civicrm-core/pull/13790) and
+ [13780](https://github.com/civicrm/civicrm-core/pull/13780))**
+
+ These changes improve reports by removing redundant birth date and gender
+ evaluation code.
+
+- **Contact Subtype field at Reports does not support contacts with multiple
+ subtypes ([dev/core/544](https://lab.civicrm.org/dev/core/issues/544):
+ [13158](https://github.com/civicrm/civicrm-core/pull/13158) and
+ [13908](https://github.com/civicrm/civicrm-core/pull/13908))**
+
+ Makes reports support filtering on multiple contact subtypes.
+
+- **Replace all instances of CRM_Core_Fatal with throw new CRM_Core_Exception
+ ([dev/core#560](https://lab.civicrm.org/dev/core/issues/560):
+ [13850](https://github.com/civicrm/civicrm-core/pull/13850)) CONTINUES WORK**
+
+ Updates Cancel Billing & Update Billing screens to use status bounce rather
+ than throw a fatal error.
+
+- **Activity tab performance fix - switch to faster getActivities &
+ getActivitiesCount
+ ([13768](https://github.com/civicrm/civicrm-core/pull/13768))**
+
+ Performance improvement when loading the activity tab for a contact.
+
+- **Add hook findDuplicates
+ ([13234](https://github.com/civicrm/civicrm-core/pull/13234))**
+
+ Adds the hook findDuplicates which provides the ability for extensions to
+ intercept and/or override core duplicate checking when
+ registering/contributing.
+
+- **Deadlocks and performance issues when using smartgroups / ACLs extensively
+ ([dev/core#748](https://lab.civicrm.org/dev/core/issues/748):
+ [13772](https://github.com/civicrm/civicrm-core/pull/13772)) CONTINUES WORK**
+
+ Improves performance by switching the alphabetQuery to use new
+ getSearchSQLParts() function.
+
+- **Improve data when known time-dependent-failing test fails
+ ([13964](https://github.com/civicrm/civicrm-core/pull/13964))**
+
+ Improves output data when time dependent tests fail.
+
+- **Use TempTable methods.
+ ([13880](https://github.com/civicrm/civicrm-core/pull/13880),
+ [13865](https://github.com/civicrm/civicrm-core/pull/13865),
+ [13819](https://github.com/civicrm/civicrm-core/pull/13819),
+ [13848](https://github.com/civicrm/civicrm-core/pull/13848),
+ [13847](https://github.com/civicrm/civicrm-core/pull/13847) and
+ [13703](https://github.com/civicrm/civicrm-core/pull/13703))**
+
+ These changes move towards using the TempTable class to improve
+ naming standards, including but not limited to the following places: when
+ creating the dedupe table, on the contribution detail report, on the
+ bookkeeping report and when debugging.
+
+- **Allow extensions to enable validate.tpl
+ ([13961](https://github.com/civicrm/civicrm-core/pull/13961))**
+
+ This change makes it possible for extensions to add a validate.tpl which
+ provides front end javascript validation, which improves user experience by
+ making it so users do not have to refresh the page to see validation issues.
+
+- **Promise polyfill for older browsers
+ ([13955](https://github.com/civicrm/civicrm-core/pull/13955))**
+
+ Loads a polyfill for IE aQnd other outdated browsers so developers can use
+ native js Promises in our code.
+
+### CiviCase
+
+- **Case Activity Assignment Restriction
+ ([dev/core#641](https://lab.civicrm.org/dev/core/issues/641):
+ [13541](https://github.com/civicrm/civicrm-core/pull/13541))**
+
+ Makes it possible to restrict the assignment of case activities to
+ a group or to contacts having user accounts.
+
+### CiviContribute
+
+- **Add cancel_reason field to civicrm_contribution_recur table
+ ([dev/core#830](https://lab.civicrm.org/dev/core/issues/830):
+ [13930](https://github.com/civicrm/civicrm-core/pull/13930),
+ [13999](https://github.com/civicrm/civicrm-core/pull/13999), and
+ [14164](https://github.com/civicrm/civicrm-core/pull/14164))**
+
+ Adds a recur cancel reason field `civicrm_contribution_recur.cancel_reason
+ field`.
+
+- **Add ID / Test ID for payment processors to list - makes setup of IPNs much
+ easier! ([13869](https://github.com/civicrm/civicrm-core/pull/13869))**
+
+ Adds a column for payment processor id and test payment processor id to the
+ table of "Settings - Payment Processors" page so that users can more easily
+ access these ids.
+
+- **Add payment_processor column/filter to recurring contribution report
+ ([13699](https://github.com/civicrm/civicrm-core/pull/13699))**
+
+ On the "Recurring Contribution" report, this adds "Payment Processor" as an
+ option to the Column and Filter tabs enabling users to filter this report by
+ "Payment Processor" and/or include a column for "Payment Processor".
+
+- **Payment processor names: separate internal and external usage
+ ([dev/financial#2](https://lab.civicrm.org/dev/financial/issues/2):
+ [13995](https://github.com/civicrm/civicrm-core/pull/13995) and
+ [13954](https://github.com/civicrm/civicrm-core/pull/13954))**
+
+ Adds the field `payment_processor.title` to the schema and makes it
+ translatable.
+
+- **Support paying refunds
+ ([dev/financial#38](https://lab.civicrm.org/dev/financial/issues/38):
+ [13952](https://github.com/civicrm/civicrm-core/pull/13952)) BEGINS WORK**
+
+ Adds a PaymentProcessor.refund API which makes it possible for payment
+ processor extension authors to have their extensions issue refund payments.
+
+- **Add minimal PaymentProcessor.pay api
+ ([13953](https://github.com/civicrm/civicrm-core/pull/13953))**
+
+ Adds a new api PaymentProcessor.pay.
+
+- **Include lower level data when throwing an exception on payment processor.pay
+ ([14006](https://github.com/civicrm/civicrm-core/pull/14006))**
+
+ Improves error processing when using the PaymentProcessor.pay api.
+
+- **Add billingblock region to event registration thankyou to match contribution
+ thankyou ([13762](https://github.com/civicrm/civicrm-core/pull/13762))**
+
+ This change makes it so one can use the same method to
+ replace billingblock for both event and contribution thankyou workflows.
+
+- **CQ: Refactor Recurring Contribution Forms
+ ([dev/core#846](https://lab.civicrm.org/dev/core/issues/846):
+ [13940](https://github.com/civicrm/civicrm-core/pull/13940)) BEGINS WORK**
+
+ Begins work to refactor recurring contribution forms specifically by
+ rationalizing url variables into shared parent for recurring contribution
+ forms.
+
+- **Use label not name for payment processor type
+ ([13885](https://github.com/civicrm/civicrm-core/pull/13885))**
+
+ On the "Settings - Payment Processor" page changes the "Processor Type" column
+ to use the label instead of the name.
+
+- **Improve ContributionPage.validate api
+ ([13798](https://github.com/civicrm/civicrm-core/pull/13798))**
+
+ Makes it so that one can use the ContributionPage validate api on POST, for
+ example when using Paypal Checkout which calls a Promise, the
+ ContributionPage.validate api could be called to determine whether to proceed
+ after the button is pushed.
+
+- **Add pseudoconstant support for payment_processor_id on ContributionRecur
+ ([13698](https://github.com/civicrm/civicrm-core/pull/13698))**
+
+- **Allow payment processor to determine the text around 'continue'
+ ([13787](https://github.com/civicrm/civicrm-core/pull/13787))**
+
+
+### CiviEvent
+
+- **Expose Registered by Participant Name field to participant report
+ ([dev/core#835](https://lab.civicrm.org/dev/core/issues/835):
+ [13936](https://github.com/civicrm/civicrm-core/pull/13936))**
+
+ Exposes "Registered by Participant Name" field to participant reports.
+
+### CiviMail
+
+- **Report results don't show inactive campaigns
+ ([dev/core#491](https://lab.civicrm.org/dev/core/issues/491):
+ [13383](https://github.com/civicrm/civicrm-core/pull/13383))**
+
+ Standardizes the way campaign fields are added to the "Mailing Summary"
+ report.
+
+- **Use Mailing.preview API to display mailing in browser
+ ([14163](https://github.com/civicrm/civicrm-core/pull/14163))**
+
+ This resolves an error on sites with Flexmailer when an anonymous visitor
+ views a mailing in the browser.
+
+### CiviMember
+
+- **Membership form address fields for payment processors
+ ([13802](https://github.com/civicrm/civicrm-core/pull/13802))**
+
+ Standardizes processing and validating address parameters on the
+ Membership form.
+
+### Wordpress Integration
+
+- **Cleaner front-end URLs
+ ([144](https://github.com/civicrm/civicrm-wordpress/pull/144))**
+
+ Makes Wordpress URLs have the same structure as Drupal URLs ex:
+ `https://domain.tld/civicrm/contribute/transact/?reset=1&id=1 as opposed to
+ old` Wordpress URLs that looked like
+ `https://domain.tld/civicrm/?page=CiviCRM&q=civicrm/contribute/transact&reset=1&id=1`
+
+## <a name="bugs"></a>Bugs resolved
+
+### Core CiviCRM
+
+- **Add a test matrix for E2E tests on each CMS
+ ([infra/ops#878](https://lab.civicrm.org/infra/ops/issues/878):
+ [13810](https://github.com/civicrm/civicrm-core/pull/13810),
+ [13826](https://github.com/civicrm/civicrm-core/pull/13826) and
+ [13811](https://github.com/civicrm/civicrm-core/pull/13811)) CONTINUES WORK**
+
+ These changes resolve bugs when running E2E tests on WordPress moving the
+ project towards the goal of having a test matrix for E2E tests for each CMS
+ that CiviCRM is compatible with.
+
+- **Use the correct membership date for the notification that appear after
+ completing the membership payment in case pre hook is used
+ ([dev/core#288](https://lab.civicrm.org/dev/core/issues/288):
+ [12583](https://github.com/civicrm/civicrm-core/pull/12583))**
+
+ This change ensures that any changes made to the membership dates using
+ hook_civicrm_pre are taken into account when generating the membership payment
+ notification.
+
+- **Custom Field checkbox value renders empty if the values are randomly sorted
+ ([dev/core#499](https://lab.civicrm.org/dev/core/issues/499):
+ [13051](https://github.com/civicrm/civicrm-core/pull/13051))**
+
+ This change ensures that the values selected by a user for alphanumeric
+ checkbox custom fields render properly in view mode.
+
+- **New Organisation: "Check For Matching Contact (S)" button does not find
+ matching records ([dev/core#570](https://lab.civicrm.org/dev/core/issues/570):
+ [13398](https://github.com/civicrm/civicrm-core/pull/13398))**
+
+ This change ensures that "Check For Matching Contact(s)" retrieves
+ contacts for users with limited permissions.
+
+- **Soft Credits Multiply GIft Amount in Contribution Detail Report
+ ([dev/core#655](https://lab.civicrm.org/dev/core/issues/655):
+ [13906](https://github.com/civicrm/civicrm-core/pull/13906))**
+
+ This change updates the Contribution Detail report so that the "Total Amount"
+ field is not summed because the summing of the "Total Amount" field was
+ leading to the "Total Amount" field for contributions with multiple soft
+ credits to be multiplied by the number of soft credits.
+
+- **Add new indexes when updating log table schema regardless of engine change
+ ([dev/core#664](https://lab.civicrm.org/dev/core/issues/664):
+ [13462](https://github.com/civicrm/civicrm-core/pull/13462))**
+
+ This change makes it so that when the alterLogTables hook defines a new index
+ it is applied regardless of the engine.
+
+- **Contacts -> New Email give Unknown Error in Smarty when Allow Mail to be
+ sent from logged in contact's email address disabled
+ ([dev/core#688](https://lab.civicrm.org/dev/core/issues/688):
+ [13508](https://github.com/civicrm/civicrm-core/pull/13508))**
+
+ This change fixes an error "Warning: Smarty error" when attempting to send an
+ email from Contacts -> New Email for sites with "Allow Mail to be sent from
+ logged in user" disabled.
+
+- **Edit contribution : wrong decimal separator on total_amount for
+ participant(s) ([dev/core#706](https://lab.civicrm.org/dev/core/issues/706):
+ [13554](https://github.com/civicrm/civicrm-core/pull/13554))**
+
+ This change ensures that the decimal separator on total_amount field for
+ participant(s) is displayed as it is configured on the site.
+
+- **Contact Report: The filter by the custom datetime field with "Today" option
+ doesn't find matching contacts
+ ([dev/core#709](https://lab.civicrm.org/dev/core/issues/709):
+ [13567](https://github.com/civicrm/civicrm-core/pull/13567))**
+
+ This change ensures that on the Contact Report, when using a filter for a
+ custom datetime field the "Today" option properly filters results.
+
+- **Address API incorrectly sets state_province_id if multiple countries have
+ same state name / abbreviation
+ ([dev/core#725](https://lab.civicrm.org/dev/core/issues/725):
+ [13938](https://github.com/civicrm/civicrm-core/pull/13938))**
+
+ This change fixes a regression so that the Address API country_id parameter
+ works with an abbreviation or numeric id.
+
+- **Advanced Search: There is an Internal Server Error (500) when the user tries
+ to search by the "Mailing List" group type
+ ([dev/core#726](https://lab.civicrm.org/dev/core/issues/726):
+ [13603](https://github.com/civicrm/civicrm-core/pull/13603) and
+ [13888](https://github.com/civicrm/civicrm-core/pull/13888))**
+
+ Fixes a bug where searching by a group type (ex: Mailing List) on the Advanced
+ Search form would result in a fatal error so that this search runs as
+ expected.
+
+- **Notes: It isn't possible to edit note if the user uploaded an image larger
+ than 3 MByte(s) ([dev/core#740](https://lab.civicrm.org/dev/core/issues/740):
+ [13640](https://github.com/civicrm/civicrm-core/pull/13640))**
+
+ This change makes it so that it is possible to edit a note field
+ that includes images larger than 3 MByte(s).
+
+- **Custom field caching is not group-specific
+ ([dev/core#755](https://lab.civicrm.org/dev/core/issues/755):
+ [13900](https://github.com/civicrm/civicrm-core/pull/13900))**
+
+ Adds the $groupTitle parameter to the cache key for custom fields, so field
+ names that exist in multiple groups will be cached separately.
+
+- **Warning: A non-numeric value encountered in ...
+ ([dev/core#788](https://lab.civicrm.org/dev/core/issues/788):
+ [13795](https://github.com/civicrm/civicrm-core/pull/13795))**
+
+ This change fixes several warnings "Warning: A non-numeric value encountered
+ in ..." on contribution pages & event registrations forms.
+
+- **PHP Warning "explode() expects parameter 2 to be string, array given" for
+ multi-value country fields
+ ([dev/core#795](https://lab.civicrm.org/dev/core/issues/795):
+ [13858](https://github.com/civicrm/civicrm-core/pull/13858))**
+
+ Fixes a PHP warning when updating a multiselect country field.
+
+- **Prefix/suffix select2 renders oddly on public-facing pages
+ ([dev/core#798](https://lab.civicrm.org/dev/core/issues/798):
+ [13816](https://github.com/civicrm/civicrm-core/pull/13816))**
+
+ This change makes the Prefix/suffix select2's render full height (as opposed
+ to squished) in contribution/event profiles.
+
+- **DB Error:: Already exists during renewing membership automatically
+ ([dev/core#806](https://lab.civicrm.org/dev/core/issues/806):
+ [13852](https://github.com/civicrm/civicrm-core/pull/13852))**
+
+ Fixes a DB error "already exists" when recording recurring payments with taxes
+ enabled.
+
+- **Autocomplete select list disabled options
+ ([dev/core#811](https://lab.civicrm.org/dev/core/issues/811):
+ [13859](https://github.com/civicrm/civicrm-core/pull/13859))**
+
+ This change ensures that for select fields with autocomplete, only enabled
+ options autocomplete.
+
+- **Search results: Actions: Export contacts: DB Error: Syntax error occurs when
+ not all necessary fields are selected
+ ([dev/core#819](https://lab.civicrm.org/dev/core/issues/819):
+ [13889](https://github.com/civicrm/civicrm-core/pull/13889))**
+
+ Resolves a fatal database error when attempting to export contacts without
+ selecting any fields to be exported.
+
+- **Value in the "Contact Type" field disappears when the user tries to edit
+ Contact Details ([dev/core#823](https://lab.civicrm.org/dev/core/issues/823):
+ [13945](https://github.com/civicrm/civicrm-core/pull/13945))**
+
+ Ensures that when a user uses a profile with a contact sub type field to
+ update a contact that the contact sub type field pre-populates as expected.
+
+- **Notice error while creating smart group using Contribution Aggregate custom
+ search ([dev/core#837](https://lab.civicrm.org/dev/core/issues/837):
+ [13921](https://github.com/civicrm/civicrm-core/pull/13921))**
+
+ Resolves "Notice: Undefined index:" errors when creating a smart group using
+ Contribution Aggregate custom search.
+
+- **Notice error when deleting profile
+ ([dev/core#840](https://lab.civicrm.org/dev/core/issues/840):
+ [13926](https://github.com/civicrm/civicrm-core/pull/13926))**
+
+ Resolves a "Undefined property" notice when deleting a profile.
+
+- **CQ: Use Guzzle as our preferred way to retrieve via http
+ ([dev/core#849](https://lab.civicrm.org/dev/core/issues/849):
+ [13946](https://github.com/civicrm/civicrm-core/pull/13946))**
+
+ Replaces get_headers functions call with Guzzle HTTP request to prevent timing
+ out.
+
+- **Queries combining multiple text labels using REGEXP do not escape regular
+ expression metacharacters
+ ([dev/core#433](https://lab.civicrm.org/dev/core/issues/433):
+ [12998](https://github.com/civicrm/civicrm-core/pull/12998))**
+
+ Ensures that querying for fee levels that contain regex metacharacters will
+ match.
+
+- **Fix status type (error, not fail) for CRM_Core_Session::setStatus
+ ([13943](https://github.com/civicrm/civicrm-core/pull/13943))**
+
+ Fixes an incorrect status for setMessage which was using 'fail' (which is not
+ a valid status) instead of 'error'.
+
+- **Covert the CRM_Core_Error::fatal calls to exceptions when trying to access
+ Contact Photos ([13917](https://github.com/civicrm/civicrm-core/pull/13917))**
+
+ Cleaner errors when attempting to access contact photos fails.
+
+- **CiviCRM API, lookup state_province_id options based on country parameter if
+ present, or default country
+ ([13596](https://github.com/civicrm/civicrm-core/pull/13596))**
+
+ Ensures the CiviCRM Address API correctly sets state_province_id for states
+ whose name/abbreviation exist in multiple countries.
+
+- **Fix php 7.2 count notice
+ ([13877](https://github.com/civicrm/civicrm-core/pull/13877))**
+
+ Fixes a "Warning: count()" error on the "Import Contacts" Summary step screen.
+
+- **New Organisation: "Contact Type" dropdown disappears when fields validation
+ occurs ([dev/core#699](https://lab.civicrm.org/dev/core/issues/699):
+ [13545](https://github.com/civicrm/civicrm-core/pull/13545))**
+
+ Fixes a bug where when creating a new organization, if the form fails
+ validation the "Contact Sub Type" dropdown disappears.
+
+- **Fix contact.merge api to pass check_permissions parameter through to the
+ deeper layer ([13807](https://github.com/civicrm/civicrm-core/pull/13807))**
+
+ Fixes a bug where calling Contact.merge from php will leave the merged contact
+ unmerged if the logged in user does not have the delete contacts permission
+
+- **checkEditInboundEmailsPermissions should be a static function
+ ([13805](https://github.com/civicrm/civicrm-core/pull/13805))**
+
+- **DB error "no such field" when executing actions on a sorted contact search
+ result with search profile
+ ([dev/core#502](https://lab.civicrm.org/dev/core/issues/502):
+ [13884](https://github.com/civicrm/civicrm-core/pull/13884))**
+
+- **Transfer picks up the deleted contact ID basically transferring to the wrong
+ contact ([dev/core#314](https://lab.civicrm.org/dev/core/issues/314):
+ [12639](https://github.com/civicrm/civicrm-core/pull/12639))**
+
+- **Fix bug whereby sorting by state province gives an error in search builder
+ ([13748](https://github.com/civicrm/civicrm-core/pull/13748))**
+
+- **Fix Contact.create calls to respect passed in variables & variables set via
+ hook for sort_name & display_name
+ ([13863](https://github.com/civicrm/civicrm-core/pull/13863))**
+
+- **Do not cause a fatal error if no contact_id field for a note is filled in
+ when viewing contact notes
+ ([13910](https://github.com/civicrm/civicrm-core/pull/13910))**
+
+- **Disable phpcs checking in the Crypt files where mcrypt functions are
+ ([14031](https://github.com/civicrm/civicrm-core/pull/14031))**
+
+- **Fix 4.7.31 Upgrade in multilingual mode
+ ([14003](https://github.com/civicrm/civicrm-core/pull/14003))**
+
+- **Optimise the loading of the CiviCRM Deduplication Exception page
+ ([13435](https://github.com/civicrm/civicrm-core/pull/13435))**
+
+- **Unfork Zetacomponents mail and use patch to apply differences
+ ([13934](https://github.com/civicrm/civicrm-core/pull/13934))**
+
+- **Update lockfile to take into account of civicrm/zetacomponents-mail#4 being
+ merged ([13822](https://github.com/civicrm/civicrm-core/pull/13822))**
+
+- **Upgrade zetacomponents/base and zetacomponents/mail
+ ([13799](https://github.com/civicrm/civicrm-core/pull/13799))**
+
+- **Smart group with uf_group_id does not load contacts with same search profile
+ ([dev/core#771](https://lab.civicrm.org/dev/core/issues/771):
+ [13742](https://github.com/civicrm/civicrm-core/pull/13742))**
+
+- **Fix Deprecation notice for use of `while` in PHP7.2 in bin/cli.class.php
+ ([dev/core#907](https://lab.civicrm.org/dev/core/issues/907):
+ [14155](https://github.com/civicrm/civicrm-core/pull/14155))**
+
+### CiviCase
+
+- **PR 13333 breaks the Save and New button on a new case
+ ([dev/core#904](https://lab.civicrm.org/dev/core/issues/904):
+ [14145](https://github.com/civicrm/civicrm-core/pull/14145))**
+
+ This reverts a change making the Save and New button `submitOnce`, as that
+ prevented the button from opening a new case form after saving.
+
+- **Creating a new relationship type while editing case type definition adds a
+ blank row in the roles section
+ ([dev/core#784](https://lab.civicrm.org/dev/core/issues/784):
+ [13785](https://github.com/civicrm/civicrm-core/pull/13785))**
+
+- **PHP warnings on Case Dashboard and Find Cases
+ ([13998](https://github.com/civicrm/civicrm-core/pull/13998))**
+
+### CiviContribute
+
+- **Can't self-service cancel a recurring contribution made while you're logged
+ in ([dev/core#571](https://lab.civicrm.org/dev/core/issues/571):
+ [13237](https://github.com/civicrm/civicrm-core/pull/13237))**
+
+ This change makes it so that users without "edit contributions" permission can
+ edit their own recurring contribution subscriptions if they are logged in,
+ before this change they could only edit their own contribution subscriptions
+ thru a checksum link.
+
+- **Possible paypal fix to avoid sending 500 errors from ipn triggerred by
+ one-off payment
+ ([13867](https://github.com/civicrm/civicrm-core/pull/13867))**
+
+ Fixes paypal system sending a warning email when there is a failed IPN call
+ (500 errors from ipn triggered by non-recurring payment).
+
+- **Missing links to cancel recurring payments (regression .. sorta)
+ ([dev/core#704](https://lab.civicrm.org/dev/core/issues/704):
+ [13935](https://github.com/civicrm/civicrm-core/pull/13935))**
+
+ Fixes a 5.8 regression where the cancel link became unavailable on recurring
+ contributions with no attached payment processor id.
+
+- **Improve flushing after creating a processor so it can be used for a
+ recurring in the same run
+ ([14009](https://github.com/civicrm/civicrm-core/pull/14009))**
+
+ This change ensures tests run properly by flushing caches when creating a
+ payment processor.
+
+- **Flush ContributionRecur static cache when flushing processors
+ ([13962](https://github.com/civicrm/civicrm-core/pull/13962))**
+
+ Ensures a (new) static cache is flushed when creating a processor, mostly
+ useful for unit testing.
+
+- **Display test contributions when viewing contributions related to a test
+ recurring contribution
+ ([13779](https://github.com/civicrm/civicrm-core/pull/13779))**
+
+### CiviEvent
+
+- **Cancelling or An Error during event registration payment should cancel all
+ additional participates
+ ([dev/core#253](https://lab.civicrm.org/dev/core/issues/253):
+ [12457](https://github.com/civicrm/civicrm-core/pull/12457))**
+
+ This fixes a bug where if a user was registering for an event with additional
+ participants, and for some reason the payment failed or the user decided to
+ cancel the payment, only the main user participant record was set to
+ "canceled", the rest of the additional participants would have the status
+ "pending incomplete transaction" so that all participant statuses are changed
+ to "canceled".
+
+- **Event Cart: Fix PHP 7.2 fatal error (pass by ref)
+ ([13927](https://github.com/civicrm/civicrm-core/pull/13927))**
+
+ Fixes a Fatal error when using Event cart and PHP 7.2, specifically when
+ checking out.
+
+- **CRM/Event - Fix participant note search parameter being ignored
+ ([13697](https://github.com/civicrm/civicrm-core/pull/13697))**
+
+ This change ensures that when searching by "Participant Note" on the "Search
+ Builder form the filter is applied.
+
+- **BAO_Participant - Use default status if not specified for create
+ ([13875](https://github.com/civicrm/civicrm-core/pull/13875))**
+
+ This change fixes a api4 test failure by ensuring that the default value of
+ 'status_id' is respected by the Participant BAO. Before this change the
+ 'status_id' had a default value and thus was not required but the BAO would
+ not work correctly if a 'status_id' was not supplied. After this change the
+ 'status_id' default is respected and the BAO works regardless of whether the
+ 'status_id' is supplied.
+
+### CiviGrant
+
+- **PHP Error on Grant Detail Report
+ ([13883](https://github.com/civicrm/civicrm-core/pull/13883))**
+
+ Fixes a PHP error when viewing the "Grant Detail Report" for sites running PHP
+ 7.2.
+
+### CiviMail
+
+- **Do not track CSS URLs
+ ([dev/core#836](https://lab.civicrm.org/dev/core/issues/836):
+ [13920](https://github.com/civicrm/civicrm-core/pull/13920))**
+
+ Ensures CiviMail does not convert css URLs into trackable URLs.
+
+- **Restore support for preview of "mailing"/"action" tokens via
+ TokenProcessor/Flexmailer
+ ([14156](https://github.com/civicrm/civicrm-core/pull/14156))**
+
+### CiviMember
+
+- **Disabling or deleting Expired status breaks membership status update
+ ([13259](https://github.com/civicrm/civicrm-core/pull/13259))**
+
+ This change ensures that deleting, disabling or renaming the "Expired"
+ membership status does not cause issues when running the "Membership status
+ processor" scheduled job. Before this change deleting, disabling or renaming
+ the "Expired" membership status would result in the "Membership status
+ processor" scheduled job failing with the message `Finished execution of
+ Membership status processor with result: Failure, Error message: A fatal error
+ was triggered: One of parameters (value: ) is not of the type Integer`.
+
+- **Fatal error to exception on Membership BAO
+ ([13774](https://github.com/civicrm/civicrm-core/pull/13774))**
+
+ This change improves experience by throwing an exception instead of a fatal
+ error.
+
+### Backdrop Integration
+
+- **civicrm/admin/setting/uf - Fix advice about Backdrop Views
+ ($database_prefix)
+ ([13803](https://github.com/civicrm/civicrm-core/pull/13803))**
+
+ This change updates the advice for configuring views on the
+ `civicrm/admin/setting/uf` form so that it works for Backdrop sites.
+
+### Drupal Integration
+
+- **Drupal8: Can't upload images via CKEditor/kcfinder
+ ([dev/drupal#42](https://lab.civicrm.org/dev/drupal/issues/42):
+ [242](https://github.com/civicrm/civicrm-packages/pull/242))**
+
+ Makes it so on Drupal 8 sites users can upload and browse images in KCFinder.
+
+## <a name="misc"></a>Miscellany
+
+- **Fix e-notice in IDS
+ ([247](https://github.com/civicrm/civicrm-packages/pull/247))**
+
+- **Update 7.x Drupal code to be that of the new coder style
+ ([571](https://github.com/civicrm/civicrm-drupal/pull/571))**
+
+- **Fix up to newer coder style
+ ([69](https://github.com/civicrm/civicrm-backdrop/pull/69))**
+
+- **Remove now-obsolete additionalFromClause parameter from prepareOrderBy
+ ([13874](https://github.com/civicrm/civicrm-core/pull/13874))**
+
+- **Remove switch statement that no longer switches
+ ([13886](https://github.com/civicrm/civicrm-core/pull/13886))**
+
+- **Add unit test for exporting with incomplete data
+ ([13904](https://github.com/civicrm/civicrm-core/pull/13904))**
+
+- **Delete webtests
+ ([13861](https://github.com/civicrm/civicrm-core/pull/13861))**
+
+- **Add a class to handle test entities consistently
+ ([13814](https://github.com/civicrm/civicrm-core/pull/13814))**
+
+- **Fix up composer for composer 2.0 compatibility
+ ([13872](https://github.com/civicrm/civicrm-core/pull/13872))**
+
+- **Remove log files as now supplied by composer
+ ([244](https://github.com/civicrm/civicrm-packages/pull/244), [245](https://github.com/civicrm/civicrm-packages/pull/245))**
+
+- **Remove htmlpurifier from within the IDS to facilitate it being provided by
+ composer ([246](https://github.com/civicrm/civicrm-packages/pull/246))**
+
+- **Remove Log.php require_once statements
+ ([13842](https://github.com/civicrm/civicrm-core/pull/13842))**
+
+- **Remove amavisd now that it is removed from the packages repository
+ ([243](https://github.com/civicrm/civicrm-packages/pull/243), [13841](https://github.com/civicrm/civicrm-core/pull/13841))**
+
+- **Upgrade htmlpurifier to 4.10 to support PHP7.2 and install via composer
+ ([13840](https://github.com/civicrm/civicrm-core/pull/13840))**
+
+- **Refactor CRM_Contact_Form_Task_PDFLetterCommon
+ ([13892](https://github.com/civicrm/civicrm-core/pull/13892))**
+
+- **Reformat test files for array format
+ ([13862](https://github.com/civicrm/civicrm-core/pull/13862))**
+
+- **Logging - attempt to fix tests
+ ([13832](https://github.com/civicrm/civicrm-core/pull/13832))**
+
+- **Try Reverting commit removing require once to see if it fixes the problem
+ for api4 ([13870](https://github.com/civicrm/civicrm-core/pull/13870))**
+
+- **Import date test
+ ([13823](https://github.com/civicrm/civicrm-core/pull/13823))**
+
+- **Add shared parent for ContributionRecur forms
+ ([13931](https://github.com/civicrm/civicrm-core/pull/13931))**
+
+- **Update PHPWord Patches to match the latest versions of their code
+ ([13923](https://github.com/civicrm/civicrm-core/pull/13923))**
+
+- **Extract assignPaymentFields
+ ([13957](https://github.com/civicrm/civicrm-core/pull/13957))**
+
+- **Move code to assign tax information into shared parent
+ ([13899](https://github.com/civicrm/civicrm-core/pull/13899))**
+
+- **Update test to reflect recently merged PR lower permission to access
+ dedupecheck ([13866](https://github.com/civicrm/civicrm-core/pull/13866))**
+
+- **Test fix ([13856](https://github.com/civicrm/civicrm-core/pull/13856))**
+
+- **Add unit testing for activity creation when cancelling a recurring, related
+ cleanup ([14000](https://github.com/civicrm/civicrm-core/pull/14000))**
+
+- **Improve test coverage for CRM_Utils_Color::getRgb()
+ ([14007](https://github.com/civicrm/civicrm-core/pull/14007))**
+
+- **Add in tests of purifying HTML output
+ ([13845](https://github.com/civicrm/civicrm-core/pull/13845))**
+
+- **Remove more instances of ->free()
+ ([dev/core#562](https://lab.civicrm.org/dev/core/issues/562):
+ [13786](https://github.com/civicrm/civicrm-core/pull/13786)) CONTINUES WORK**
+
+- **(NFC) Ensure phpcs ignores eval notice in these files as it is required
+ ([14032](https://github.com/civicrm/civicrm-core/pull/14032))**
+
+- **(NFC) Lint additional php files up to the new coder standard
+ ([14025](https://github.com/civicrm/civicrm-core/pull/14025))**
+
+- **Port code style fixes to 5.13 from master
+ ([14026](https://github.com/civicrm/civicrm-core/pull/14026))**
+
+- **(NFC) Update CRM/Core CRM/Custom CRM/Dedupe to match the new coder style
+ ([14023](https://github.com/civicrm/civicrm-core/pull/14023))**
+
+- **(NFC) Update CRM/Event folder for the new coder style
+ ([14019](https://github.com/civicrm/civicrm-core/pull/14019))**
+
+- **(NFC) update CRM/Contribute to be the new coder standard
+ ([14021](https://github.com/civicrm/civicrm-core/pull/14021))**
+
+- **(NFC) Update CRM/Friend CRM/Grant CRM/Group CRM/Mailing to be up to d…
+ ([14016](https://github.com/civicrm/civicrm-core/pull/14016))**
+
+- **(NFC) Update CRM/Badge CRM/Campaign CRM/Case to be up to date with a …
+ ([14017](https://github.com/civicrm/civicrm-core/pull/14017))**
+
+- **(NFC) Update CRM/Cxn CRM/Dashlet CRM/Export CRM/Extension and CRM/Fin…
+ ([14018](https://github.com/civicrm/civicrm-core/pull/14018))**
+
+- **[NFC] Short array syntax - auto convert settings dir
+ ([14005](https://github.com/civicrm/civicrm-core/pull/14005))**
+
+- **(NFC) SchemaStructure.php - Fix up mismatch between stored+generated code
+ ([14046](https://github.com/civicrm/civicrm-core/pull/14046))**
+
+- **Arg I put these fixes in 5.12 & master while trying for 5.13
+ ([14036](https://github.com/civicrm/civicrm-core/pull/14036))**
+
+- **(NFC) Update CRM/Member CRM/Note CRM/Logging CRM/Import and CRM/Price…
+ ([13992](https://github.com/civicrm/civicrm-core/pull/13992))**
+
+- **(REF) CRM_Core_Resources - Move hook declaration from addCoreResources() to
+ Container.php ([14008](https://github.com/civicrm/civicrm-core/pull/14008))**
+
+- **(NFC) Update CRM/Activity CRM/Admin and CRM/Batch folders to be the f…
+ ([13990](https://github.com/civicrm/civicrm-core/pull/13990))**
+
+- **(NFC) Update coding style in PCP, Pledge, Profile, Queue, Report folders
+ ([13987](https://github.com/civicrm/civicrm-core/pull/13987))**
+
+- **(NFC) Update CRM/SMS/ CRM/UF/ CRM/Upgrade/ CRM/Tag/ to be up to speed…
+ ([13986](https://github.com/civicrm/civicrm-core/pull/13986))**
+
+- **(NFC) Bring CRM/Utils folder up to future coder standards
+ ([13985](https://github.com/civicrm/civicrm-core/pull/13985))**
+
+- **(NFC) Set _log and _tableName variables to be public
+ ([13988](https://github.com/civicrm/civicrm-core/pull/13988))**
+
+- **Grammar fixes
+ ([13960](https://github.com/civicrm/civicrm-core/pull/13960))**
+
+- **Update Unit test styling to cover the future coder version
+ ([13983](https://github.com/civicrm/civicrm-core/pull/13983))**
+
+- **(NFC) Fix location of comment to match future coder version
+ ([13984](https://github.com/civicrm/civicrm-core/pull/13984))**
+
+- **(NFC) Bring up API folder to style of future coder checker
+ ([13980](https://github.com/civicrm/civicrm-core/pull/13980))**
+
+- **(NFC) Upgrade Civi Folder to the new coder version
+ ([13981](https://github.com/civicrm/civicrm-core/pull/13981))**
+
+- **(NFC) Update various files to pass future civicrm/coder ruleset
+ ([13979](https://github.com/civicrm/civicrm-core/pull/13979))**
+
+- **(NFC) Update various files to pass current phpcs
+ ([13978](https://github.com/civicrm/civicrm-core/pull/13978))**
+
+- **[NFC] Reformat tricksy file CRM_Mailing_BAO_Mailing
+ ([13973](https://github.com/civicrm/civicrm-core/pull/13973))**
+
+- **[NFC] Reformat tricksy file CRM/Contribute/Import/Form/MapField
+ ([13974](https://github.com/civicrm/civicrm-core/pull/13974))**
+
+- **[NFC] short array syntax Autoformat - just the tricksy bits of CRM/Activity
+ ([13969](https://github.com/civicrm/civicrm-core/pull/13969))**
+
+- **[NFC] array format tricksie file CRM/Admin/Form/MessageTemplates
+ ([13970](https://github.com/civicrm/civicrm-core/pull/13970))**
+
+- **[NFC] array format tricksie file CRM_Utils_Rest
+ ([13971](https://github.com/civicrm/civicrm-core/pull/13971))**
+
+- **[NFC] array formatting tricksy tricksie file (another CRM/UF/Form/Group.php)
+ ([13972](https://github.com/civicrm/civicrm-core/pull/13972))**
+
+- **[NFC] short array syntax Autoformat - just CRM/ACL dir
+ ([13968](https://github.com/civicrm/civicrm-core/pull/13968))**
+
+- **[NFC] Short array syntax - auto-format CRM directory
+ ([13915](https://github.com/civicrm/civicrm-core/pull/13915))**
+
+- **[REF] extract token functions
+ ([13967](https://github.com/civicrm/civicrm-core/pull/13967))**
+
+- **Test fix ([13949](https://github.com/civicrm/civicrm-core/pull/13949))**
+
+- **Payment test cleanup
+ ([13924](https://github.com/civicrm/civicrm-core/pull/13924))**
+
+- **(NFC) Fix mode on files
+ ([13933](https://github.com/civicrm/civicrm-core/pull/13933))**
+
+- **[NFC] Cleanup DAO factory classes for code standards
+ ([13922](https://github.com/civicrm/civicrm-core/pull/13922))**
+
+- **NFC - Short array syntax - auto-convert ang dir
+ ([13912](https://github.com/civicrm/civicrm-core/pull/13912))**
+
+- **NFC - Short array syntax - auto-convert Civi dir
+ ([13911](https://github.com/civicrm/civicrm-core/pull/13911))**
+
+- **[NFC] Short array syntax - auto-convert api dir
+ ([13909](https://github.com/civicrm/civicrm-core/pull/13909))**
+
+- **Minor code cleanup
+ ([13839](https://github.com/civicrm/civicrm-core/pull/13839))**
+
+- **Minor code cleanup - remove unnecessary ids declaration
+ ([13838](https://github.com/civicrm/civicrm-core/pull/13838))**
+
+- **Code cleanup on membership block loop
+ ([13851](https://github.com/civicrm/civicrm-core/pull/13851))**
+
+- **[NFC] IDE formatting only
+ ([13896](https://github.com/civicrm/civicrm-core/pull/13896))**
+
+- **Minor code cleanups around invoicing assignment
+ ([13857](https://github.com/civicrm/civicrm-core/pull/13857))**
+
+- **[nfc] Reset entire session object between tests
+ ([13878](https://github.com/civicrm/civicrm-core/pull/13878))**
+
+- **Fixing formatting of contributors section
+ ([13860](https://github.com/civicrm/civicrm-core/pull/13860))**
+
+- **[NFC] code formatting only
+ ([13846](https://github.com/civicrm/civicrm-core/pull/13846))**
+
+- **[NFC] Code reformatting
+ ([13849](https://github.com/civicrm/civicrm-core/pull/13849))**
+
+- **Add comments about usage for doPayment() function
+ ([13812](https://github.com/civicrm/civicrm-core/pull/13812))**
+
+- **[nfc] Attempt to improve false negatives on Logging test
+ ([13829](https://github.com/civicrm/civicrm-core/pull/13829))**
+
+- **[NFC] remove unnecessary variable
+ ([13836](https://github.com/civicrm/civicrm-core/pull/13836))**
+
+- **REF Extract case action links into a separate function to facilitate
+ refactoring ([13793](https://github.com/civicrm/civicrm-core/pull/13793))**
+
+- **NFC Whitespace cleanup MembershipBlock.tpl
+ ([13830](https://github.com/civicrm/civicrm-core/pull/13830))**
+
+- **Further cleanup on getRelatedMemberships - just get them with the api
+ ([13797](https://github.com/civicrm/civicrm-core/pull/13797))**
+
+- **[REF] small cleanups on payment.create flow.
+ ([13778](https://github.com/civicrm/civicrm-core/pull/13778))**
+
+- **[NFC] reformat Contact api file to switch to new array formatting
+ ([13806](https://github.com/civicrm/civicrm-core/pull/13806))**
+
+- **[REF] minor code simplification - remove over-handling of amount comp with
+ zero ([13783](https://github.com/civicrm/civicrm-core/pull/13783))**
+
+- **Attempted test fix
+ ([13791](https://github.com/civicrm/civicrm-core/pull/13791))**
+
+- **Remove reference to google checkout
+ ([13784](https://github.com/civicrm/civicrm-core/pull/13784))**
+
+- **[NFC] template whitespace cleanup
+ ([13782](https://github.com/civicrm/civicrm-core/pull/13782))**
+
+- **(NFC) Update for compliance with next phpcs standard
+ ([577](https://github.com/civicrm/civicrm-drupal/pull/577))**
+
+- **Lint .inc files in Drupal module folder to match newer coder standard
+ ([574](https://github.com/civicrm/civicrm-drupal/pull/574))**
+
+- **(NFC) Get phpcs to ignore the use of eval in this file as it is required
+ ([575](https://github.com/civicrm/civicrm-drupal/pull/575))**
+
+- **(NFC) Update for compliance with next phpcs standard
+ ([72](https://github.com/civicrm/civicrm-backdrop/pull/72))**
+
+- **(NFC) Lint .inc files and ensure that the eval usage in tests/phpunit…
+ ([71](https://github.com/civicrm/civicrm-backdrop/pull/71))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following code authors:
+
+AGH Strategies - Alice Frumin, Andrew Hunt, Eli Lisseck; Agileware - Alok Patel,
+Francis Whittle; Australian Greens - Seamus Lee; calibrate - Wouter Hechtermans;
+Christian Wach; CiviCRM - Coleman Watts, Tim Otten; CiviDesk - Yashodha Chaku;
+CompuCorp - Omar Abu Hussein; Coop SymbioTIC - Mathieu Lutfy; Dave D; Electronic
+Frontier Foundation - Mark Burdett; Freeform Solutions - Herb van den Dool;
+Fuzion - Jitendra Purohit; GreenPeace Central and Eastern Europe - Patrick
+Figel; JMA Consulting - Monish Deb; Megaphone Technology Consulting - Jon
+Goldberg; MJW Consulting - Matthew Wire; Pradeep Nayak; Skvare - Mark Hanna;
+Squiffle Consulting - Aidan Saunders; Stephen Palmstrom; Timbsoft Technologies -
+Tunbola Ogunwande; Wikimedia Foundation - Eileen McNaughton
+
+Most authors also reviewed code for this release; in addition, the following
+reviewers contributed their comments:
+
+Agileware - Justin Freeman; Aniessh Sethh; Artful Robot - Rich Lott; British
+Humanist Association - Andrew West; Circle Interactive - Dave Jenkins; Shitij
+Gugnani; Coop SymbioTIC - Samuel Vanhove; GValFr35; JMA Consulting - Joe Murray;
+Joinery - Allen Shaw; Aivars; Korlon - Stuart Gaston; Lighthouse Design and
+Consulting - Brian Shaughnessy; mcuradoc; Onyemenam Ndubuisi; Tadpole
+Collective - Kevin Cristiano
+
+## <a name="feedback"></a>Feedback
+
+These release notes are edited by Alice Frumin and Andrew Hunt. If you'd like
+to provide feedback on them, please log in to https://chat.civicrm.org/civicrm
+and contact `@agh1`.
--- /dev/null
+# CiviCRM 5.13.1
+
+Released May 2, 2019
+
+- **[Synopsis](#synopsis)**
+- **[Bugs resolved](#bugs)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?* | |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities? | no |
+| Change the database schema? | no |
+| Alter the API? | no |
+| Require attention to configuration options? | no |
+| Fix problems installing or upgrading to a previous version? | no |
+| Introduce features? | no |
+| **Fix bugs?** | **yes** |
+
+## <a name="bugs"></a>Bugs resolved
+
+- **Fix upgrade failure on multilingual sites ([dev/core#931](https://lab.civicrm.org/dev/core/issues/931): [14187](https://github.com/civicrm/civicrm-core/pull/14187))**
+
+- **Fix regression in REST endpoint on WordPress ([dev/wordpress#26](https://lab.civicrm.org/dev/wordpress/issues/26): [14186](https://github.com/civicrm/civicrm-core/pull/14186))**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following authors and reviewers:
+
+Australian Greens - Seamus Lee; Megaphone Technology Consulting - Jon Goldberg;
+Tadpole Collective - Kevin Cristiano;
+
+## <a name="feedback"></a>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`.
--- /dev/null
+# CiviCRM 5.13.2
+
+Released May 6, 2019
+
+- **[Synopsis](#synopsis)**
+- **[Bugs resolved](#bugs)**
+- **[Credits](#credits)**
+- **[Feedback](#feedback)**
+
+## <a name="synopsis"></a>Synopsis
+
+| *Does this version...?* | |
+|:--------------------------------------------------------------- |:-------:|
+| Fix security vulnerabilities? | no |
+| Change the database schema? | no |
+| Alter the API? | no |
+| Require attention to configuration options? | no |
+| Fix problems installing or upgrading to a previous version? | no |
+| Introduce features? | no |
+| **Fix bugs?** | **yes** |
+
+## <a name="bugs"></a>Bugs resolved
+
+- **Fix regression in sorting "Activities" tab by "source_name" ([dev/core#934](https://lab.civicrm.org/dev/core/issues/934):
+ [14194](https://github.com/civicrm/civicrm-core/pull/14194), [14204](https://github.com/civicrm/civicrm-core/pull/14204))**
+
+- **Fix regression in which inbound email attachments were saved as `.unknown`" ([dev/core#940](https://lab.civicrm.org/dev/core/issues/940):
+ [14207](https://github.com/civicrm/civicrm-core/pull/14207)**
+
+## <a name="credits"></a>Credits
+
+This release was developed by the following authors and reviewers:
+
+Wikimedia Foundation - Eileen McNaughton; CiviCRM - Tim Otten; Australian Greens - Seamus Lee;
+Dave D;
+
+## <a name="feedback"></a>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`.
LOCK TABLES `civicrm_domain` WRITE;
/*!40000 ALTER TABLE `civicrm_domain` DISABLE KEYS */;
-INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `config_backend`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,NULL,'5.14.alpha1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
+INSERT INTO `civicrm_domain` (`id`, `name`, `description`, `config_backend`, `version`, `contact_id`, `locales`, `locale_custom_strings`) VALUES (1,'Default Domain Name',NULL,NULL,'5.15.alpha1',1,NULL,'a:1:{s:5:\"en_US\";a:0:{}}');
/*!40000 ALTER TABLE `civicrm_domain` ENABLE KEYS */;
UNLOCK TABLES;
</tr>
<tr>
- <td>
- {include file="CRM/Core/DatePickerRange.tpl" fieldName="activity_date_time"}
- </td>
+ {include file="CRM/Core/DatePickerRangeWrapper.tpl" fieldName="activity_date_time" colspan="2"}
+ <td> </td>
</tr>
<tr>
<td>
<tr>
<td>{$form.start_date.label}<br/>
- {include file="CRM/common/jcalendar.tpl" elementName=start_date}
+ {$form.start_date.html}
</td>
<td>{$form.end_date.label}<br/>
- {include file="CRM/common/jcalendar.tpl" elementName=end_date}
+ {$form.end_date.html}
</td>
</tr>
--- /dev/null
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 5 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2018 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+*}
+{* Wrapper around DatePickerRange TPL file *}
+<td {if $colspan} colspan="{$colspan}" {else} colspan="2" {/if} {if $class} class="{$class}" {/if}>
+ {include file="CRM/Core/DatePickerRange.tpl" fieldName=$fieldName}
+</td>
{foreach from=$line_items item=line_item}
<tr class="event-line-item {$line_item.class}">
<td class="event-title">
- {$line_item.event->title} ({$line_item.event->start_date})
+ {$line_item.event->title} ({$line_item.event->start_date|crmDate})
</td>
<td class="participants-column">
{$line_item.num_participants}<br/>
});
}
else {
- if (elementId.is('select') === true && firstElement.parent().find(':input').select().index() >= 1 && firstElement.parent().find('select').select().index < 1) {
+ if (elementId.is('select') === true && firstElement.parent().find(':input').select().index() >= 1 && firstElement.parent().find('select').select().length > 1) {
// its a multiselect case
firstElement.parent().find(':input').select().each( function(count) {
var firstElementValue = $(this).val();
* Test getActivities BAO method for getting count
*/
public function testGetActivitiesCountforNonAdminDashboard() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$params = array(
'contact_id' => 9,
* Test getActivities BAO method for getting count
*/
public function testGetActivitiesCountforContactSummary() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$params = array(
'contact_id' => 9,
* CRM-18706 - Test Include/Exclude Activity Filters
*/
public function testActivityFilters() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
Civi::settings()->set('preserve_activity_tab_filter', 1);
$this->createLoggedInUser();
* Test getActivities BAO method for getting count
*/
public function testGetActivitiesCountforContactSummaryWithNoActivities() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$params = array(
'contact_id' => 17,
* Test getActivities BAO method.
*/
public function testGetActivitiesforNonAdminDashboard() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$contactID = 9;
$params = array(
}
+ /**
+ * Test getActivities BAO method.
+ */
+ public function testGetActivitiesforContactSummaryWithSortOptions() {
+ $this->createTestActivities();
+ $params = [
+ 'contact_id' => 9,
+ 'admin' => FALSE,
+ 'caseId' => NULL,
+ 'context' => 'activity',
+ 'activity_type_id' => NULL,
+ 'offset' => 0,
+ 'rowCount' => 0,
+ 'sort' => 'source_contact_name desc',
+ ];
+
+ $activities = CRM_Activity_BAO_Activity::getActivities($params);
+ $alphaOrder = ['Test Contact 11', 'Test Contact 12', 'Test Contact 3', 'Test Contact 4', 'Test Contact 9'];
+ foreach ($activities as $activity) {
+ $this->assertEquals(array_pop($alphaOrder), $activity['source_contact_name']);
+ }
+
+ }
+
/**
* Test getActivities BAO method.
*/
public function testGetActivitiesforContactSummary() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$contactID = 9;
$params = array(
'activity_type_id' => NULL,
'offset' => 0,
'rowCount' => 0,
- 'sort' => NULL,
);
//since we are loading activities from dataset, we know total number of activities for this contact
* Test getActivities BAO method.
*/
public function testGetActivitiesforContactSummaryWithActivities() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
// parameters for different test cases, check each array key for the specific test-case
$testCases = array(
*/
public function testByActivityDateAndStatus() {
CRM_Core_Config::singleton()->userPermissionClass->permissions = ['view all contacts', 'access CiviCRM'];
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
// activity IDs catagorised by date
$lastWeekActivities = array(1, 2, 3);
* Set up for testing activity queries.
*/
protected function setUpForActivityDashboardTests() {
- $op = new PHPUnit_Extensions_Database_Operation_Insert();
- $op->execute($this->_dbconn,
- $this->createFlatXMLDataSet(
- dirname(__FILE__) . '/activities_for_dashboard_count.xml'
- )
- );
+ $this->createTestActivities();
$this->_params = array(
'contact_id' => NULL,
public function testSendSMSWithoutPermission() {
$dummy = NULL;
$session = CRM_Core_Session::singleton();
- $config = &CRM_Core_Config::singleton();
- $config->userPermissionClass->permissions = array('access CiviCRM');
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = array('access CiviCRM');
CRM_Activity_BAO_Activity::sendSMS(
$dummy,
return array($sent, $activityId, $success);
}
+ protected function createTestActivities() {
+ $op = new PHPUnit_Extensions_Database_Operation_Insert();
+ $op->execute($this->_dbconn,
+ $this->createFlatXMLDataSet(
+ dirname(__FILE__) . '/activities_for_dashboard_count.xml'
+ )
+ );
+ }
+
}
CRM_Core_BAO_ConfigSetting::enableComponent('CiviCase');
}
+ /**
+ * Make sure that the latest case activity works accurately.
+ */
+ public function testCaseActivity() {
+ $userID = $this->createLoggedInUser();
+
+ $addTimeline = civicrm_api3('Case', 'addtimeline', [
+ 'case_id' => 1,
+ 'timeline' => "standard_timeline",
+ ]);
+
+ $query = CRM_Case_BAO_Case::getCaseActivityQuery('recent', $userID, ' civicrm_case.id IN( 1 )');
+ $res = CRM_Core_DAO::executeQuery($query);
+ $openCaseType = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Open Case');
+ while ($res->fetch()) {
+ $message = 'Failed asserting that the case activity query has a activity_type_id property:';
+ $this->assertObjectHasAttribute('activity_type_id', $res, $message . PHP_EOL . print_r($res, TRUE));
+ $message = 'Failed asserting that the latest activity from Case ID 1 was "Open Case":';
+ $this->assertEquals($openCaseType, $res->activity_type_id, $message . PHP_EOL . print_r($res, TRUE));
+ }
+ }
+
protected function tearDown() {
parent::tearDown();
$this->quickCleanup($this->tablesToTruncate, TRUE);
* @group headless
*/
class CRM_Contact_Import_Parser_ContactTest extends CiviUnitTestCase {
- protected $_tablesToTruncate = ['civicrm_address', 'civicrm_phone'];
+ protected $_tablesToTruncate = ['civicrm_address', 'civicrm_phone', 'civicrm_email'];
/**
* Setup function.
$contactValues['external_identifier'] = 'android';
$contactValues['street_address'] = 'Big Mansion';
$contactValues['phone'] = 12334;
- $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary'));
+ $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary'));
$address = $this->callAPISuccessGetSingle('Address', array('street_address' => 'Big Mansion'));
$this->assertEquals(1, $address['location_type_id']);
$this->assertEquals(1, $address['is_primary']);
- $this->markTestIncomplete('phone actually doesn\'t work');
$phone = $this->callAPISuccessGetSingle('Phone', array('phone' => '12334'));
$this->assertEquals(1, $phone['location_type_id']);
+ $this->callAPISuccessGetSingle('Email', array('email' => 'bill.gates@microsoft.com'));
+
$contact = $this->callAPISuccessGetSingle('Contact', $contactValues);
$this->callAPISuccess('Contact', 'delete', array('id' => $contact['id']));
}
$this->assertEquals(1, $address['values'][1]['is_primary']);
$this->assertEquals('Big Mansion', $address['values'][1]['street_address']);
- $this->markTestIncomplete('phone import primary actually IS broken');
$phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1));
$this->assertEquals(1, $phone['values'][0]['location_type_id']);
$this->assertEquals(1, $phone['values'][0]['is_primary']);
$this->assertEquals(0, $address[0]['is_primary']);
$this->assertEquals('Big Mansion', $address[0]['street_address']);
- $this->markTestIncomplete('phone import primary actually IS broken');
- $phone = $this->callAPISuccess('Phone', 'get', array('contact_id' => $contact['id'], 'sequential' => 1))['values'];
+ $phone = $this->callAPISuccess('Phone', 'get', ['contact_id' => $contact['id'], 'sequential' => 1, 'options' => ['sort' => 'is_primary DESC']])['values'];
$this->assertEquals(3, $phone[1]['location_type_id']);
$this->assertEquals(0, $phone[1]['is_primary']);
$this->assertEquals(12334, $phone[1]['phone']);
*/
public function testImportPrimaryAddressUpdate() {
list($contactValues) = $this->setUpBaseContact(array('external_identifier' => 'android'));
- $contactValues['nick_name'] = 'Old Bill';
+ $contactValues['email'] = 'melinda.gates@microsoft.com';
+ $contactValues['phone'] = '98765';
$contactValues['external_identifier'] = 'android';
$contactValues['street_address'] = 'Big Mansion';
$contactValues['city'] = 'Big City';
$contactID = $this->callAPISuccessGetValue('Contact', array('external_identifier' => 'android', 'return' => 'id'));
$originalAddress = $this->callAPISuccess('Address', 'create', array('location_type_id' => 2, 'street_address' => 'small house', 'contact_id' => $contactID));
- $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary'));
+ $originalPhone = $this->callAPISuccess('phone', 'create', array('location_type_id' => 2, 'phone' => '1234', 'contact_id' => $contactID));
+ $this->runImport($contactValues, CRM_Import_Parser::DUPLICATE_UPDATE, CRM_Import_Parser::VALID, array(0 => NULL, 1 => NULL, 2 => 'Primary', 3 => NULL, 4 => NULL, 5 => 'Primary', 6 => 'Primary', 7 => 'Primary'));
+ $phone = $this->callAPISuccessGetSingle('Phone', ['phone' => '98765']);
+ $this->assertEquals(2, $phone['location_type_id']);
+ $this->assertEquals($originalPhone['id'], $phone['id']);
+ $email = $this->callAPISuccess('Email', 'getsingle', ['contact_id' => $contactID]);
$address = $this->callAPISuccessGetSingle('Address', array('street_address' => 'Big Mansion'));
$this->assertEquals(2, $address['location_type_id']);
$this->assertEquals($originalAddress['id'], $address['id']);
// pay additional amount
$this->submitPayment(20);
$this->checkResults(array(30, 50, 20), 3);
+ $activities = $this->callAPISuccess('Activity', 'get', [
+ 'source_record_id' => $this->_contributionId,
+ 'activity_type_id' => 'Payment',
+ 'options' => ['sort' => 'id'],
+ 'sequential' => 1,
+ ])['values'];
+ $this->assertEquals(2, count($activities));
+ $this->assertEquals('$ 50.00 - Offline Payment for Contribution', $activities[0]['subject']);
+ $this->assertEquals('$ 20.00 - Offline Payment for Contribution', $activities[1]['subject']);
}
/**
*
* @dataProvider getSearchData
*/
- public function testContributionRecurStatusFilter($formValues, $expectedCount, $expectedContact, $expectedQill) {
+ public function testContributionRecurSearchFilters($formValues, $expectedCount, $expectedContact, $expectedQill) {
$this->setUpRecurringContributions();
$query = new CRM_Contact_BAO_Query(CRM_Contact_BAO_Query::convertFormValues($formValues));
'sequential' => 1,
'contact_id' => $this->ids['Contact']['contactID2'],
'frequency_interval' => 1,
- 'frequency_unit' => "month",
+ 'frequency_unit' => 'month',
'amount' => 22,
'currency' => "CAD",
'payment_instrument_id' => 1,
'contribution_status_id' => 1,
- 'financial_type_id' => "Donation",
+ 'financial_type_id' => 'Donation',
+ 'trxn_id' => 'a transaction',
+ 'processor_id' => 'a processor',
]);
$Contribution2 = $this->callAPISuccess('Contribution', 'create', [
'financial_type_id' => 'Donation',
'expected_contact' => [],
'expected_qill' => "Recurring Contribution Status = 'Cancelled'",
],
+ 'trxn_id_search' => [
+ 'form_value' => ['contribution_recur_trxn_id' => 'a transaction'],
+ 'expected_count' => 1,
+ 'expected_contact' => ['Mr. Terrence Smith II'],
+ 'expected_qill' => "Recurring Contribution Transaction ID = 'a transaction'",
+ ],
+ 'processor_id_search' => [
+ 'form_value' => ['contribution_recur_processor_id' => 'a processor'],
+ 'expected_count' => 1,
+ 'expected_contact' => ['Mr. Terrence Smith II'],
+ 'expected_qill' => "Recurring Contribution Processor ID = 'a processor'",
+ ],
];
return $useCases;
}
$this->assertDBCompareValues('CRM_Friend_DAO_Friend', $searchActParams, $compareActParams);
}
+ /**
+ * Testing Activity Generation through Entity Recursion with Custom Data and Tags.
+ */
+ public function testRecurringEntityGenerationWithCustomDataAndTags() {
+
+ // Create custom group and field
+ $customGroup = $this->customGroupCreate([
+ 'extends' => 'Activity',
+ ]);
+ $customField = $this->customFieldCreate([
+ 'custom_group_id' => $customGroup['id'],
+ 'default_value' => '',
+ ]
+ );
+
+ // Create activity Tag
+ $tag = $this->tagCreate([
+ 'used_for' => 'Activities',
+ ]);
+
+ // Create original activity
+ $customFieldValue = 'Custom Value';
+ $activityDateTime = date('YmdHis');
+ $activityId = $this->activityCreate([
+ 'activity_date_time' => $activityDateTime,
+ 'custom_' . $customField['id'] => $customFieldValue,
+ ]);
+
+ $activityId = $activityId['id'];
+
+ // Assign tag to a activity.
+ $this->callAPISuccess('EntityTag', 'create', [
+ 'entity_table' => 'civicrm_activity',
+ 'entity_id' => $activityId,
+ 'tag_id' => $tag['id'],
+ ]);
+
+ // Create recurring activities.
+ $recursion = new CRM_Core_BAO_RecurringEntity();
+ $recursion->entity_id = $activityId;
+ $recursion->entity_table = 'civicrm_activity';
+ $recursion->dateColumns = ['activity_date_time'];
+ $recursion->schedule = [
+ 'entity_value' => $activityId,
+ 'start_action_date' => $activityDateTime,
+ 'entity_status' => 'fourth saturday',
+ 'repetition_frequency_unit' => 'month',
+ 'repetition_frequency_interval' => 3,
+ 'start_action_offset' => 3,
+ 'used_for' => 'activity',
+ ];
+
+ $generatedEntities = $recursion->generate();
+ $generatedActivities = $generatedEntities['civicrm_activity'];
+
+ $this->assertEquals(3, count($generatedActivities), "Check if number of iterations are 3");
+
+ foreach ($generatedActivities as $generatedActivityId) {
+
+ /* Validate tag in recurring activity
+ // @todo - refer https://github.com/civicrm/civicrm-core/pull/13470
+ $this->callAPISuccess('EntityTag', 'getsingle', [
+ 'entity_table' => 'civicrm_activity',
+ 'entity_id' => $generatedActivityId,
+ ]);
+ */
+
+ // Validate custom data in recurring activity
+ $activity = $this->callAPISuccess('activity', 'getsingle', [
+ 'return' => [
+ 'custom_' . $customField['id'],
+ ],
+ 'id' => $generatedActivityId,
+ ]);
+
+ $this->assertEquals($customFieldValue, $activity['custom_' . $customField['id']], 'Custom field value should be ' . $customFieldValue);
+
+ }
+ }
+
}
$this->assertEquals(1, $count);
}
+ public function getExampleTokensForUseWithoutMailingJob() {
+ $cases = [];
+ $cases[] = ['text/plain', 'To opt out: {action.optOutUrl}!', '@To opt out: .*civicrm/mailing/optout.*&jid=&qid=@'];
+ $cases[] = ['text/html', 'To opt out: <a href="{action.optOutUrl}">click here</a>!', '@To opt out: <a href=".*civicrm/mailing/optout.*&jid=&qid=.*">click@'];
+ return $cases;
+ }
+
/**
- * Check the behavior in the erroneous situation where someone uses
- * a mailing-related token without providing a mailing ID.
+ * When previewing a mailing, there is no active mailing job, so one cannot
+ * generate fully formed URLs which reference the job. The current behavior
+ * is to link to a placeholder URL which has blank values for key fields
+ * like `jid` and `qid`.
+ *
+ * This current behavior may be wise or unwise - either way, having ensures
+ * that changes are intentional.
+ *
+ * @dataProvider getExampleTokensForUseWithoutMailingJob
*/
- public function testTokensWithoutMailing() {
- // We only need one case to see that the mailing-object works as
- // an alternative to the mailing-id.
- $inputTemplateFormat = 'text/plain';
- $inputTemplate = 'To optout: {action.optOutUrl}!';
-
+ public function testTokensWithoutMailingJob($inputTemplateFormat, $inputTemplateText, $expectRegex) {
$mailing = CRM_Core_DAO::createTestObject('CRM_Mailing_DAO_Mailing', array(
'name' => 'Example Name',
));
$p = new \Civi\Token\TokenProcessor(Civi::service('dispatcher'), array(
'mailing' => $mailing,
));
- $p->addMessage('example', $inputTemplate, $inputTemplateFormat);
+ $p->addMessage('example', $inputTemplateText, $inputTemplateFormat);
$p->addRow()->context(array(
'contactId' => $contact->id,
));
- try {
- $p->evaluate();
- $this->fail('TokenProcessor::evaluate() should have thrown an exception');
- }
- catch (CRM_Core_Exception $e) {
- $this->assertRegExp(';Cannot use action tokens unless context defines mailingJobId and mailingActionTarget;', $e->getMessage());
- }
+ // try {
+ // $p->evaluate();
+ // $this->fail('TokenProcessor::evaluate() should have thrown an exception');
+ // }
+ // catch (CRM_Core_Exception $e) {
+ // $this->assertRegExp(';Cannot use action tokens unless context defines mailingJobId and mailingActionTarget;', $e->getMessage());
+ // }
+
+ $p->evaluate();
+
+ // FIXME: For compatibility with
+ $actual = $p->getRow(0)->render('example');
+ $this->assertRegExp($expectRegex, $actual);
}
}
parent::setUp();
$this->useTransaction(TRUE);
$this->createLoggedInUser();
- $this->_mailingID_A = $this->createMailing();
- $this->_mailingID_B = $this->createMailing();
- $this->_mailingID_C = $this->createMailing();
+ $this->_mailingID_A = $this->createMailing([
+ 'subject' => 'subject a ' . time(),
+ 'body_text' => 'body_text a ' . time(),
+ ]);
+ $this->_mailingID_B = $this->createMailing([
+ 'subject' => 'subject b ' . time(),
+ 'body_text' => 'body_text b ' . time(),
+ ]);
+ $this->_mailingID_C = $this->createMailing([
+ 'subject' => 'not yet ' . time(),
+ 'body_text' => 'not yet ' . time(),
+ ]);
$this->_groupID = $this->groupCreate();
$this->_params = array(
$this->assertJobCounts(1, 1, 1);
}
+ /**
+ * Create a test. Declare the second mailing a winner. Ensure that key
+ * fields propagate to the final mailing.
+ */
+ public function testSubmitWinnderId() {
+ $checkSyncFields = ['subject', 'body_text'];
+
+ $result = $this->groupContactCreate($this->_groupID, 20, TRUE);
+ $this->assertEquals(20, $result['added'], "in line " . __LINE__);
+
+ $params = $this->_params;
+ $params['group_percentage'] = 10;
+ $result = $this->callAPISuccess($this->_entity, 'create', $params);
+
+ $this->callAPISuccess('Mailing', 'create', [
+ 'id' => $this->_mailingID_A,
+ 'groups' => ['include' => [$this->_groupID]],
+ ]);
+ $this->assertJobCounts(0, 0, 0);
+
+ $this->callAPISuccess('MailingAB', 'submit', [
+ 'id' => $result['id'],
+ 'status' => 'Testing',
+ 'scheduled_date' => 'now',
+ 'approval_date' => 'now',
+ ]);
+ $this->assertJobCounts(1, 1, 0);
+
+ $b = $this->getApiValues('Mailing', ['id' => $this->_mailingID_B], $checkSyncFields);
+ $c = $this->getApiValues('Mailing', ['id' => $this->_mailingID_C], $checkSyncFields);
+ $this->assertNotEquals($b, $c);
+
+ $this->callAPISuccess('MailingAB', 'submit', [
+ 'id' => $result['id'],
+ 'status' => 'Final',
+ 'winner_id' => $this->_mailingID_B,
+ 'scheduled_date' => 'now',
+ 'approval_date' => 'now',
+ ]);
+ $this->assertJobCounts(1, 1, 1);
+
+ $b = $this->getApiValues('Mailing', ['id' => $this->_mailingID_B], $checkSyncFields);
+ $c = $this->getApiValues('Mailing', ['id' => $this->_mailingID_C], $checkSyncFields);
+ $this->assertEquals($b, $c);
+ }
+
+ /**
+ * Lookup a record via API. Return *only* the expected values.
+ *
+ * @param $entity
+ * @param $filter
+ * @param $return
+ * @return array
+ */
+ protected function getApiValues($entity, $filter, $return) {
+ $rec = $this->callAPISuccess($entity, 'getsingle', $filter + ['return' => $return]);
+ return CRM_Utils_Array::subset($rec, $return);
+ }
+
/**
* @param $expectedCountA
* @param $expectedCountB
$this->_params = array(
'subject' => 'Hello {contact.display_name}',
'body_text' => "This is {contact.display_name}.\nhttps://civicrm.org\n{domain.address}{action.optOutUrl}",
- 'body_html' => "<link href='https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Zilla+Slab:500,700' rel='stylesheet' type='text/css'><p>This is {contact.display_name}.</p><p><a href='https://civicrm.org/'>CiviCRM.org</a></p><p>{domain.address}{action.optOutUrl}</p>",
+ 'body_html' => "<link href='https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Zilla+Slab:500,700' rel='stylesheet' type='text/css'><p><a href=\"http://{action.forward}\">Forward this email</a><a href=\"{action.forward}\">Forward this email with no protocol</a></p<p>This is {contact.display_name}.</p><p><a href='https://civicrm.org/'>CiviCRM.org</a></p><p>{domain.address}{action.optOutUrl}</p>",
'name' => 'mailing name',
'created_id' => $this->_contactID,
'header_id' => '',
$this->assertDBQuery($maxIDs['group'], 'SELECT MAX(id) FROM civicrm_mailing_group');
// 'Preview should not create any mailing_recipient records'
$this->assertDBQuery($maxIDs['recipient'], 'SELECT MAX(id) FROM civicrm_mailing_recipients');
-
+ $baseurl = CRM_Utils_System::baseCMSURL();
$previewResult = $result['values'][$result['id']]['api.Mailing.preview'];
$this->assertEquals("Hello $displayName", $previewResult['values']['subject']);
$this->assertContains("This is $displayName", $previewResult['values']['body_text']);
$this->assertContains("<p>This is $displayName.</p>", $previewResult['values']['body_html']);
+ $this->assertContains('<a href="' . $baseurl . 'index.php?q=civicrm/mailing/forward&amp;reset=1&jid=&qid=&h=">Forward this email with no protocol</a>', $previewResult['values']['body_html']);
+ $this->assertNotContains("http://http://", $previewResult['values']['body_html']);
+ }
+
+ public function testMailerPreviewUnknownContact() {
+ $params = $this->_params;
+ $params['api.Mailing.preview'] = array(
+ 'id' => '$value.id',
+ );
+
+ $result = $this->callAPISuccess('mailing', 'create', $params);
+
+ // NOTE: It's highly debatable what's best to do with contact-tokens for an
+ // unknown-contact. However, changes should be purposeful, so we'll test
+ // for the current behavior (i.e. returning blanks).
+ $previewResult = $result['values'][$result['id']]['api.Mailing.preview'];
+ $this->assertEquals("Hello ", $previewResult['values']['subject']);
+ $this->assertContains("This is .", $previewResult['values']['body_text']);
+ $this->assertContains("<p>This is .</p>", $previewResult['values']['body_html']);
}
public function testMailerPreviewRecipients() {
'from_financial_account_id' => 7,
'to_financial_account_id' => 6,
'total_amount' => -30,
- 'status_id' => 1,
+ 'status_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_FinancialTrxn', 'status_id', 'Refunded'),
'is_payment' => 1,
];
foreach ($expected as $key => $value) {
*/
public function checkPaymentResult($payment, $expectedResult) {
foreach ($expectedResult[$payment['id']] as $key => $value) {
- $this->assertEquals($payment['values'][$payment['id']][$key], $value);
+ $this->assertEquals($payment['values'][$payment['id']][$key], $value, 'mismatch on ' . $key);
}
}
*/
-
-require_once ('bin/cli.php');
+require_once('bin/cli.php');
require_once 'CRM/Core/BAO/Tag.php';
/**
die("you need to profide a csv file (1st column parent name, 2nd tag name");
}
$this->file = $this->args[0];
- $this->tags = array_flip(CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', array('onlyActive' => FALSE)));
+ $this->tags = array_flip(CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]));
}
//format expected: parent name, tag
echo "\n- exists already: " . $param['name'];
return;
}
- $key = array('tag' => '');
+ $key = ['tag' => ''];
if ($param['parent']) {
if (array_key_exists($param['parent'], $this->tags)) {
$param['parent_id'] = $this->tags[$param['parent']];
}
- else $param['parent_id'] = $this->addTag(array(parent => '', name => $param['parent']));
+ else {
+ $param['parent_id'] = $this->addTag([
+ parent => '',
+ name => $param['parent'],
+ ]);
+ }
$tag = CRM_Core_BAO_Tag::add($param, $key);
echo "\n" . $tag->id . ": create " . $param['name'] . " below " . $param['parent'];
}
require_once 'Console/Getopt.php';
$shortOptions = "n:p:k:pre";
- $longOptions = array('name=', 'pass=', 'key=', 'prefix=');
+ $longOptions = ['name=', 'pass=', 'key=', 'prefix='];
$getopt = new Console_Getopt();
$args = $getopt->readPHPArgv();
array_shift($args);
list($valid, $dontCare) = $getopt->getopt2($args, $shortOptions, $longOptions);
- $vars = array(
+ $vars = [
'name' => 'n',
'pass' => 'p',
'key' => 'k',
'prefix' => 'pre',
- );
+ ];
foreach ($vars as $var => $short) {
$$var = NULL;
$dao = &CRM_Core_DAO::executeQuery($query);
$updateQuery = "UPDATE civicrm_phone SET phone = %1 where id = %2";
- $params = array(1 => array('', 'String'),
- 2 => array(0, 'Integer'),
- );
+ $params = [
+ 1 => ['', 'String'],
+ 2 => [0, 'Integer'],
+ ];
$totalPhone = $validPhone = $nonPrefixedPhone = 0;
while ($dao->fetch()) {
$newPhone = processPhone($dao->phone, $prefix);
* Scrape all config options from the CKEditor documentation site.
*/
$content = file_get_contents('http://docs.ckeditor.com/?print=/api/CKEDITOR.config');
-$matches = $output = array();
+$matches = $output = [];
preg_match_all("#name expandable'>([^<]+)</a>\s?:\s?(.*)<span.*'short'>([\s\S]*?)</div>#", $content, $matches);
foreach ($matches[1] as $i => $name) {
- $output[] = array(
+ $output[] = [
'id' => $name,
'type' => strip_tags($matches[2][$i]),
- 'description' => str_replace(array("\n", '. ...'), array(' ', '.'), $matches[3][$i]),
- );
+ 'description' => str_replace(["\n", '. ...'], [' ', '.'], $matches[3][$i]),
+ ];
}
if ($output) {
$location = str_replace('tools/bin/scripts', '', __DIR__);
/**
* A PHP shell script
-
- On drupal if you have a symlink to your civi module, don't forget to create a new file - settings_location.php
- Enter the following code (substitute the actual location of your <drupal root>/sites directory)
- <?php
- define( 'CIVICRM_CONFDIR', '/var/www/drupal.6/sites' );
- ?>
-
+ *
+ * On drupal if you have a symlink to your civi module, don't forget to create a new file - settings_location.php
+ * Enter the following code (substitute the actual location of your <drupal root>/sites directory)
+ * <?php
+ * define( 'CIVICRM_CONFDIR', '/var/www/drupal.6/sites' );
+ * ?>
*/
$include_path = "../packages/:" . get_include_path();
set_include_path($include_path);
// set_include_path( $include_path );
require_once 'Console/Getopt.php';
$shortOptions = "s:u:p:k:";
- $longOptions = array('site=', 'user', 'pass');
+ $longOptions = ['site=', 'user', 'pass'];
$getopt = new Console_Getopt();
$args = $getopt->readPHPArgv();
array_shift($args);
list($valid, $this->args) = $getopt->getopt2($args, $shortOptions, $longOptions);
- $vars = array(
+ $vars = [
'user' => 'u',
'pass' => 'p',
'key' => 'k',
'site' => 's',
- );
+ ];
foreach ($vars as $var => $short) {
$$var = NULL;
function authenticate($user, $pass) {
session_start();
require_once 'CRM/Core/Config.php';
-
- $config = &CRM_Core_Config::singleton();
+ // Does calling this do anything here?
+ CRM_Core_Config::singleton();
// this does not return on failure
// require_once 'CRM/Utils/System.php';
$_SERVER['PHP_SELF'] = "/index.php";
$_SERVER['HTTP_HOST'] = $this->site;
$_REQUEST['key'] = $this->key;
- require_once ("./civicrm.config.php");
+ require_once("./civicrm.config.php");
}
}
define('CIVICRM_USE_MEMCACHE', 1);
-$config = &CRM_Core_Config::singleton();
-$cache = &CRM_Utils_Cache::singleton();
+$config = CRM_Core_Config::singleton();
+$cache = CRM_Utils_Cache::singleton();
-$cache->set('CRM_Core_Config' .CRM_Core_Config::domainID(), $config);
+$cache->set('CRM_Core_Config' . CRM_Core_Config::domainID(), $config);
CRM_Core_Error::debug('get', $cache->get('CRM_Core_Config' . CRM_Core_Config::domainID()));
echo "Changing version from $oldVersion to $newVersion...\n";
$verName = makeVerName($newVersion);
-$phpFile = initFile("CRM/Upgrade/Incremental/php/{$verName}.php", function() use ($verName) {
+$phpFile = initFile("CRM/Upgrade/Incremental/php/{$verName}.php", function () use ($verName) {
ob_start();
global $camelNumber;
$camelNumber = $verName;
return ob_get_clean();
});
-$sqlFile = initFile("CRM/Upgrade/Incremental/sql/{$newVersion}.mysql.tpl", function() use ($newVersion) {
+$sqlFile = initFile("CRM/Upgrade/Incremental/sql/{$newVersion}.mysql.tpl", function () use ($newVersion) {
return "{* file to handle db changes in $newVersion during upgrade *}\n";
});
<?php
-require_once '../civicrm.config.php'; require_once 'CRM/Core/Config.php';
+require_once '../civicrm.config.php';
+require_once 'CRM/Core/Config.php';
require_once 'CRM/Core/Error.php';
$config = CRM_Core_Config::singleton();
exit();
$xmlProcessor->run(104, 1, 'Substance Abuse', '15 Day Review');
-$params = array('clientID' => 104,
+$params = [
+ 'clientID' => 104,
'creatorID' => 108,
'standardTimeline' => 1,
'activityTypeName' => 'Open Case',
'dueDateTime' => time(),
'caseID' => 1,
-);
+];
require_once 'CRM/Case/XMLProcessor/Process.php';
$xmlProcessor = new CRM_Case_XMLProcessor_Process();
*/
-
/*
* This script recaches the display_name and sort_name values
*
function __construct() {
// you can run this program either from an apache command, or from the cli
if (php_sapi_name() == "cli") {
- require_once ("cli.php");
+ require_once("cli.php");
$cli = new civicrm_cli();
//if it doesn't die, it's authenticated
}
$prefixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'prefix_id');
$suffixes = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'suffix_id');
- $tokens = array();
+ $tokens = [];
CRM_Utils_Hook::tokens($tokens);
- $tokenFields = array();
+ $tokenFields = [];
foreach ($tokens as $category => $catTokens) {
foreach ($catTokens as $token) {
$tokenFields[] = $token;
while ($dao->fetch()) {
$contactID = $dao->id;
- $params = array('first_name' => $dao->first_name,
+ $params = [
+ 'first_name' => $dao->first_name,
'middle_name' => $dao->middle_name,
'last_name' => $dao->last_name,
'prefix_id' => $dao->prefix_id,
'suffix_id' => $dao->suffix_id,
- );
+ ];
$params['individual_prefix'] = $prefixes[$dao->prefix_id];
$params['individual_suffix'] = $suffixes[$dao->suffix_id];
* Implementation of hook_enable
*/
function civicrm_webtest_enable() {
- user_role_grant_permissions(1, array(
+ user_role_grant_permissions(1, [
'access CiviMail subscribe/unsubscribe pages',
'access all custom data',
'access uploaded files',
'profile create',
'profile view',
'register for events',
- ));
+ ]);
$roles = user_roles();
if (!in_array('civicrm_webtest_user', $roles)) {
$role->name = 'civicrm_webtest_user';
user_role_save($role);
$rid = $role->rid;
- } else {
+ }
+ else {
$rid = array_search('civicrm_webtest_user', $roles);
}
- user_role_grant_permissions($rid, array(
+ user_role_grant_permissions($rid, [
// FIXME: whoa, why do we bother with users if both need admin rights?
- 'access AJAX API',
+ 'access AJAX API',
// 'access all cases and activities',
- 'access all custom data',
- 'access CiviContribute',
- 'access CiviCRM',
- 'access CiviEvent',
- // 'access CiviGrant',
- 'access CiviMail',
- 'access CiviMail subscribe/unsubscribe pages',
- 'access CiviMember',
- 'access CiviPledge',
- 'access CiviReport',
- 'access Contact Dashboard',
- 'access contact reference fields',
- 'access deleted contacts',
- // 'access my cases and activities',
- 'access Report Criteria',
- 'save Report Criteria',
- 'access uploaded files',
- // 'add cases',
- 'add contacts',
- // 'administer CiviCase',
- 'administer CiviCRM',
- 'administer dedupe rules',
- 'administer Reports',
- 'administer reserved groups',
- 'administer reserved reports',
- 'administer reserved tags',
- 'administer Tagsets',
- 'delete activities',
- 'delete contacts',
- // 'delete in CiviCase',
- 'delete in CiviContribute',
- 'delete in CiviEvent',
- // 'delete in CiviGrant',
- 'delete in CiviMail',
- 'delete in CiviMember',
- 'delete in CiviPledge',
- 'edit all contacts',
- 'view my contact',
- 'edit my contact',
- 'edit all events',
- 'edit contributions',
- 'edit event participants',
- 'edit message templates',
- // 'edit grants',
- 'edit groups',
- 'edit memberships',
- 'edit pledges',
- 'import contacts',
- 'make online contributions',
- 'manage tags',
- 'merge duplicate contacts',
- 'profile create',
- 'profile edit',
- 'profile listings',
- 'profile listings and forms',
- 'profile view',
- 'register for events',
- 'translate CiviCRM',
- 'view all activities',
- 'view all contacts',
- 'view all notes',
- 'view event info',
- 'view event participants',
- 'view public CiviMail content',
- 'administer payment processors',
- 'create manual batch',
- 'edit own manual batches',
- 'edit all manual batches',
- 'view own manual batches',
- 'view all manual batches',
- 'delete own manual batches',
- 'delete all manual batches',
- 'export own manual batches',
- 'export all manual batches',
- ));
+ 'access all custom data',
+ 'access CiviContribute',
+ 'access CiviCRM',
+ 'access CiviEvent',
+ // 'access CiviGrant',
+ 'access CiviMail',
+ 'access CiviMail subscribe/unsubscribe pages',
+ 'access CiviMember',
+ 'access CiviPledge',
+ 'access CiviReport',
+ 'access Contact Dashboard',
+ 'access contact reference fields',
+ 'access deleted contacts',
+ // 'access my cases and activities',
+ 'access Report Criteria',
+ 'save Report Criteria',
+ 'access uploaded files',
+ // 'add cases',
+ 'add contacts',
+ // 'administer CiviCase',
+ 'administer CiviCRM',
+ 'administer dedupe rules',
+ 'administer Reports',
+ 'administer reserved groups',
+ 'administer reserved reports',
+ 'administer reserved tags',
+ 'administer Tagsets',
+ 'delete activities',
+ 'delete contacts',
+ // 'delete in CiviCase',
+ 'delete in CiviContribute',
+ 'delete in CiviEvent',
+ // 'delete in CiviGrant',
+ 'delete in CiviMail',
+ 'delete in CiviMember',
+ 'delete in CiviPledge',
+ 'edit all contacts',
+ 'view my contact',
+ 'edit my contact',
+ 'edit all events',
+ 'edit contributions',
+ 'edit event participants',
+ 'edit message templates',
+ // 'edit grants',
+ 'edit groups',
+ 'edit memberships',
+ 'edit pledges',
+ 'import contacts',
+ 'make online contributions',
+ 'manage tags',
+ 'merge duplicate contacts',
+ 'profile create',
+ 'profile edit',
+ 'profile listings',
+ 'profile listings and forms',
+ 'profile view',
+ 'register for events',
+ 'translate CiviCRM',
+ 'view all activities',
+ 'view all contacts',
+ 'view all notes',
+ 'view event info',
+ 'view event participants',
+ 'view public CiviMail content',
+ 'administer payment processors',
+ 'create manual batch',
+ 'edit own manual batches',
+ 'edit all manual batches',
+ 'view own manual batches',
+ 'view all manual batches',
+ 'delete own manual batches',
+ 'delete all manual batches',
+ 'export own manual batches',
+ 'export all manual batches',
+ ]);
}
*/
function _angularex_civix_civicrm_config(&$config = NULL) {
static $configured = FALSE;
- if ($configured) return;
+ if ($configured) {
+ return;
+ }
$configured = TRUE;
$template =& CRM_Core_Smarty::singleton();
- $extRoot = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
+ $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$extDir = $extRoot . 'templates';
- if ( is_array( $template->template_dir ) ) {
- array_unshift( $template->template_dir, $extDir );
- } else {
- $template->template_dir = array( $extDir, $template->template_dir );
+ if (is_array($template->template_dir)) {
+ array_unshift($template->template_dir, $extDir);
+ }
+ else {
+ $template->template_dir = [$extDir, $template->template_dir];
}
- $include_path = $extRoot . PATH_SEPARATOR . get_include_path( );
- set_include_path( $include_path );
+ $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
+ set_include_path($include_path);
}
/**
function _angularex_civix_civicrm_enable() {
_angularex_civix_civicrm_config();
if ($upgrader = _angularex_civix_upgrader()) {
- if (is_callable(array($upgrader, 'onEnable'))) {
+ if (is_callable([$upgrader, 'onEnable'])) {
return $upgrader->onEnable();
}
}
function _angularex_civix_civicrm_disable() {
_angularex_civix_civicrm_config();
if ($upgrader = _angularex_civix_upgrader()) {
- if (is_callable(array($upgrader, 'onDisable'))) {
+ if (is_callable([$upgrader, 'onDisable'])) {
return $upgrader->onDisable();
}
}
* @return CRM_Angularex_Upgrader
*/
function _angularex_civix_upgrader() {
- if (!file_exists(__DIR__.'/CRM/Angularex/Upgrader.php')) {
+ if (!file_exists(__DIR__ . '/CRM/Angularex/Upgrader.php')) {
return NULL;
- } else {
+ }
+ else {
return CRM_Angularex_Upgrader_Base::instance();
}
}
* @return array(string)
*/
function _angularex_civix_find_files($dir, $pattern) {
- if (is_callable(array('CRM_Utils_File', 'findFiles'))) {
+ if (is_callable(['CRM_Utils_File', 'findFiles'])) {
return CRM_Utils_File::findFiles($dir, $pattern);
}
- $todos = array($dir);
- $result = array();
+ $todos = [$dir];
+ $result = [];
while (!empty($todos)) {
$subdir = array_shift($todos);
foreach (_angularex_civix_glob("$subdir/$pattern") as $match) {
while (FALSE !== ($entry = readdir($dh))) {
$path = $subdir . DIRECTORY_SEPARATOR . $entry;
if ($entry{0} == '.') {
- } elseif (is_dir($path)) {
+ }
+ elseif (is_dir($path)) {
$todos[] = $path;
}
}
CRM_Core_Error::fatal($errorMessage);
// throw new CRM_Core_Exception($errorMessage);
}
- $caseTypes[$name] = array(
+ $caseTypes[$name] = [
'module' => 'org.civicrm.angularex',
'name' => $name,
'file' => $file,
- );
+ ];
}
}
*/
function _angularex_civix_glob($pattern) {
$result = glob($pattern);
- return is_array($result) ? $result : array();
+ return is_array($result) ? $result : [];
}
/**
// If we are done going down the path, insert menu
if (empty($path)) {
- if (!$navId) $navId = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_navigation");
- $navId ++;
- $menu[$navId] = array (
- 'attributes' => array_merge($item, array(
- 'label' => CRM_Utils_Array::value('name', $item),
- 'active' => 1,
- 'parentID' => $parentId,
- 'navID' => $navId,
- ))
- );
- return true;
- } else {
+ if (!$navId) {
+ $navId = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_navigation");
+ }
+ $navId++;
+ $menu[$navId] = [
+ 'attributes' => array_merge($item, [
+ 'label' => CRM_Utils_Array::value('name', $item),
+ 'active' => 1,
+ 'parentID' => $parentId,
+ 'navID' => $navId,
+ ]),
+ ];
+ return TRUE;
+ }
+ else {
// Find an recurse into the next level down
- $found = false;
+ $found = FALSE;
$path = explode('/', $path);
$first = array_shift($path);
foreach ($menu as $key => &$entry) {
if ($entry['attributes']['name'] == $first) {
- if (!$entry['child']) $entry['child'] = array();
+ if (!$entry['child']) {
+ $entry['child'] = [];
+ }
$found = _angularex_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item, $key);
}
}
*/
function _angularex_civix_civicrm_alterSettingsFolders(&$metaDataFolders = NULL) {
static $configured = FALSE;
- if ($configured) return;
+ if ($configured) {
+ return;
+ }
$configured = TRUE;
$settingsDir = __DIR__ . DIRECTORY_SEPARATOR . 'settings';
- if(is_dir($settingsDir) && !in_array($settingsDir, $metaDataFolders)) {
+ if (is_dir($settingsDir) && !in_array($settingsDir, $metaDataFolders)) {
$metaDataFolders[] = $settingsDir;
}
}
* @param $angularModule
*/
function angularex_civicrm_angularModules(&$angularModule) {
- $angularModule['example'] = array(
+ $angularModule['example'] = [
'ext' => 'org.civicrm.angularex',
- 'js' => array('js/*.js'),
- 'partials' => array('partials'),
- );
+ 'js' => ['js/*.js'],
+ 'partials' => ['partials'],
+ ];
}
const QUEUE_NAME = 'demo-queue';
function run() {
- $queue = CRM_Queue_Service::singleton()->create(array(
+ $queue = CRM_Queue_Service::singleton()->create([
'type' => 'Sql',
'name' => self::QUEUE_NAME,
'reset' => TRUE,
- ));
+ ]);
for ($i = 0; $i < 5; $i++) {
$queue->createItem(new CRM_Queue_Task(
- array('CRM_Demoqueue_Page_DemoQueue', 'doMyWork'), // callback
- array($i, "Task $i takes $i second(s)"), // arguments
+ ['CRM_Demoqueue_Page_DemoQueue', 'doMyWork'], // callback
+ [$i, "Task $i takes $i second(s)"], // arguments
"Task $i" // title
));
if ($i == 2) {
$queue->createItem(new CRM_Queue_Task(
- array('CRM_Demoqueue_Page_DemoQueue', 'addMoreWork'), // callback
- array(), // arguments
+ ['CRM_Demoqueue_Page_DemoQueue', 'addMoreWork'], // callback
+ [], // arguments
"Add More Work" // title
));
}
}
- $runner = new CRM_Queue_Runner(array(
+ $runner = new CRM_Queue_Runner([
'title' => ts('Demo Queue Runner'),
'queue' => $queue,
- 'onEnd' => array('CRM_Demoqueue_Page_DemoQueue', 'onEnd'),
+ 'onEnd' => ['CRM_Demoqueue_Page_DemoQueue', 'onEnd'],
'onEndUrl' => CRM_Utils_System::url('civicrm/demo-queue/done'),
- ));
+ ]);
$runner->runAllViaWeb(); // does not return
}
sleep(1);
for ($i = 0; $i < 5; $i++) {
$ctx->queue->createItem(new CRM_Queue_Task(
- array('CRM_Demoqueue_Page_DemoQueue', 'doMyWork'), // callback
- array($i, "Extra task $i takes $i second(s)"), // arguments
+ ['CRM_Demoqueue_Page_DemoQueue', 'doMyWork'], // callback
+ [$i, "Extra task $i takes $i second(s)"], // arguments
"Extra Task $i" // title
- ), array(
+ ), [
'weight' => -1,
- ));
+ ]);
}
return TRUE; // success
}
* Class CRM_Demoqueue_Page_DemoQueueDone
*/
class CRM_Demoqueue_Page_DemoQueueDone extends CRM_Core_Page {
- function run() {
- // Example: Set the page-title dynamically; alternatively, declare a static title in xml/Menu/*.xml
- CRM_Utils_System::setTitle(ts('DemoQueueDone'));
- parent::run();
- }
+ function run() {
+ // Example: Set the page-title dynamically; alternatively, declare a static title in xml/Menu/*.xml
+ CRM_Utils_System::setTitle(ts('DemoQueueDone'));
+ parent::run();
+ }
}
function _demoqueue_civix_civicrm_config(&$config) {
$template =& CRM_Core_Smarty::singleton();
- $extRoot = dirname( __FILE__ ) . DIRECTORY_SEPARATOR;
+ $extRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$extDir = $extRoot . 'templates';
- if ( is_array( $template->template_dir ) ) {
- array_unshift( $template->template_dir, $extDir );
- } else {
- $template->template_dir = array( $extDir, $template->template_dir );
+ if (is_array($template->template_dir)) {
+ array_unshift($template->template_dir, $extDir);
+ }
+ else {
+ $template->template_dir = [$extDir, $template->template_dir];
}
- $include_path = $extRoot . PATH_SEPARATOR . get_include_path( );
- set_include_path( $include_path );
+ $include_path = $extRoot . PATH_SEPARATOR . get_include_path();
+ set_include_path($include_path);
}
/**
global $civicrm_root;
$realArgs = $argv;
$diffCmd = FALSE;
-$files = array();
+$files = [];
array_shift($realArgs);
foreach ($realArgs as $arg) {
echo "File \"$file\" appears to have consistency issues. Created $newFile.\n";
file_put_contents($newFile, $newMarkup);
if ($diffCmd) {
- passthru($diffCmd . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($newFile));
+ passthru($diffCmd . ' ' . escapeshellarg($file) . ' ' . escapeshellarg($newFile));
}
}
-}
\ No newline at end of file
+}
<?php
-$options = getopt('bc:ht:'); if (isset($options['h'])) {
+$options = getopt('bc:ht:');
+if (isset($options['h'])) {
print ("\nUsage: php civimail-spooler.php [-bh] [-c <config>] [-t <period>]\n");
print (" -b Run this process continuously\n");
print (" -c Path to CiviCRM civicrm.settings.php\n");
require_once "CRM/Core/Config.php";
');
-$config = &CRM_Core_Config::singleton();
+$config = CRM_Core_Config::singleton();
/* Temporary permissioning hack for now */
* process into the background and provide init.d scripts */
-
CRM_Mailing_BAO_MailingJob::runJobs();
sleep($config->mailerPeriod);
}
/**
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @version //autogentag//
+diff --git a/src/parser/interfaces/part_parser.php b/src/parser/interfaces/part_parser.php
+index a81378b..6c59e5a 100644
+--- a/src/parser/interfaces/part_parser.php
++++ b/src/parser/interfaces/part_parser.php
+@@ -168,7 +168,11 @@ abstract class ezcMailPartParser
+ break;
+
+ case 'text':
+- if ( ezcMailPartParser::$parseTextAttachmentsAsFiles === true )
++ // dev/core#940 Ensure that emails are not processed as .unknown attachments by checking
++ // for Filename or name in the content-disposition and content-type headers.
++ if ( (ezcMailPartParser::$parseTextAttachmentsAsFiles === true) &&
++ (preg_match('/\s*filename="?([^;"]*);?/i', $headers['Content-Disposition']) ||
++ preg_match( '/\s*name="?([^;"]*);?/i' , $headers['Content-Type']) ) )
+ {
+ $bodyParser = new ezcMailFileParser( $mainType, $subType, $headers );
+ }
function &splitContactIDs(&$contactIDs) {
// contactIDs could be a real large array, so we split it up into
// smaller chunks and then general xml for each chunk
- $chunks = array();
- $current = 0;
- $chunks[$current] = array();
- $count = 0;
+ $chunks = [];
+ $current = 0;
+ $chunks[$current] = [];
+ $count = 0;
foreach ($contactIDs as $cid) {
$chunks[$current][] = $cid;
if ($count == CHUNK_SIZE) {
$current++;
- $chunks[$current] = array();
+ $chunks[$current] = [];
$count = 0;
}
}
foreach ($tokens as $n => $v) {
if (is_array($v)) {
- $str = array();
+ $str = [];
foreach ($v as $el) {
$el = escapeJsonString($el);
$str[] = "\"$el\"";
* @return mixed
*/
function escapeJsonString($value) {
- $escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
- $replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
+ $escapers = ["\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c"];
+ $replacements = ["\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b"];
return str_replace($escapers, $replacements, $value);
}
* @return array
*/
function getValues(&$contactIDs, &$values) {
- $values = array();
+ $values = [];
foreach ($contactIDs as $cid) {
- $values[$cid] = array();
+ $values[$cid] = [];
}
getContactInfo($contactIDs, $values);
* @param $values
*/
function getContactInfo(&$contactIDs, &$values) {
- $fields = array('sort_name' => NULL,
+ $fields = [
+ 'sort_name' => NULL,
'display_name' => NULL,
'contact_type' => NULL,
'legal_identifier' => NULL,
'organization_name' => NULL,
'legal_name' => NULL,
'job_title' => NULL,
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_contact', $fields, 'id');
}
WHERE c.id IN ( $ids )
";
- $fields = array('location_type', 'street_address', 'supplemental_address_1',
- 'supplemental_address_2', 'supplemental_address_3', 'city', 'postal_code',
- 'state', 'country',
- );
+ $fields = [
+ 'location_type',
+ 'street_address',
+ 'supplemental_address_1',
+ 'supplemental_address_2',
+ 'supplemental_address_3',
+ 'city',
+ 'postal_code',
+ 'state',
+ 'country',
+ ];
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
$address = '';
else {
if (!is_array($values[$contactID][$name])) {
$save = $values[$contactID][$name];
- $values[$contactID][$name] = array();
+ $values[$contactID][$name] = [];
$values[$contactID][$name][] = $save;
}
$values[$contactID][$name][] = $value;
$chunks = &splitContactIDs($contactIDs);
foreach ($chunks as $chunk) {
- $values = array();
+ $values = [];
getValues($chunk, $values);
$xml = &generateSolrJSON($values);
echo $xml;
}
}
-$config = &CRM_Core_Config::singleton();
+$config = CRM_Core_Config::singleton();
$config->userFramework = 'Soap';
$config->userFrameworkClass = 'CRM_Utils_System_Soap';
$config->userHookClass = 'CRM_Utils_Hook_Soap';
$dao = &CRM_Core_DAO::executeQuery($sql);
-$contactIDs = array();
+$contactIDs = [];
while ($dao->fetch()) {
$contactIDs[] = $dao->id;
}
function &splitContactIDs(&$contactIDs) {
// contactIDs could be a real large array, so we split it up into
// smaller chunks and then general xml for each chunk
- $chunks = array();
- $current = 0;
- $chunks[$current] = array();
- $count = 0;
+ $chunks = [];
+ $current = 0;
+ $chunks[$current] = [];
+ $count = 0;
foreach ($contactIDs as $cid) {
$chunks[$current][] = $cid;
if ($count == CHUNK_SIZE) {
$current++;
- $chunks[$current] = array();
+ $chunks[$current] = [];
$count = 0;
}
}
* @return array
*/
function getValues(&$contactIDs, &$values) {
- $values = array();
+ $values = [];
foreach ($contactIDs as $cid) {
- $values[$cid] = array();
+ $values[$cid] = [];
}
getContactInfo($contactIDs, $values);
if (!$name) {
$name = $fld;
}
- $values[$dao->contact_id][] = array($name, $dao->$fld);
+ $values[$dao->contact_id][] = [$name, $dao->$fld];
}
}
}
* @param $values
*/
function getContactInfo(&$contactIDs, &$values) {
- $fields = array('sort_name' => NULL,
+ $fields = [
+ 'sort_name' => NULL,
'display_name' => NULL,
'contact_type' => NULL,
'legal_identifier' => NULL,
'external_identifier' => NULL,
'source' => 'contact_source',
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_contact', $fields, 'id');
- $fields = array('first_name' => NULL,
+ $fields = [
+ 'first_name' => NULL,
'last_name' => NULL,
'middle_name' => NULL,
'job_title' => NULL,
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_individual', $fields, 'contact_id');
- $fields = array('household_name' => NULL);
+ $fields = ['household_name' => NULL];
getTableInfo($contactIDs, $values, 'civicrm_household', $fields, 'contact_id');
- $fields = array('organization_name' => NULL,
+ $fields = [
+ 'organization_name' => NULL,
'legal_name' => NULL,
'sic_code' => NULL,
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_organization', $fields, 'contact_id');
- $fields = array('note' => 'note_body',
+ $fields = [
+ 'note' => 'note_body',
'subject' => 'note_subject',
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_note', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
}
AND l.entity_id IN ( $ids )
";
- $fields = array('location_name', 'street_address', 'supplemental_address_1',
- 'supplemental_address_2', 'supplemental_address_3', 'city', 'postal_code', 'county', 'state',
- 'country', 'email', 'phone', 'im',
- );
+ $fields = [
+ 'location_name',
+ 'street_address',
+ 'supplemental_address_1',
+ 'supplemental_address_2',
+ 'supplemental_address_3',
+ 'city',
+ 'postal_code',
+ 'county',
+ 'state',
+ 'country',
+ 'email',
+ 'phone',
+ 'im',
+ ];
$dao = CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
continue;
}
- $values[$dao->contact_id][] = array($fld, $dao->$fld);
+ $values[$dao->contact_id][] = [$fld, $dao->$fld];
}
}
}
$chunks = &splitContactIDs($contactIDs);
foreach ($chunks as $chunk) {
- $values = array();
+ $values = [];
getValues($chunk, $values);
$xml = &generateSolrXML($values);
echo $xml;
}
}
-$config = &CRM_Core_Config::singleton();
+$config = CRM_Core_Config::singleton();
$config->userFramework = 'Soap';
$config->userFrameworkClass = 'CRM_Utils_System_Soap';
$config->userHookClass = 'CRM_Utils_Hook_Soap';
EOT;
$dao = CRM_Core_DAO::executeQuery($sql);
-$contactIDs = array();
+$contactIDs = [];
while ($dao->fetch()) {
$contactIDs[] = $dao->id;
}
function &splitContactIDs(&$contactIDs) {
// contactIDs could be a real large array, so we split it up into
// smaller chunks and then general xml for each chunk
- $chunks = array();
- $current = 0;
- $chunks[$current] = array();
- $count = 0;
+ $chunks = [];
+ $current = 0;
+ $chunks[$current] = [];
+ $count = 0;
foreach ($contactIDs as $k => $v) {
$chunks[$current][$k] = $v;
if ($count == CHUNK_SIZE) {
$current++;
- $chunks[$current] = array();
+ $chunks[$current] = [];
$count = 0;
}
}
* @return array
*/
function getValues(&$contactIDs, &$values, &$allContactIDs, &$addditionalContactIDs) {
- $values = array();
+ $values = [];
getContactInfo($contactIDs, $values);
getAddressInfo($contactIDs, $values);
* @param bool $flat
*/
function getTableInfo(&$contactIDs, &$values, $tableName, &$fields,
- $whereField, $additionalWhereCond = NULL,
- $flat = FALSE
+ $whereField, $additionalWhereCond = NULL,
+ $flat = FALSE
) {
$selectString = implode(',', array_keys($fields));
$idString = implode(',', $contactIDs);
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
- $contact = array();
+ $contact = [];
foreach ($fields as $fld => $name) {
$name = $name ? $name : $fld;
if (empty($dao->$fld)) {
* @param $values
*/
function getContactInfo(&$contactIDs, &$values) {
- $fields = array('id' => NULL,
+ $fields = [
+ 'id' => NULL,
'sort_name' => NULL,
'display_name' => NULL,
'contact_type' => NULL,
'organization_name' => NULL,
'legal_name' => NULL,
'job_title' => NULL,
- );
+ ];
getTableInfo($contactIDs, $values, 'civicrm_contact', $fields, 'id', NULL, TRUE);
}
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
- $note = array('id' => $dao->id,
+ $note = [
+ 'id' => $dao->id,
'contact_id' => $dao->contact_id,
'subject' => empty($dao->subject) ? NULL : $dao->subject,
'note' => empty($dao->note) ? NULL : $dao->note,
- );
+ ];
appendValue($values, $dao->id, 'note', $note);
}
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
- $phone = array('id' => $dao->id,
+ $phone = [
+ 'id' => $dao->id,
'contact_id' => $dao->contact_id,
'location_type' => empty($dao->location_type) ? NULL : $dao->location_type,
'phone' => $dao->phone,
'phone_type' => empty($dao->phone_type) ? NULL : $dao->phone_type,
- );
+ ];
appendValue($values, $dao->id, 'phone', $phone);
}
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
- $email = array('id' => $dao->id,
+ $email = [
+ 'id' => $dao->id,
'contact_id' => $dao->contact_id,
'location_type' => empty($dao->location_type) ? NULL : $dao->location_type,
'email' => $dao->email,
- );
+ ];
appendValue($values, $dao->id, 'email', $email);
}
$dao->free();
WHERE c.id IN ( $ids )
";
- $fields = array('id', 'contact_id',
- 'location_type', 'street_address', 'supplemental_address_1',
- 'supplemental_address_2', 'supplemental_address_3', 'city', 'postal_code',
- 'state', 'country',
- );
+ $fields = [
+ 'id',
+ 'contact_id',
+ 'location_type',
+ 'street_address',
+ 'supplemental_address_1',
+ 'supplemental_address_2',
+ 'supplemental_address_3',
+ 'city',
+ 'postal_code',
+ 'state',
+ 'country',
+ ];
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
- $address = array();
+ $address = [];
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
$address[$fld] = NULL;
*/
function getRelationshipInfo(&$contactIDs, &$values, &$allContactIDs, &$additionalContacts) {
// handle relationships only once
- static $_relationshipsHandled = array();
+ static $_relationshipsHandled = [];
$ids = implode(',', $contactIDs);
";
$relationshipFields = getDBFields('CRM_Contact_DAO_Relationship');
- $fields = array_keys($relationshipFields);
- $dao = &CRM_Core_DAO::executeQuery($sql);
+ $fields = array_keys($relationshipFields);
+ $dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
if (isset($_relationshipsHandled[$dao->id])) {
continue;
}
$_relationshipsHandled[$dao->id] = $dao->id;
- $relationship = array();
+ $relationship = [];
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
$relationship[$fld] = NULL;
}
appendValue($values, $dao->id, 'relationship', $relationship);
- addAdditionalContacts(array($dao->contact_id_a,
- $dao->contact_id_b,
- ),
+ addAdditionalContacts([
+ $dao->contact_id_a,
+ $dao->contact_id_b,
+ ],
$allContactIDs, $additionalContacts
);
}
* @param $additionalContacts
*/
function getActivityInfo(&$contactIDs, &$values, &$allContactIDs, &$additionalContacts) {
- static $_activitiesHandled = array();
+ static $_activitiesHandled = [];
$ids = implode(',', $contactIDs);
$activityFields = &getDBFields('CRM_Activity_DAO_Activity');
$fields = array_keys($activityFields);
- $activityIDs = array();
+ $activityIDs = [];
$dao = &CRM_Core_DAO::executeQuery($sql);
while ($dao->fetch()) {
if (isset($_activitiesHandled[$dao->id])) {
$_activitiesHandled[$dao->id] = $dao->id;
$activityIDs[] = $dao->id;
- $activity = array();
+ $activity = [];
foreach ($fields as $fld) {
if (empty($dao->$fld)) {
$activity[$fld] = NULL;
}
appendValue($values, $dao->id, 'activity', $activity);
- addAdditionalContacts(array($dao->source_contact_id),
+ addAdditionalContacts([$dao->source_contact_id],
$allContactIDs, $additionalContacts
);
}
$activityIDString = implode(",", $activityIDs);
// now get all assignee contact ids and target contact ids for this activity
- $sql = "SELECT * FROM civicrm_activity_assignment WHERE activity_id IN ($activityIDString)";
- $aaDAO = &CRM_Core_DAO::executeQuery($sql);
- $activityContacts = array();
+ $sql = "SELECT * FROM civicrm_activity_assignment WHERE activity_id IN ($activityIDString)";
+ $aaDAO = &CRM_Core_DAO::executeQuery($sql);
+ $activityContacts = [];
while ($aaDAO->fetch()) {
- $activityAssignee = array('id' => $aaDAO->id,
+ $activityAssignee = [
+ 'id' => $aaDAO->id,
'assignee_contact_id' => $aaDAO->assignee_contact_id,
'activity_id' => $aaDAO->activity_id,
- );
+ ];
appendValue($values, $aaDAO->id, 'activity_assignment', $activityAssignee);
$activityContacts[] = $aaDAO->assignee_contact_id;
}
$sql = "SELECT * FROM civicrm_activity_target WHERE activity_id IN ($activityIDString)";
$atDAO = &CRM_Core_DAO::executeQuery($sql);
while ($atDAO->fetch()) {
- $activityTarget = array('id' => $atDAO->id,
+ $activityTarget = [
+ 'id' => $atDAO->id,
'target_contact_id' => $atDAO->target_contact_id,
'activity_id' => $atDAO->activity_id,
- );
+ ];
appendValue($values, $atDAO->id, 'activity_target', $activityTarget);
$activityContacts[] = $atDAO->target_contact_id;
}
}
if (!isset($values[$name])) {
- $values[$name] = array();
+ $values[$name] = [];
$values[$name][] = array_keys($value);
}
$values[$name][] = array_values($value);
* @return mixed
*/
function getDBFields($daoName) {
- static $_fieldsRetrieved = array();
+ static $_fieldsRetrieved = [];
if (!isset($_fieldsRetrieved[$daoName])) {
- $_fieldsRetrieved[$daoName] = array();
+ $_fieldsRetrieved[$daoName] = [];
$daoFile = str_replace('_',
- DIRECTORY_SEPARATOR,
- $daoName
- ) . '.php';
- include_once ($daoFile);
+ DIRECTORY_SEPARATOR,
+ $daoName
+ ) . '.php';
+ include_once($daoFile);
$daoFields = &$daoName::fields();
require_once 'CRM/Utils/Array.php';
foreach ($daoFields as $key => & $value) {
- $_fieldsRetrieved[$daoName][$value['name']] = array('uniqueName' => $key,
+ $_fieldsRetrieved[$daoName][$value['name']] = [
+ 'uniqueName' => $key,
'type' => $value['type'],
'title' => CRM_Utils_Array::value('title', $value, NULL),
- );
+ ];
}
}
return $_fieldsRetrieved[$daoName];
function run(&$values, &$contactIDs, &$allContactIDs) {
$chunks = &splitContactIDs($contactIDs);
- $additionalContactIDs = array();
+ $additionalContactIDs = [];
foreach ($chunks as $chunk) {
getValues($chunk, $values, $allContactIDs, $additionalContactIDs);
}
}
-$config = &CRM_Core_Config::singleton();
+$config = CRM_Core_Config::singleton();
$config->userFramework = 'Soap';
$config->userFrameworkClass = 'CRM_Utils_System_Soap';
$config->userHookClass = 'CRM_Utils_Hook_Soap';
$dao = &CRM_Core_DAO::executeQuery($sql);
-$contactIDs = array();
+$contactIDs = [];
while ($dao->fetch()) {
$contactIDs[$dao->id] = $dao->id;
}
-$values = array();
+$values = [];
run($values, $contactIDs, $contactIDs);
$json = json_encode($values);
// FIXME: Make this a proper app with unit-tests
-$civi_pkgs_dir = dirname( dirname( dirname( __FILE__ ) ) ) . DIRECTORY_SEPARATOR . 'packages';
+$civi_pkgs_dir = dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'packages';
require_once $civi_pkgs_dir . DIRECTORY_SEPARATOR . 'simple_html_dom.php';
exit(main($argv));
$files = $argv;
array_shift($files); // skip program name
foreach ($files as $file) {
- check_tpl($file, function($code, $message) use ($file) {
+ check_tpl($file, function ($code, $message) use ($file) {
printf("[%s] %s\n", $file, $message);
});
}
<localizable>true</localizable>
<comment>Name of Group.</comment>
<add>1.1</add>
+ <html>
+ <type>Text</type>
+ </html>
</field>
<field>
<name>description</name>
<log>true</log>
<field>
<name>id</name>
+ <uniqueName>contribution_recur_id</uniqueName>
<title>Recurring Contribution ID</title>
<type>int unsigned</type>
<required>true</required>
</field>
<field>
<name>processor_id</name>
+ <uniqueName>contribution_recur_processor_id</uniqueName>
<title>Processor ID</title>
<type>varchar</type>
<length>255</length>
</foreignKey>
<field>
<name>trxn_id</name>
+ <uniqueName>contribution_recur_trxn_id</uniqueName>
<title>Transaction ID</title>
<type>varchar</type>
<length>255</length>
</index>
<field>
<name>contribution_status_id</name>
+ <uniqueName>contribution_recur_contribution_status_id</uniqueName>
<title>Status</title>
<type>int unsigned</type>
<default>1</default>
<field>
<name>payment_processor_id</name>
<title>Payment Processor</title>
+ <uniqueName>contribution_recur_payment_processor_id</uniqueName>
<type>int unsigned</type>
<comment>Foreign key to civicrm_payment_processor.id</comment>
<add>3.3</add>
<?xml version="1.0" encoding="iso-8859-1" ?>
<version>
- <version_no>5.14.alpha1</version_no>
+ <version_no>5.15.alpha1</version_no>
</version>