Commit 29cde71a authored by Christian Wach's avatar Christian Wach
Browse files

Merge remote-tracking branch 'remotes/upstream/master'

parents b51d2f49 2ea958b8
......@@ -2,7 +2,7 @@
/*
Plugin Name: CiviCRM
Description: CiviCRM - Growing and Sustaining Relationships
Version: 5.30.1
Version: 5.31.1
Requires at least: 4.9
Requires PHP: 7.1
Author: CiviCRM LLC
......@@ -56,7 +56,7 @@ if ( ! defined( 'ABSPATH' ) ) exit;
// Set version here: when it changes, will force JS to reload
define( 'CIVICRM_PLUGIN_VERSION', '5.30.1' );
define( 'CIVICRM_PLUGIN_VERSION', '5.31.1' );
// Store reference to this file
if (!defined('CIVICRM_PLUGIN_FILE')) {
......
......@@ -204,36 +204,29 @@ SELECT acl.*
protected static function getGroupACLRoles($contact_id) {
$contact_id = CRM_Utils_Type::escape($contact_id, 'Integer');
$rule = new CRM_ACL_BAO_ACL();
$aclRole = 'civicrm_acl_role';
$aclER = CRM_ACL_DAO_EntityRole::getTableName();
$c2g = CRM_Contact_BAO_GroupContact::getTableName();
$query = " SELECT acl.*
FROM civicrm_acl acl
INNER JOIN civicrm_option_group og
ON og.name = 'acl_role'
INNER JOIN civicrm_option_value ov
ON acl.entity_table = '$aclRole'
ON acl.entity_table = 'civicrm_acl_role'
AND ov.option_group_id = og.id
AND acl.entity_id = ov.value
AND ov.is_active = 1
INNER JOIN $aclER
ON $aclER.acl_role_id = acl.entity_id
AND $aclER.is_active = 1
INNER JOIN $c2g
ON $aclER.entity_id = $c2g.group_id
AND $aclER.entity_table = 'civicrm_group'
WHERE acl.entity_table = '$aclRole'
INNER JOIN civicrm_acl_entity_role acl_entity_role
ON acl_entity_role.acl_role_id = acl.entity_id
AND acl_entity_role.is_active = 1
INNER JOIN civicrm_group_contact group_contact
ON acl_entity_role.entity_id = group_contact.group_id
AND acl_entity_role.entity_table = 'civicrm_group'
WHERE acl.entity_table = 'civicrm_acl_role'
AND acl.is_active = 1
AND $c2g.contact_id = $contact_id
AND $c2g.status = 'Added'";
AND group_contact.contact_id = $contact_id
AND group_contact.status = 'Added'";
$results = [];
$rule->query($query);
$rule = CRM_Core_DAO::executeQuery($query);
while ($rule->fetch()) {
$results[$rule->id] = $rule->toArray();
......@@ -254,7 +247,7 @@ SELECT acl.*
AND acl.entity_table = 'civicrm_acl_role'
";
$rule->query($query);
$rule = CRM_Core_DAO::executeQuery($query);
while ($rule->fetch()) {
$results[$rule->id] = $rule->toArray();
}
......@@ -484,10 +477,10 @@ SELECT g.*
$aclKeys = array_keys($acls);
$aclKeys = implode(',', $aclKeys);
$cacheKey = CRM_Utils_Cache::cleanKey("$tableName-$aclKeys");
$cacheKey = CRM_Utils_Cache::cleanKey("$type-$tableName-$aclKeys");
$cache = CRM_Utils_Cache::singleton();
$ids = $cache->get($cacheKey);
if (!$ids) {
if (!is_array($ids)) {
$ids = [];
$query = "
SELECT a.operation, a.object_id
......
......@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/ACL/ACL.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:f75eaa0ee87675c14a224ec22b2c30a7)
* (GenCodeChecksum:54e8c75c28c9dd74192f60bbcf1605f6)
*/
/**
......@@ -117,9 +117,12 @@ class CRM_ACL_DAO_ACL extends CRM_Core_DAO {
/**
* Returns localized title of this entity.
*
* @param bool $plural
* Whether to return the plural version of the title.
*/
public static function getEntityTitle() {
return ts('ACLs');
public static function getEntityTitle($plural = FALSE) {
return $plural ? ts('ACLs') : ts('ACL');
}
/**
......
......@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/ACL/ACLCache.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:cbf36d56ce734a5f7ceeb2071b68ebf8)
* (GenCodeChecksum:7faa5879056a56b463304bd81829afda)
*/
/**
......@@ -68,9 +68,12 @@ class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO {
/**
* Returns localized title of this entity.
*
* @param bool $plural
* Whether to return the plural version of the title.
*/
public static function getEntityTitle() {
return ts('ACLCaches');
public static function getEntityTitle($plural = FALSE) {
return $plural ? ts('ACLCaches') : ts('ACLCache');
}
/**
......@@ -82,7 +85,6 @@ class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO {
public static function getReferenceColumns() {
if (!isset(Civi::$statics[__CLASS__]['links'])) {
Civi::$statics[__CLASS__]['links'] = static::createReferenceColumns(__CLASS__);
Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'contact_id', 'civicrm_contact', 'id');
Civi::$statics[__CLASS__]['links'][] = new CRM_Core_Reference_Basic(self::getTableName(), 'acl_id', 'civicrm_acl', 'id');
CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'links_callback', Civi::$statics[__CLASS__]['links']);
}
......@@ -120,7 +122,6 @@ class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO {
'entity' => 'ACLCache',
'bao' => 'CRM_ACL_DAO_ACLCache',
'localizable' => 0,
'FKClassName' => 'CRM_Contact_DAO_Contact',
'html' => [
'type' => 'EntityRef',
],
......@@ -228,6 +229,14 @@ class CRM_ACL_DAO_ACLCache extends CRM_Core_DAO {
*/
public static function indices($localize = TRUE) {
$indices = [
'index_contact_id' => [
'name' => 'index_contact_id',
'field' => [
0 => 'contact_id',
],
'localizable' => FALSE,
'sig' => 'civicrm_acl_cache::0::contact_id',
],
'index_acl_id' => [
'name' => 'index_acl_id',
'field' => [
......
......@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/ACL/EntityRole.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:d985c951ef9a8872008576b41c1f2b9c)
* (GenCodeChecksum:c1087517beb5b266d4a1a0a1a342ced0)
*/
/**
......@@ -75,9 +75,12 @@ class CRM_ACL_DAO_EntityRole extends CRM_Core_DAO {
/**
* Returns localized title of this entity.
*
* @param bool $plural
* Whether to return the plural version of the title.
*/
public static function getEntityTitle() {
return ts('Entity Roles');
public static function getEntityTitle($plural = FALSE) {
return $plural ? ts('Entity Roles') : ts('Entity Role');
}
/**
......
......@@ -96,11 +96,12 @@ class CRM_ACL_Form_ACL extends CRM_Admin_Form {
$this->add('text', 'name', ts('Description'), CRM_Core_DAO::getAttribute('CRM_ACL_DAO_ACL', 'name'), TRUE);
$operations = ['' => ts('- select -')] + CRM_ACL_BAO_ACL::operation();
$this->add('select',
'operation',
ts('Operation'),
$operations, TRUE
CRM_ACL_BAO_ACL::operation(),
TRUE,
['placeholder' => TRUE]
);
$objTypes = [
......@@ -129,22 +130,22 @@ class CRM_ACL_Form_ACL extends CRM_Admin_Form {
$this->add('select', 'entity_id', $label, $role, TRUE);
$group = [
'-1' => ts('- select -'),
'-1' => ts('- select group -'),
'0' => ts('All Groups'),
] + CRM_Core_PseudoConstant::group();
$customGroup = [
'-1' => ts('- select -'),
'-1' => ts('- select set of custom fields -'),
'0' => ts('All Custom Groups'),
] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_CustomField', 'custom_group_id');
$ufGroup = [
'-1' => ts('- select -'),
'-1' => ts('- select profile -'),
'0' => ts('All Profiles'),
] + CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
$event = [
'-1' => ts('- select -'),
'-1' => ts('- select event -'),
'0' => ts('All Events'),
] + CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )");
......
......@@ -26,9 +26,8 @@ class CRM_ACL_Form_EntityRole extends CRM_Admin_Form {
return;
}
$aclRoles = ['' => ts('- select -')] + CRM_Core_OptionGroup::values('acl_role');
$this->add('select', 'acl_role_id', ts('ACL Role'),
$aclRoles, TRUE
CRM_Core_OptionGroup::values('acl_role'), TRUE, ['placeholder' => TRUE]
);
$label = ts('Assigned to');
......
......@@ -118,4 +118,12 @@ class CRM_Activity_ActionMapping extends \Civi\ActionSchedule\Mapping {
return $query;
}
/**
* Determine whether a schedule based on this mapping should
* send to additional contacts.
*/
public function sendToAdditional($entityId): bool {
return TRUE;
}
}
......@@ -9,6 +9,8 @@
+--------------------------------------------------------------------+
*/
use Civi\Api4\ActivityContact;
/**
*
* @package CRM
......@@ -315,13 +317,8 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
$params['assignee_contact_id'] = array_unique($params['assignee_contact_id']);
}
// CRM-9137
if (!empty($params['id'])) {
CRM_Utils_Hook::pre('edit', 'Activity', $params['id'], $params);
}
else {
CRM_Utils_Hook::pre('create', 'Activity', NULL, $params);
}
$action = empty($params['id']) ? 'create' : 'edit';
CRM_Utils_Hook::pre($action, 'Activity', $params['id'] ?? NULL, $params);
$activity->copyValues($params);
if (isset($params['case_id'])) {
......@@ -344,124 +341,79 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
}
$activityId = $activity->id;
$sourceID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Source');
$assigneeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Assignees');
$targetID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets');
if (isset($params['source_contact_id'])) {
$acParams = [
'activity_id' => $activityId,
'contact_id' => $params['source_contact_id'],
'record_type_id' => $sourceID,
];
self::deleteActivityContact($activityId, $sourceID);
CRM_Activity_BAO_ActivityContact::create($acParams);
}
// check and attach and files as needed
CRM_Core_BAO_File::processAttachment($params, 'civicrm_activity', $activityId);
// attempt to save activity assignment
$resultAssignment = NULL;
if (!empty($params['assignee_contact_id'])) {
$assignmentParams = ['activity_id' => $activityId];
$activityRecordTypes = [
'source_contact_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Source'),
'assignee_contact_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Assignees'),
'target_contact_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'),
];
if (is_array($params['assignee_contact_id'])) {
if (CRM_Utils_Array::value('deleteActivityAssignment', $params, TRUE)) {
// first delete existing assignments if any
self::deleteActivityContact($activityId, $assigneeID);
$activityContacts = [];
// Cast to an array if we just have an integer. Index by record type id.
foreach ($activityRecordTypes as $key => $recordTypeID) {
if (isset($params[$key])) {
if (empty($params[$key])) {
$activityContacts[$recordTypeID] = [];
}
foreach ($params['assignee_contact_id'] as $acID) {
if ($acID) {
$assigneeParams = [
'activity_id' => $activityId,
'contact_id' => $acID,
'record_type_id' => $assigneeID,
];
CRM_Activity_BAO_ActivityContact::create($assigneeParams);
else {
foreach ((array) $params[$key] as $contactID) {
$activityContacts[$recordTypeID][$contactID] = (int) $contactID;
}
}
}
else {
$assignmentParams['contact_id'] = $params['assignee_contact_id'];
$assignmentParams['record_type_id'] = $assigneeID;
if (!empty($params['id'])) {
$assignment = new CRM_Activity_BAO_ActivityContact();
$assignment->activity_id = $activityId;
$assignment->record_type_id = $assigneeID;
$assignment->find(TRUE);
if ($assignment->contact_id != $params['assignee_contact_id']) {
$assignmentParams['id'] = $assignment->id;
$resultAssignment = CRM_Activity_BAO_ActivityContact::create($assignmentParams);
}
}
if ($action === 'edit' && !empty($activityContacts)) {
$wheres = [];
foreach ($activityContacts as $recordTypeID => $contactIDs) {
if (!empty($contactIDs)) {
$wheres[$key] = "(record_type_id = $recordTypeID AND contact_id IN (" . implode(',', $contactIDs) . '))';
}
else {
$resultAssignment = CRM_Activity_BAO_ActivityContact::create($assignmentParams);
}
$existingArray = empty($wheres) ? [] : CRM_Core_DAO::executeQuery("
SELECT id, contact_id, record_type_id
FROM civicrm_activity_contact
WHERE activity_id = %1
AND (" . implode(' OR ', $wheres) . ')',
[1 => [$params['id'], 'Integer']])->fetchAll();
$recordsToKeep = [];
$wheres = [['activity_id', '=', $params['id']], ['record_type_id', 'IN', array_keys($activityContacts)]];
foreach ($existingArray as $existingRecords) {
$recordsToKeep[$existingRecords['id']] = ['contact_id' => $existingRecords['contact_id'], 'record_type_id' => $existingRecords['record_type_id']];
unset($activityContacts[$recordTypeID][$existingRecords['contact_id']]);
if (empty($activityContacts[$recordTypeID])) {
// If we just removed the last one to update then also unset the key.
unset($activityContacts[$recordTypeID]);
}
}
}
else {
if (CRM_Utils_Array::value('deleteActivityAssignment', $params, TRUE)) {
self::deleteActivityContact($activityId, $assigneeID);
if (!empty($recordsToKeep)) {
$wheres[] = ['id', 'NOT IN', array_keys($recordsToKeep)];
}
}
if (is_a($resultAssignment, 'CRM_Core_Error')) {
$transaction->rollback();
return $resultAssignment;
// Delete all existing records for the types to be updated. Do a quick check to make sure there
// is at least one to avoid a delete query if not necessary (delete queries are more likely to cause contention).
if (ActivityContact::get($params['check_permissions'] ?? FALSE)->setLimit(1)->setWhere($wheres)->selectRowCount()->execute()) {
ActivityContact::delete($params['check_permissions'] ?? FALSE)->setWhere($wheres)->execute();
}
}
// attempt to save activity targets
$resultTarget = NULL;
if (!empty($params['target_contact_id'])) {
$targetParams = ['activity_id' => $activityId];
$resultTarget = [];
if (is_array($params['target_contact_id'])) {
if (CRM_Utils_Array::value('deleteActivityTarget', $params, TRUE)) {
// first delete existing targets if any
self::deleteActivityContact($activityId, $targetID);
}
foreach ($params['target_contact_id'] as $tid) {
if ($tid) {
$targetContactParams = [
'activity_id' => $activityId,
'contact_id' => $tid,
'record_type_id' => $targetID,
];
CRM_Activity_BAO_ActivityContact::create($targetContactParams);
}
}
}
else {
$targetParams['contact_id'] = $params['target_contact_id'];
$targetParams['record_type_id'] = $targetID;
if (!empty($params['id'])) {
$target = new CRM_Activity_BAO_ActivityContact();
$target->activity_id = $activityId;
$target->record_type_id = $targetID;
$target->find(TRUE);
if ($target->contact_id != $params['target_contact_id']) {
$targetParams['id'] = $target->id;
$resultTarget = CRM_Activity_BAO_ActivityContact::create($targetParams);
}
}
else {
$resultTarget = CRM_Activity_BAO_ActivityContact::create($targetParams);
}
$activityContactApiValues = [];
foreach ($activityContacts as $recordTypeID => $contactIDs) {
foreach ($contactIDs as $contactID) {
$activityContactApiValues[] = ['record_type_id' => $recordTypeID, 'contact_id' => $contactID];
}
}
else {
if (CRM_Utils_Array::value('deleteActivityTarget', $params, TRUE)) {
self::deleteActivityContact($activityId, $targetID);
}
if (!empty($activityContactApiValues)) {
ActivityContact::save($params['check_permissions'] ?? FALSE)->addDefault('activity_id', $activityId)
->setRecords($activityContactApiValues)->execute();
}
// check and attach and files as needed
CRM_Core_BAO_File::processAttachment($params, 'civicrm_activity', $activityId);
// write to changelog before transaction is committed/rolled
// back (and prepare status to display)
if (!empty($params['id'])) {
......@@ -607,13 +559,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
self::logActivityAction($activity, "Case details for {$matches[1]} not found while recording an activity on case.");
}
}
if (!empty($params['id'])) {
CRM_Utils_Hook::post('edit', 'Activity', $activity->id, $activity);
}
else {
CRM_Utils_Hook::post('create', 'Activity', $activity->id, $activity);
}
CRM_Utils_Hook::post($action, 'Activity', $activity->id, $activity);
return $result;
}
......@@ -1694,21 +1640,12 @@ WHERE activity.id IN ($activityIds)";
*/
public static function addActivity(
$activity,
$activityType = 'Membership Signup',
$activityType,
$targetContactID = NULL,
$params = []
) {
$date = date('YmdHis');
if ($activity->__table == 'civicrm_membership') {
$component = 'Membership';
}
elseif ($activity->__table == 'civicrm_participant') {
if ($activityType != 'Email') {
$activityType = 'Event Registration';
}
$component = 'Event';
}
elseif ($activity->__table == 'civicrm_contribution') {
if ($activity->__table == 'civicrm_contribution') {
// create activity record only for Completed Contributions
$contributionCompletedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
if ($activity->contribution_status_id != $contributionCompletedStatusId) {
......@@ -1718,7 +1655,6 @@ WHERE activity.id IN ($activityIds)";
}
$params['status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_status_id', 'Scheduled');
}
$activityType = $component = 'Contribution';
// retrieve existing activity based on source_record_id and activity_type
if (empty($params['id'])) {
......@@ -1732,7 +1668,7 @@ WHERE activity.id IN ($activityIds)";
$params['campaign_id'] = $activity->campaign_id;
}
$date = CRM_Utils_Date::isoToMysql($activity->receive_date);
$date = $activity->receive_date;
}
$activityParams = [
......@@ -1772,7 +1708,7 @@ WHERE activity.id IN ($activityIds)";
// @todo - use api - remove lots of wrangling above. Remove deprecated fatal & let form layer
// deal with any exceptions.
if (is_a(self::create($activityParams), 'CRM_Core_Error')) {
throw new CRM_Core_Exception("Failed creating Activity for $component of id {$activity->id}");
throw new CRM_Core_Exception("Failed creating Activity of type $activityType for entity id {$activity->id}");
}
}
......@@ -2630,7 +2566,7 @@ INNER JOIN civicrm_option_group grp ON (grp.id = option_group_id AND grp.name =
}
}
}
elseif (!$values['target_contact_name']) {
elseif (empty($values['target_contact_name'])) {
$activity['target_contact_name'] = '<em>n/a</em>';
}
......
......@@ -37,13 +37,22 @@ class CRM_Activity_BAO_ActivityContact extends CRM_Activity_DAO_ActivityContact
* activity_contact object
*/
public static function create($params) {
$errorScope = CRM_Core_TemporaryErrorScope::useException();
$activityContact = new CRM_Activity_DAO_ActivityContact();
$activityContact->copyValues($params);
if (!$activityContact->find(TRUE)) {
try {
return $activityContact->save();
}
return $activityContact;
catch (PEAR_Exception $e) {
// This check used to be done first, creating an extra query before each insert.
// However, in none of the core tests was this ever called with values that already
// existed, meaning that this line would never or almost never be hit.
// hence it's better to save the select query here.
if ($activityContact->find(TRUE)) {
return $activityContact;
}
throw $e;
}
}
/**
......
......@@ -478,7 +478,7 @@ class CRM_Activity_BAO_Query {
'multiple' =>
'multiple',
'class' => 'crm-select2',
'placeholder' => ts('- select -'),
'placeholder' => ts('- select tags -'),
]
);
}
......
......@@ -28,6 +28,8 @@
*/
class CRM_Activity_Controller_Search extends CRM_Core_Controller {
protected $entity = 'Activity';
/**
* Class constructor.
*
......@@ -46,6 +48,7 @@ class CRM_Activity_Controller_Search extends CRM_Core_Controller {
// Add all the actions.
$this->addActions();
$this->set('entity', $this->entity);
}
/**
......
......@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Activity/Activity.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
* (GenCodeChecksum:cbcbcbb6720f015deae4097b01196c9a)
* (GenCodeChecksum:c1b4cc908c0220abf69f57d281eeda95)
*/
/**
......@@ -226,9 +226,12 @@ class CRM_Activity_DAO_Activity extends CRM_Core_DAO {
/**
* Returns localized title of this entity.
*
* @param bool $plural