3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2020 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2020
35 * This class generates form components for Activity.
37 class CRM_Activity_Form_Activity
extends CRM_Contact_Form_Task
{
40 * The id of the object being edited / created
47 * Store activity ids when multiple activities are created.
51 public $_activityIds = [];
54 * The id of activity type.
58 public $_activityTypeId;
61 * The name of activity type.
65 public $_activityTypeName;
68 * The id of currently viewed contact.
72 public $_currentlyViewedContactId;
75 * The id of source contact and target contact.
79 protected $_sourceContactId;
80 protected $_targetContactId;
81 protected $_asigneeContactId;
88 public $_activityTypeFile;
91 * The id of the logged in user, used when add / edit
95 public $_currentUserId;
98 * The array of form field attributes.
105 * The the directory inside CRM, to include activity type file from
109 protected $_crmDir = 'Activity';
116 protected $_isSurveyActivity;
118 protected $_values = [];
120 protected $unsavedWarn = TRUE;
124 * Is it possible to create separate activities with this form?
126 * When TRUE, the form will ask whether the user wants to create separate
127 * activities (if the user has specified multiple contacts in the "with"
130 * When FALSE, the form will create one activity with all contacts together
131 * and won't ask the user anything.
133 * Note: This is a class property so that child classes can turn off this
134 * behavior (e.g. in CRM_Case_Form_Activity)
139 protected $supportsActivitySeparation = TRUE;
141 public $submitOnce = TRUE;
144 * Explicitly declare the entity api name.
148 public function getDefaultEntity() {
153 * The _fields var can be used by sub class to set/unset/edit the
154 * form fields based on their requirement
156 public function setFields() {
157 // Remove print document activity type
158 $unwanted = CRM_Core_OptionGroup
::values('activity_type', FALSE, FALSE, FALSE, "AND v.name = 'Print PDF Letter'");
159 $activityTypes = array_diff_key(CRM_Core_PseudoConstant
::ActivityType(FALSE), $unwanted);
164 'label' => ts('Subject'),
165 'attributes' => CRM_Core_DAO
::getAttribute('CRM_Activity_DAO_Activity', 'activity_subject'),
169 'label' => ts('Duration'),
170 'attributes' => ['class' => 'four', 'min' => 1],
175 'label' => ts('Location'),
176 'attributes' => CRM_Core_DAO
::getAttribute('CRM_Activity_DAO_Activity', 'location'),
181 'label' => ts('Details'),
182 'attributes' => ['class' => 'huge'],
193 'source_contact_id' => [
194 'type' => 'entityRef',
195 'label' => ts('Added By'),
198 'target_contact_id' => [
199 'type' => 'entityRef',
200 'label' => ts('With Contact'),
201 'attributes' => ['multiple' => TRUE, 'create' => TRUE],
203 'assignee_contact_id' => [
204 'type' => 'entityRef',
205 'label' => ts('Assigned to'),
209 'api' => ['params' => ['is_deceased' => 0]],
212 'activity_date_time' => [
213 'type' => 'datepicker',
214 'label' => ts('Date'),
217 'followup_assignee_contact_id' => [
218 'type' => 'entityRef',
219 'label' => ts('Assigned to'),
223 'api' => ['params' => ['is_deceased' => 0]],
226 'followup_activity_type_id' => [
228 'label' => ts('Followup Activity'),
229 'attributes' => ['' => '- ' . ts('select activity') . ' -'] +
$activityTypes,
230 'extra' => ['class' => 'crm-select2'],
232 // Add optional 'Subject' field for the Follow-up Activiity, CRM-4491
233 'followup_activity_subject' => [
235 'label' => ts('Subject'),
236 'attributes' => CRM_Core_DAO
::getAttribute('CRM_Activity_DAO_Activity', 'subject'),
242 * Build the form object.
244 public function preProcess() {
245 CRM_Core_Form_RecurringEntity
::preProcess('civicrm_activity');
246 $this->_atypefile
= CRM_Utils_Array
::value('atypefile', $_GET);
247 $this->assign('atypefile', FALSE);
248 if ($this->_atypefile
) {
249 $this->assign('atypefile', TRUE);
252 $session = CRM_Core_Session
::singleton();
253 $this->_currentUserId
= CRM_Core_Session
::getLoggedInContactID();
255 $this->_currentlyViewedContactId
= $this->get('contactId');
256 if (!$this->_currentlyViewedContactId
) {
257 $this->_currentlyViewedContactId
= CRM_Utils_Request
::retrieve('cid', 'Positive', $this);
259 $this->assign('contactId', $this->_currentlyViewedContactId
);
262 if (!isset($this->_context
)) {
263 $this->_context
= CRM_Utils_Request
::retrieve('context', 'Alphanumeric', $this);
264 if (CRM_Contact_Form_Search
::isSearchContext($this->_context
)) {
265 $this->_context
= 'search';
267 elseif (!in_array($this->_context
, ['dashlet', 'case', 'dashletFullscreen'])
268 && $this->_currentlyViewedContactId
270 $this->_context
= 'activity';
272 $this->_compContext
= CRM_Utils_Request
::retrieve('compContext', 'String', $this);
275 $this->assign('context', $this->_context
);
277 $this->_action
= CRM_Utils_Request
::retrieve('action', 'String', $this);
279 if ($this->_action
& CRM_Core_Action
::DELETE
) {
280 if (!CRM_Core_Permission
::check('delete activities')) {
281 CRM_Core_Error
::statusBounce(ts('You do not have permission to access this page.'));
286 // When we come from contact search, activity id never comes.
287 // So don't try to get from object, it might gives you wrong one.
289 // if we're not adding new one, there must be an id to
290 // an activity we're trying to work on.
291 if ($this->_action
!= CRM_Core_Action
::ADD
&&
292 get_class($this->controller
) != 'CRM_Contact_Controller_Search'
294 $this->_activityId
= CRM_Utils_Request
::retrieve('id', 'Positive', $this);
297 $this->_activityTypeId
= CRM_Utils_Request
::retrieve('atype', 'Positive', $this);
298 $this->assign('atype', $this->_activityTypeId
);
300 $this->assign('activityId', $this->_activityId
);
302 // Check for required permissions, CRM-6264.
303 if ($this->_activityId
&&
304 in_array($this->_action
, [
305 CRM_Core_Action
::UPDATE
,
306 CRM_Core_Action
::VIEW
,
308 !CRM_Activity_BAO_Activity
::checkPermission($this->_activityId
, $this->_action
)
310 CRM_Core_Error
::statusBounce(ts('You do not have permission to access this page.'));
312 if (($this->_action
& CRM_Core_Action
::VIEW
) &&
313 CRM_Activity_BAO_Activity
::checkPermission($this->_activityId
, CRM_Core_Action
::UPDATE
)
315 $this->assign('permission', 'edit');
316 $this->assign('allow_edit_inbound_emails', CRM_Activity_BAO_Activity
::checkEditInboundEmailsPermissions());
319 if (!$this->_activityTypeId
&& $this->_activityId
) {
320 $this->_activityTypeId
= CRM_Core_DAO
::getFieldValue('CRM_Activity_DAO_Activity',
326 $this->assignActivityType();
328 // Check the mode when this form is called either single or as
329 // search task action.
330 if ($this->_activityTypeId ||
331 $this->_context
== 'standalone' ||
332 $this->_currentlyViewedContactId
334 $this->_single
= TRUE;
335 $this->assign('urlPath', 'civicrm/activity');
338 // Set the appropriate action.
339 $url = CRM_Utils_System
::currentPath();
340 $urlArray = explode('/', $url);
341 $searchPath = array_pop($urlArray);
342 $searchType = 'basic';
343 $this->_action
= CRM_Core_Action
::BASIC
;
344 switch ($searchPath) {
346 $searchType = $searchPath;
347 $this->_action
= CRM_Core_Action
::BASIC
;
351 $searchType = $searchPath;
352 $this->_action
= CRM_Core_Action
::ADVANCED
;
356 $searchType = $searchPath;
357 $this->_action
= CRM_Core_Action
::PROFILE
;
361 $this->_action
= CRM_Core_Action
::COPY
;
362 $searchType = $searchPath;
366 parent
::preProcess();
367 $this->_single
= FALSE;
369 $this->assign('urlPath', "civicrm/contact/search/$searchType");
370 $this->assign('urlPathVar', "_qf_Activity_display=true&qfKey={$this->controller->_key}");
373 $this->assign('single', $this->_single
);
374 $this->assign('action', $this->_action
);
376 if ($this->_action
& CRM_Core_Action
::VIEW
) {
377 // Get the tree of custom fields.
378 $this->_groupTree
= CRM_Core_BAO_CustomGroup
::getTree('Activity', NULL,
379 $this->_activityId
, 0, $this->_activityTypeId
383 if ($this->_activityTypeId
) {
384 // Set activity type name and description to template.
385 list($this->_activityTypeName
, $activityTypeDescription) = CRM_Core_BAO_OptionValue
::getActivityTypeDetails($this->_activityTypeId
);
386 $this->assign('activityTypeName', $this->_activityTypeName
);
387 $this->assign('activityTypeDescription', $activityTypeDescription);
391 $urlParams = $urlString = NULL;
392 $qfKey = CRM_Utils_Request
::retrieve('key', 'String', $this);
394 $qfKey = CRM_Utils_Request
::retrieve('qfKey', 'String', $this);
397 // Validate the qfKey.
398 if (!CRM_Utils_Rule
::qfKey($qfKey)) {
402 if ($this->_context
== 'fulltext') {
404 $urlParams = 'force=1';
405 $urlString = 'civicrm/contact/search/custom';
406 if ($this->_action
== CRM_Core_Action
::UPDATE
) {
408 $urlParams .= '&context=fulltext&action=view';
409 $urlString = 'civicrm/contact/view/activity';
412 $urlParams .= "$keyName=$qfKey";
414 $this->assign('searchKey', $qfKey);
416 elseif (in_array($this->_context
, [
423 $urlParams = 'reset=1';
424 $urlString = 'civicrm/dashboard';
426 elseif ($this->_context
== 'search') {
427 $urlParams = 'force=1';
429 $urlParams .= "&qfKey=$qfKey";
431 $path = CRM_Utils_System
::currentPath();
432 if ($this->_compContext
== 'advanced') {
433 $urlString = 'civicrm/contact/search/advanced';
435 elseif ($path == 'civicrm/group/search'
436 ||
$path == 'civicrm/contact/search'
437 ||
$path == 'civicrm/contact/search/advanced'
438 ||
$path == 'civicrm/contact/search/custom'
439 ||
$path == 'civicrm/group/search'
440 ||
$path == 'civicrm/contact/search/builder'
445 $urlString = 'civicrm/activity/search';
447 $this->assign('searchKey', $qfKey);
449 elseif ($this->_context
!= 'caseActivity') {
450 $urlParams = "action=browse&reset=1&cid={$this->_currentlyViewedContactId}&selectedChild=activity";
451 $urlString = 'civicrm/contact/view';
455 $session->pushUserContext(CRM_Utils_System
::url($urlString, $urlParams));
458 // hack to retrieve activity type id from post variables
459 if (!$this->_activityTypeId
) {
460 $this->_activityTypeId
= CRM_Utils_Array
::value('activity_type_id', $_POST);
463 // when custom data is included in this page
464 $this->assign('cid', $this->_currentlyViewedContactId
);
465 if (!empty($_POST['hidden_custom'])) {
466 // We need to set it in the session for the code below to work.
468 // Need to assign custom data subtype to the template.
469 $this->set('type', 'Activity');
470 $this->set('subType', $this->_activityTypeId
);
471 $this->set('entityId', $this->_activityId
);
472 CRM_Custom_Form_CustomData
::preProcess($this, NULL, $this->_activityTypeId
, 1, 'Activity', $this->_activityId
);
473 CRM_Custom_Form_CustomData
::buildQuickForm($this);
474 CRM_Custom_Form_CustomData
::setDefaultValues($this);
477 // add attachments part
478 CRM_Core_BAO_File
::buildAttachment($this, 'civicrm_activity', $this->_activityId
, NULL, TRUE);
480 // figure out the file name for activity type, if any
481 if ($this->_activityTypeId
&&
482 $this->_activityTypeFile
= CRM_Activity_BAO_Activity
::getFileForActivityTypeId($this->_activityTypeId
, $this->_crmDir
)
484 $this->assign('activityTypeFile', $this->_activityTypeFile
);
485 $this->assign('crmDir', $this->_crmDir
);
490 if ($this->_activityTypeFile
) {
491 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
492 $className::preProcess($this);
495 $this->_values
= $this->get('values');
496 if (!is_array($this->_values
)) {
498 if (isset($this->_activityId
) && $this->_activityId
) {
499 $params = ['id' => $this->_activityId
];
500 CRM_Activity_BAO_Activity
::retrieve($params, $this->_values
);
503 $this->set('values', $this->_values
);
506 if ($this->_action
& CRM_Core_Action
::UPDATE
) {
507 // We filter out alternatives, in case this is a stored e-mail, before sending to front-end
508 if (isset($this->_values
['details'])) {
509 $this->_values
['details'] = CRM_Utils_String
::stripAlternatives($this->_values
['details']) ?
: '';
512 if ($this->_activityTypeName
=== 'Inbound Email' &&
513 !CRM_Core_Permission
::check('edit inbound email basic information and content')
515 $this->_fields
['details']['type'] = 'static';
518 CRM_Core_Form_RecurringEntity
::preProcess('civicrm_activity');
521 if ($this->_action
& CRM_Core_Action
::VIEW
) {
522 $url = CRM_Utils_System
::url(implode("/", $this->urlPath
), "reset=1&id={$this->_activityId}&action=view&cid={$this->_values['source_contact_id']}");
523 CRM_Utils_Recent
::add(CRM_Utils_Array
::value('subject', $this->_values
, ts('(no subject)')),
525 $this->_values
['id'],
527 $this->_values
['source_contact_id'],
528 $this->_values
['source_contact']
534 * Set default values for the form.
536 * For edit/view mode the default values are retrieved from the database.
540 public function setDefaultValues() {
542 $defaults = $this->_values + CRM_Core_Form_RecurringEntity
::setDefaultValues();
543 // if we're editing...
544 if (isset($this->_activityId
)) {
546 if ($this->_context
!= 'standalone') {
547 $this->assign('target_contact_value',
548 CRM_Utils_Array
::value('target_contact_value', $defaults)
550 $this->assign('assignee_contact_value',
551 CRM_Utils_Array
::value('assignee_contact_value', $defaults)
555 // Fixme: why are we getting the wrong keys from upstream?
556 $defaults['target_contact_id'] = CRM_Utils_Array
::value('target_contact', $defaults);
557 $defaults['assignee_contact_id'] = CRM_Utils_Array
::value('assignee_contact', $defaults);
559 // set default tags if exists
560 $defaults['tag'] = implode(',', CRM_Core_BAO_EntityTag
::getTag($this->_activityId
, 'civicrm_activity'));
563 // if it's a new activity, we need to set default values for associated contact fields
564 $this->_sourceContactId
= $this->_currentUserId
;
565 $this->_targetContactId
= $this->_currentlyViewedContactId
;
567 $defaults['source_contact_id'] = $this->_sourceContactId
;
568 $defaults['target_contact_id'] = $this->_targetContactId
;
571 if (empty($defaults['activity_date_time'])) {
572 $defaults['activity_date_time'] = date('Y-m-d H:i:s');
575 if ($this->_activityTypeId
) {
576 $defaults['activity_type_id'] = $this->_activityTypeId
;
579 if (!$this->_single
&& !empty($this->_contactIds
)) {
580 $defaults['target_contact_id'] = $this->_contactIds
;
583 // CRM-15472 - 50 is around the practical limit of how many items a select2 entityRef can handle
584 if ($this->_action
== CRM_Core_Action
::UPDATE
&& !empty($defaults['target_contact_id'])) {
585 $count = count(is_array($defaults['target_contact_id']) ?
$defaults['target_contact_id'] : explode(',', $defaults['target_contact_id']));
587 $this->freeze(['target_contact_id']);
591 if ($this->_action
& (CRM_Core_Action
::DELETE | CRM_Core_Action
::RENEW
)) {
592 $this->assign('delName', CRM_Utils_Array
::value('subject', $defaults));
595 if ($this->_activityTypeFile
) {
596 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
597 $defaults +
= $className::setDefaultValues($this);
599 if (empty($defaults['priority_id'])) {
600 $priority = CRM_Core_PseudoConstant
::get('CRM_Activity_DAO_Activity', 'priority_id');
601 $defaults['priority_id'] = array_search('Normal', $priority);
603 if (empty($defaults['status_id'])) {
604 $defaults['status_id'] = CRM_Core_OptionGroup
::getDefaultValue('activity_status');
612 * @throws \CRM_Core_Exception
613 * @throws \CiviCRM_API3_Exception
615 public function buildQuickForm() {
616 if ($this->_action
& (CRM_Core_Action
::DELETE | CRM_Core_Action
::RENEW
)) {
617 //enable form element (ActivityLinks sets this true)
618 $this->assign('suppressForm', FALSE);
620 $button = ts('Delete');
621 if ($this->_action
& CRM_Core_Action
::RENEW
) {
622 $button = ts('Restore');
628 'spacing' => ' ',
633 'name' => ts('Cancel'),
639 // Build other activity links.
640 CRM_Activity_Form_ActivityLinks
::commonBuildQuickForm($this);
642 // Enable form element (ActivityLinks sets this true).
643 $this->assign('suppressForm', FALSE);
645 $element = &$this->add('select', 'activity_type_id', ts('Activity Type'),
646 ['' => '- ' . ts('select') . ' -'] +
$this->_fields
['followup_activity_type_id']['attributes'],
648 'onchange' => "CRM.buildCustomData( 'Activity', this.value, false, false, false, false, false, false, {$this->_currentlyViewedContactId});",
649 'class' => 'crm-select2 required',
653 // Freeze for update mode.
654 if ($this->_action
& CRM_Core_Action
::UPDATE
) {
658 // Call to RecurringEntity buildQuickForm for add/update mode.
659 if ($this->_action
& (CRM_Core_Action
::UPDATE | CRM_Core_Action
::ADD
)) {
660 CRM_Core_Form_RecurringEntity
::buildQuickForm($this);
663 foreach ($this->_fields
as $field => $values) {
664 if (!empty($this->_fields
[$field])) {
665 $attribute = CRM_Utils_Array
::value('attributes', $values);
666 $required = !empty($values['required']);
668 if ($values['type'] == 'select' && empty($attribute)) {
669 $this->addSelect($field, ['entity' => 'activity'], $required);
671 elseif ($values['type'] == 'entityRef') {
672 $this->addEntityRef($field, $values['label'], $attribute, $required);
675 $this->add($values['type'], $field, $values['label'], $attribute, $required, CRM_Utils_Array
::value('extra', $values));
680 // CRM-7362 --add campaigns.
681 CRM_Campaign_BAO_Campaign
::addCampaign($this, CRM_Utils_Array
::value('campaign_id', $this->_values
));
683 // Add engagement level CRM-7775
684 $buildEngagementLevel = FALSE;
685 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable() &&
686 CRM_Campaign_BAO_Campaign
::accessCampaign()
688 $buildEngagementLevel = TRUE;
689 $this->addSelect('engagement_level', ['entity' => 'activity']);
690 $this->addRule('engagement_level',
691 ts('Please enter the engagement index as a number (integers only).'),
695 $this->assign('buildEngagementLevel', $buildEngagementLevel);
697 // check for survey activity
698 $this->_isSurveyActivity
= FALSE;
700 if ($this->_activityId
&& CRM_Campaign_BAO_Campaign
::isCampaignEnable() &&
701 CRM_Campaign_BAO_Campaign
::accessCampaign()
704 $this->_isSurveyActivity
= CRM_Campaign_BAO_Survey
::isSurveyActivity($this->_activityId
);
705 if ($this->_isSurveyActivity
) {
706 $surveyId = CRM_Core_DAO
::getFieldValue('CRM_Activity_DAO_Activity',
710 $responseOptions = CRM_Campaign_BAO_Survey
::getResponsesOptions($surveyId);
711 if ($responseOptions) {
712 $this->add('select', 'result', ts('Result'),
713 ['' => ts('- select -')] +
array_combine($responseOptions, $responseOptions)
718 $surveyTitle = CRM_Core_DAO
::getFieldValue('CRM_Campaign_DAO_Survey', $surveyId, 'title');
720 $this->assign('surveyTitle', $surveyTitle);
723 $this->assign('surveyActivity', $this->_isSurveyActivity
);
725 // Add the "Activity Separation" field
726 $actionIsAdd = ($this->_action
!= CRM_Core_Action
::UPDATE
&& $this->_action
!= CRM_Core_Action
::VIEW
);
727 $separationIsPossible = $this->supportsActivitySeparation
;
728 if ($actionIsAdd && $separationIsPossible) {
731 ts('Activity Separation'),
733 'separate' => ts('Create separate activities for each contact'),
734 'combined' => ts('Create one activity with all contacts together'),
739 $this->addRule('duration',
740 ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger'
743 // Add followup date.
744 $this->add('datepicker', 'followup_date', ts('in'));
746 // Only admins and case-workers can change the activity source
747 if (!CRM_Core_Permission
::check('administer CiviCRM') && $this->_context
!= 'caseActivity') {
748 $this->getElement('source_contact_id')->freeze();
751 //need to assign custom data type and subtype to the template
752 $this->assign('customDataType', 'Activity');
753 $this->assign('customDataSubType', $this->_activityTypeId
);
754 $this->assign('entityID', $this->_activityId
);
756 $tags = CRM_Core_BAO_Tag
::getColorTags('civicrm_activity');
759 $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, [
761 'placeholder' => ts('- select -'),
766 // we need to hide activity tagset for special activities
767 $specialActivities = ['Open Case'];
769 if (!in_array($this->_activityTypeName
, $specialActivities)) {
771 $parentNames = CRM_Core_BAO_Tag
::getTagSet('civicrm_activity');
772 CRM_Core_Form_Tag
::buildQuickForm($this, $parentNames, 'civicrm_activity', $this->_activityId
);
775 // if we're viewing, we're assigning different buttons than for adding/editing
776 if ($this->_action
& CRM_Core_Action
::VIEW
) {
777 if (isset($this->_groupTree
)) {
778 CRM_Core_BAO_CustomGroup
::buildCustomDataView($this, $this->_groupTree
, FALSE, NULL, NULL, NULL, $this->_activityId
);
780 // form should be frozen for view mode
786 'name' => ts('Done'),
794 'name' => ts('Save'),
799 'name' => ts('Cancel'),
804 if ($this->_activityTypeFile
) {
805 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
807 $className::buildQuickForm($this);
808 $this->addFormRule([$className, 'formRule'], $this);
811 $this->addFormRule(['CRM_Activity_Form_Activity', 'formRule'], $this);
813 $doNotNotifyAssigneeFor = (array) Civi
::settings()
814 ->get('do_not_notify_assignees_for');
815 if (($this->_activityTypeId
&& in_array($this->_activityTypeId
, $doNotNotifyAssigneeFor)) ||
!Civi
::settings()
816 ->get('activity_assignee_notification')) {
817 $this->assign('activityAssigneeNotification', FALSE);
820 $this->assign('activityAssigneeNotification', TRUE);
822 $this->assign('doNotNotifyAssigneeFor', $doNotNotifyAssigneeFor);
828 * @param array $fields
829 * The input form values.
830 * @param array $files
831 * The uploaded files if any.
835 * true if no errors, else array of errors
837 public static function formRule($fields, $files, $self) {
838 // skip form rule if deleting
839 if (CRM_Utils_Array
::value('_qf_Activity_next_', $fields) == 'Delete') {
843 if ((array_key_exists('activity_type_id', $fields) ||
!$self->_single
) && empty($fields['activity_type_id'])) {
844 $errors['activity_type_id'] = ts('Activity Type is a required field');
847 $activity_type_id = CRM_Utils_Array
::value('activity_type_id', $fields);
848 $activity_status_id = CRM_Utils_Array
::value('status_id', $fields);
849 $scheduled_status_id = CRM_Core_PseudoConstant
::getKey('CRM_Activity_BAO_Activity', 'status_id', 'Scheduled');
851 if ($activity_type_id && $activity_status_id == $scheduled_status_id) {
852 if ($activity_type_id == CRM_Core_PseudoConstant
::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Email')) {
853 $errors['status_id'] = ts('You cannot record scheduled email activity.');
855 elseif ($activity_type_id == CRM_Core_PseudoConstant
::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'SMS')) {
856 $errors['status_id'] = ts('You cannot record scheduled SMS activity');
860 if (!empty($fields['followup_activity_type_id']) && empty($fields['followup_date'])) {
861 $errors['followup_date'] = ts('Followup date is a required field.');
863 // Activity type is mandatory if subject or follow-up date is specified for an Follow-up activity, CRM-4515.
864 if ((!empty($fields['followup_activity_subject']) ||
!empty($fields['followup_date'])) && empty($fields['followup_activity_type_id'])) {
865 $errors['followup_activity_subject'] = ts('Follow-up Activity type is a required field.');
868 // Check that a value has been set for the "activity separation" field if needed
869 $separationIsPossible = $self->supportsActivitySeparation
;
870 $actionIsAdd = $self->_action
== CRM_Core_Action
::ADD
;
871 $hasMultipleTargetContacts = !empty($fields['target_contact_id']) && strpos($fields['target_contact_id'], ',') !== FALSE;
872 $separationFieldIsEmpty = empty($fields['separation']);
873 if ($separationIsPossible && $actionIsAdd && $hasMultipleTargetContacts && $separationFieldIsEmpty) {
874 $errors['separation'] = ts('Activity Separation is a required field.');
881 * Process the form submission.
884 * @param array $params
887 * @throws \CiviCRM_API3_Exception
889 public function postProcess($params = NULL) {
890 if ($this->_action
& CRM_Core_Action
::DELETE
) {
891 // Look up any repeat activities to be deleted.
892 $activityIds = array_column(CRM_Core_BAO_RecurringEntity
::getEntitiesFor($this->_activityId
, 'civicrm_activity', TRUE, NULL), 'id');
894 // There are no repeat activities to delete - just this one.
895 $activityIds = [$this->_activityId
];
898 // Delete each activity.
899 foreach ($activityIds as $activityId) {
900 $deleteParams = ['id' => $activityId];
901 $moveToTrash = CRM_Case_BAO_Case
::isCaseActivity($activityId);
902 CRM_Activity_BAO_Activity
::deleteActivity($deleteParams, $moveToTrash);
904 // delete tags for the entity
906 'entity_table' => 'civicrm_activity',
907 'entity_id' => $activityId,
910 CRM_Core_BAO_EntityTag
::del($tagParams);
913 CRM_Core_Session
::setStatus(
914 ts("Selected Activity has been deleted successfully.", ['plural' => '%count Activities have been deleted successfully.', 'count' => count($activityIds)]),
915 ts('Record Deleted', ['plural' => 'Records Deleted', 'count' => count($activityIds)]), 'success'
921 // store the submitted values in an array
923 $params = $this->controller
->exportValues($this->_name
);
926 // Set activity type id.
927 if (empty($params['activity_type_id'])) {
928 $params['activity_type_id'] = $this->_activityTypeId
;
931 if (!empty($params['hidden_custom']) &&
932 !isset($params['custom'])
934 $customFields = CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
935 $this->_activityTypeId
937 $customFields = CRM_Utils_Array
::crmArrayMerge($customFields,
938 CRM_Core_BAO_CustomField
::getFields('Activity', FALSE, FALSE,
942 $params['custom'] = CRM_Core_BAO_CustomField
::postProcess($params,
948 // format params as arrays
949 foreach (['target', 'assignee', 'followup_assignee'] as $name) {
950 if (!empty($params["{$name}_contact_id"])) {
951 $params["{$name}_contact_id"] = explode(',', $params["{$name}_contact_id"]);
954 $params["{$name}_contact_id"] = [];
958 // get ids for associated contacts
959 if (!$params['source_contact_id']) {
960 $params['source_contact_id'] = $this->_currentUserId
;
963 if (isset($this->_activityId
)) {
964 $params['id'] = $this->_activityId
;
967 // add attachments as needed
968 CRM_Core_BAO_File
::formatAttachment($params,
974 $params['is_multi_activity'] = CRM_Utils_Array
::value('separation', $params) == 'separate';
977 if (!empty($params['is_multi_activity']) &&
978 !CRM_Utils_Array
::crmIsEmptyArray($params['target_contact_id'])
980 $targetContacts = $params['target_contact_id'];
981 foreach ($targetContacts as $targetContactId) {
982 $params['target_contact_id'] = [$targetContactId];
984 $activity[] = $this->processActivity($params);
989 $activity = $this->processActivity($params);
992 // Redirect to contact page or activity view in standalone mode
993 if ($this->_context
== 'standalone') {
994 if (count($params['target_contact_id']) == 1) {
995 $url = CRM_Utils_System
::url('civicrm/contact/view', ['cid' => CRM_Utils_Array
::first($params['target_contact_id']), 'selectedChild' => 'activity']);
998 $url = CRM_Utils_System
::url('civicrm/activity', ['action' => 'view', 'reset' => 1, 'id' => $this->_activityId
]);
1000 CRM_Core_Session
::singleton()->pushUserContext($url);
1003 $activityIds = empty($this->_activityIds
) ?
[$this->_activityId
] : $this->_activityIds
;
1004 foreach ($activityIds as $activityId) {
1005 // set params for repeat configuration in create mode
1006 $params['entity_id'] = $activityId;
1007 $params['entity_table'] = 'civicrm_activity';
1008 if (!empty($params['entity_id']) && !empty($params['entity_table'])) {
1009 $checkParentExistsForThisId = CRM_Core_BAO_RecurringEntity
::getParentFor($params['entity_id'], $params['entity_table']);
1010 if ($checkParentExistsForThisId) {
1011 $params['parent_entity_id'] = $checkParentExistsForThisId;
1012 $scheduleReminderDetails = CRM_Core_BAO_RecurringEntity
::getReminderDetailsByEntityId($checkParentExistsForThisId, $params['entity_table']);
1015 $params['parent_entity_id'] = $params['entity_id'];
1016 $scheduleReminderDetails = CRM_Core_BAO_RecurringEntity
::getReminderDetailsByEntityId($params['entity_id'], $params['entity_table']);
1018 if (property_exists($scheduleReminderDetails, 'id')) {
1019 $params['schedule_reminder_id'] = $scheduleReminderDetails->id
;
1022 $params['dateColumns'] = ['activity_date_time'];
1024 // Set default repetition start if it was not provided.
1025 if (empty($params['repetition_start_date'])) {
1026 $params['repetition_start_date'] = $params['activity_date_time'];
1029 // unset activity id
1030 unset($params['id']);
1033 'table' => 'civicrm_activity_contact',
1035 'activity_id' => $activityId,
1037 'linkedColumns' => ['activity_id'],
1038 'isRecurringEntityRecord' => FALSE,
1041 CRM_Core_Form_RecurringEntity
::postProcess($params, 'civicrm_activity', $linkedEntities);
1044 return ['activity' => $activity];
1048 * Process activity creation.
1050 * @param array $params
1051 * Associated array of submitted values.
1053 * @return self|null|object
1055 protected function processActivity(&$params) {
1056 $activityAssigned = [];
1057 $activityContacts = CRM_Activity_BAO_ActivityContact
::buildOptions('record_type_id', 'validate');
1058 $assigneeID = CRM_Utils_Array
::key('Activity Assignees', $activityContacts);
1059 // format assignee params
1060 if (!CRM_Utils_Array
::crmIsEmptyArray($params['assignee_contact_id'])) {
1061 //skip those assignee contacts which are already assigned
1062 //while sending a copy.CRM-4509.
1063 $activityAssigned = array_flip($params['assignee_contact_id']);
1064 if ($this->_activityId
) {
1065 $assigneeContacts = CRM_Activity_BAO_ActivityContact
::getNames($this->_activityId
, $assigneeID);
1066 $activityAssigned = array_diff_key($activityAssigned, $assigneeContacts);
1070 // call begin post process. Idea is to let injecting file do
1071 // any processing before the activity is added/updated.
1072 $this->beginPostProcess($params);
1074 $activity = CRM_Activity_BAO_Activity
::create($params);
1076 // add tags if exists
1078 if (!empty($params['tag'])) {
1079 if (!is_array($params['tag'])) {
1080 $params['tag'] = explode(',', $params['tag']);
1083 $tagParams = array_fill_keys($params['tag'], 1);
1086 // Save static tags.
1087 CRM_Core_BAO_EntityTag
::create($tagParams, 'civicrm_activity', $activity->id
);
1090 if (isset($params['activity_taglist']) && !empty($params['activity_taglist'])) {
1091 CRM_Core_Form_Tag
::postProcess($params['activity_taglist'], $activity->id
, 'civicrm_activity', $this);
1094 // call end post process. Idea is to let injecting file do any
1095 // processing needed, after the activity has been added/updated.
1096 $this->endPostProcess($params, $activity);
1099 if (!empty($params['is_multi_activity'])) {
1100 $this->_activityIds
[] = $activity->id
;
1103 $this->_activityId
= $activity->id
;
1106 // create follow up activity if needed
1107 $followupStatus = '';
1108 $followupActivity = NULL;
1109 if (!empty($params['followup_activity_type_id'])) {
1110 $followupActivity = CRM_Activity_BAO_Activity
::createFollowupActivity($activity->id
, $params);
1111 $followupStatus = ts('A followup activity has been scheduled.');
1114 // send copy to assignee contacts.CRM-4509
1117 if (Civi
::settings()->get('activity_assignee_notification')
1118 && !in_array($activity->activity_type_id
, Civi
::settings()
1119 ->get('do_not_notify_assignees_for'))) {
1120 $activityIDs = [$activity->id
];
1121 if ($followupActivity) {
1122 $activityIDs = array_merge($activityIDs, [$followupActivity->id
]);
1124 $assigneeContacts = CRM_Activity_BAO_ActivityAssignment
::getAssigneeNames($activityIDs, TRUE, FALSE);
1126 if (!CRM_Utils_Array
::crmIsEmptyArray($params['assignee_contact_id'])) {
1127 $mailToContacts = [];
1129 // Build an associative array with unique email addresses.
1130 foreach ($activityAssigned as $id => $dnc) {
1131 if (isset($id) && array_key_exists($id, $assigneeContacts)) {
1132 $mailToContacts[$assigneeContacts[$id]['email']] = $assigneeContacts[$id];
1136 $sent = CRM_Activity_BAO_Activity
::sendToAssignee($activity, $mailToContacts);
1138 $mailStatus .= ts("A copy of the activity has also been sent to assignee contacts(s).");
1142 // Also send email to follow-up activity assignees if set
1143 if ($followupActivity) {
1144 $mailToFollowupContacts = [];
1145 foreach ($assigneeContacts as $values) {
1146 if ($values['activity_id'] == $followupActivity->id
) {
1147 $mailToFollowupContacts[$values['email']] = $values;
1151 $sentFollowup = CRM_Activity_BAO_Activity
::sendToAssignee($followupActivity, $mailToFollowupContacts);
1152 if ($sentFollowup) {
1153 $mailStatus .= '<br />' . ts("A copy of the follow-up activity has also been sent to follow-up assignee contacts(s).");
1158 // set status message
1160 if (!empty($params['subject'])) {
1161 $subject = "'" . $params['subject'] . "'";
1164 CRM_Core_Session
::setStatus(ts('Activity %1 has been saved. %2 %3',
1167 2 => $followupStatus,
1170 ), ts('Saved'), 'success');
1176 * Shorthand for getting id by display name (makes code more readable)
1177 * @param $displayName
1178 * @return null|string
1180 protected function _getIdByDisplayName($displayName) {
1181 return CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
1189 * Shorthand for getting display name by id (makes code more readable)
1191 * @return null|string
1193 protected function _getDisplayNameById($id) {
1194 return CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
1202 * Let injecting activity type file do any processing.
1203 * needed, before the activity is added/updated
1205 * @param array $params
1207 public function beginPostProcess(&$params) {
1208 if ($this->_activityTypeFile
) {
1209 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
1210 $className::beginPostProcess($this, $params);
1215 * Let injecting activity type file do any processing
1216 * needed, after the activity has been added/updated
1218 * @param array $params
1221 public function endPostProcess(&$params, &$activity) {
1222 if ($this->_activityTypeFile
) {
1223 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
1224 $className::endPostProcess($this, $params, $activity);
1229 * For the moment keeping this the same as the original pulled from preProcess(). Also note the "s" at the end of the function name - planning to change that but in baby steps.
1233 public function getActivityTypeDisplayLabels() {
1234 return CRM_Core_OptionGroup
::values('activity_type', FALSE, FALSE, FALSE, 'AND v.value = ' . $this->_activityTypeId
, 'label');
1238 * For the moment this is just pulled from preProcess
1240 public function assignActivityType() {
1241 if ($this->_activityTypeId
) {
1242 $activityTypeDisplayLabels = $this->getActivityTypeDisplayLabels();
1243 if ($activityTypeDisplayLabels[$this->_activityTypeId
]) {
1244 $this->_activityTypeName
= $activityTypeDisplayLabels[$this->_activityTypeId
];
1246 // At the moment this is duplicating other code in this section, but refactoring in small steps.
1247 $activityTypeObj = new CRM_Activity_BAO_ActivityType($this->_activityTypeId
);
1248 $this->assign('activityTypeNameAndLabel', $activityTypeObj->getActivityType());
1251 if (isset($activityTypeDisplayLabels)) {
1252 // FIXME - it's not clear why the if line just above is needed here and why we can't just set this once above and re-use. What is interesting, but can't possibly be the reason, is that the first if block will fail if the label is the string '0', whereas this one won't. But who would have an activity type called '0'?
1253 $activityTypeDisplayLabel = CRM_Utils_Array
::value($this->_activityTypeId
, $activityTypeDisplayLabels);
1255 if ($this->_currentlyViewedContactId
) {
1256 $displayName = CRM_Contact_BAO_Contact
::displayName($this->_currentlyViewedContactId
);
1257 // Check if this is default domain contact CRM-10482.
1258 if (CRM_Contact_BAO_Contact
::checkDomainContact($this->_currentlyViewedContactId
)) {
1259 $displayName .= ' (' . ts('default organization') . ')';
1261 CRM_Utils_System
::setTitle($displayName . ' - ' . $activityTypeDisplayLabel);
1264 CRM_Utils_System
::setTitle(ts('%1 Activity', [1 => $activityTypeDisplayLabel]));