Merge pull request #15755 from seamuslee001/copywrite_date_update
[civicrm-core.git] / CRM / Activity / Form / Activity.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2020 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
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. |
13 | |
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. |
18 | |
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 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2020
32 */
33
34 /**
35 * This class generates form components for Activity.
36 */
37 class CRM_Activity_Form_Activity extends CRM_Contact_Form_Task {
38
39 /**
40 * The id of the object being edited / created
41 *
42 * @var int
43 */
44 public $_activityId;
45
46 /**
47 * Store activity ids when multiple activities are created.
48 *
49 * @var int
50 */
51 public $_activityIds = [];
52
53 /**
54 * The id of activity type.
55 *
56 * @var int
57 */
58 public $_activityTypeId;
59
60 /**
61 * The name of activity type.
62 *
63 * @var string
64 */
65 public $_activityTypeName;
66
67 /**
68 * The id of currently viewed contact.
69 *
70 * @var int
71 */
72 public $_currentlyViewedContactId;
73
74 /**
75 * The id of source contact and target contact.
76 *
77 * @var int
78 */
79 protected $_sourceContactId;
80 protected $_targetContactId;
81 protected $_asigneeContactId;
82
83 protected $_single;
84
85 public $_context;
86 public $_compContext;
87 public $_action;
88 public $_activityTypeFile;
89
90 /**
91 * The id of the logged in user, used when add / edit
92 *
93 * @var int
94 */
95 public $_currentUserId;
96
97 /**
98 * The array of form field attributes.
99 *
100 * @var array
101 */
102 public $_fields;
103
104 /**
105 * The the directory inside CRM, to include activity type file from
106 *
107 * @var string
108 */
109 protected $_crmDir = 'Activity';
110
111 /**
112 * Survey activity.
113 *
114 * @var bool
115 */
116 protected $_isSurveyActivity;
117
118 protected $_values = [];
119
120 protected $unsavedWarn = TRUE;
121
122 /**
123 *
124 * Is it possible to create separate activities with this form?
125 *
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"
128 * field).
129 *
130 * When FALSE, the form will create one activity with all contacts together
131 * and won't ask the user anything.
132 *
133 * Note: This is a class property so that child classes can turn off this
134 * behavior (e.g. in CRM_Case_Form_Activity)
135 *
136 * @var bool
137 *
138 */
139 protected $supportsActivitySeparation = TRUE;
140
141 public $submitOnce = TRUE;
142
143 /**
144 * Explicitly declare the entity api name.
145 *
146 * @return string
147 */
148 public function getDefaultEntity() {
149 return 'Activity';
150 }
151
152 /**
153 * The _fields var can be used by sub class to set/unset/edit the
154 * form fields based on their requirement
155 */
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);
160
161 $this->_fields = [
162 'subject' => [
163 'type' => 'text',
164 'label' => ts('Subject'),
165 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'activity_subject'),
166 ],
167 'duration' => [
168 'type' => 'number',
169 'label' => ts('Duration'),
170 'attributes' => ['class' => 'four', 'min' => 1],
171 'required' => FALSE,
172 ],
173 'location' => [
174 'type' => 'text',
175 'label' => ts('Location'),
176 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'location'),
177 'required' => FALSE,
178 ],
179 'details' => [
180 'type' => 'wysiwyg',
181 'label' => ts('Details'),
182 'attributes' => ['class' => 'huge'],
183 'required' => FALSE,
184 ],
185 'status_id' => [
186 'type' => 'select',
187 'required' => TRUE,
188 ],
189 'priority_id' => [
190 'type' => 'select',
191 'required' => TRUE,
192 ],
193 'source_contact_id' => [
194 'type' => 'entityRef',
195 'label' => ts('Added By'),
196 'required' => FALSE,
197 ],
198 'target_contact_id' => [
199 'type' => 'entityRef',
200 'label' => ts('With Contact'),
201 'attributes' => ['multiple' => TRUE, 'create' => TRUE],
202 ],
203 'assignee_contact_id' => [
204 'type' => 'entityRef',
205 'label' => ts('Assigned to'),
206 'attributes' => [
207 'multiple' => TRUE,
208 'create' => TRUE,
209 'api' => ['params' => ['is_deceased' => 0]],
210 ],
211 ],
212 'activity_date_time' => [
213 'type' => 'datepicker',
214 'label' => ts('Date'),
215 'required' => TRUE,
216 ],
217 'followup_assignee_contact_id' => [
218 'type' => 'entityRef',
219 'label' => ts('Assigned to'),
220 'attributes' => [
221 'multiple' => TRUE,
222 'create' => TRUE,
223 'api' => ['params' => ['is_deceased' => 0]],
224 ],
225 ],
226 'followup_activity_type_id' => [
227 'type' => 'select',
228 'label' => ts('Followup Activity'),
229 'attributes' => ['' => '- ' . ts('select activity') . ' -'] + $activityTypes,
230 'extra' => ['class' => 'crm-select2'],
231 ],
232 // Add optional 'Subject' field for the Follow-up Activiity, CRM-4491
233 'followup_activity_subject' => [
234 'type' => 'text',
235 'label' => ts('Subject'),
236 'attributes' => CRM_Core_DAO::getAttribute('CRM_Activity_DAO_Activity', 'subject'),
237 ],
238 ];
239 }
240
241 /**
242 * Build the form object.
243 */
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);
250 }
251
252 $session = CRM_Core_Session::singleton();
253 $this->_currentUserId = CRM_Core_Session::getLoggedInContactID();
254
255 $this->_currentlyViewedContactId = $this->get('contactId');
256 if (!$this->_currentlyViewedContactId) {
257 $this->_currentlyViewedContactId = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
258 }
259 $this->assign('contactId', $this->_currentlyViewedContactId);
260
261 // Give the context.
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';
266 }
267 elseif (!in_array($this->_context, ['dashlet', 'case', 'dashletFullscreen'])
268 && $this->_currentlyViewedContactId
269 ) {
270 $this->_context = 'activity';
271 }
272 $this->_compContext = CRM_Utils_Request::retrieve('compContext', 'String', $this);
273 }
274
275 $this->assign('context', $this->_context);
276
277 $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this);
278
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.'));
282 }
283 }
284
285 // CRM-6957
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.
288
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'
293 ) {
294 $this->_activityId = CRM_Utils_Request::retrieve('id', 'Positive', $this);
295 }
296
297 $this->_activityTypeId = CRM_Utils_Request::retrieve('atype', 'Positive', $this);
298 $this->assign('atype', $this->_activityTypeId);
299
300 $this->assign('activityId', $this->_activityId);
301
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,
307 ]) &&
308 !CRM_Activity_BAO_Activity::checkPermission($this->_activityId, $this->_action)
309 ) {
310 CRM_Core_Error::statusBounce(ts('You do not have permission to access this page.'));
311 }
312 if (($this->_action & CRM_Core_Action::VIEW) &&
313 CRM_Activity_BAO_Activity::checkPermission($this->_activityId, CRM_Core_Action::UPDATE)
314 ) {
315 $this->assign('permission', 'edit');
316 $this->assign('allow_edit_inbound_emails', CRM_Activity_BAO_Activity::checkEditInboundEmailsPermissions());
317 }
318
319 if (!$this->_activityTypeId && $this->_activityId) {
320 $this->_activityTypeId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity',
321 $this->_activityId,
322 'activity_type_id'
323 );
324 }
325
326 $this->assignActivityType();
327
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
333 ) {
334 $this->_single = TRUE;
335 $this->assign('urlPath', 'civicrm/activity');
336 }
337 else {
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) {
345 case 'basic':
346 $searchType = $searchPath;
347 $this->_action = CRM_Core_Action::BASIC;
348 break;
349
350 case 'advanced':
351 $searchType = $searchPath;
352 $this->_action = CRM_Core_Action::ADVANCED;
353 break;
354
355 case 'builder':
356 $searchType = $searchPath;
357 $this->_action = CRM_Core_Action::PROFILE;
358 break;
359
360 case 'custom':
361 $this->_action = CRM_Core_Action::COPY;
362 $searchType = $searchPath;
363 break;
364 }
365
366 parent::preProcess();
367 $this->_single = FALSE;
368
369 $this->assign('urlPath', "civicrm/contact/search/$searchType");
370 $this->assign('urlPathVar', "_qf_Activity_display=true&qfKey={$this->controller->_key}");
371 }
372
373 $this->assign('single', $this->_single);
374 $this->assign('action', $this->_action);
375
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
380 );
381 }
382
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);
388 }
389
390 // set user context
391 $urlParams = $urlString = NULL;
392 $qfKey = CRM_Utils_Request::retrieve('key', 'String', $this);
393 if (!$qfKey) {
394 $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
395 }
396
397 // Validate the qfKey.
398 if (!CRM_Utils_Rule::qfKey($qfKey)) {
399 $qfKey = NULL;
400 }
401
402 if ($this->_context == 'fulltext') {
403 $keyName = '&qfKey';
404 $urlParams = 'force=1';
405 $urlString = 'civicrm/contact/search/custom';
406 if ($this->_action == CRM_Core_Action::UPDATE) {
407 $keyName = '&key';
408 $urlParams .= '&context=fulltext&action=view';
409 $urlString = 'civicrm/contact/view/activity';
410 }
411 if ($qfKey) {
412 $urlParams .= "$keyName=$qfKey";
413 }
414 $this->assign('searchKey', $qfKey);
415 }
416 elseif (in_array($this->_context, [
417 'standalone',
418 'home',
419 'dashlet',
420 'dashletFullscreen',
421 ])
422 ) {
423 $urlParams = 'reset=1';
424 $urlString = 'civicrm/dashboard';
425 }
426 elseif ($this->_context == 'search') {
427 $urlParams = 'force=1';
428 if ($qfKey) {
429 $urlParams .= "&qfKey=$qfKey";
430 }
431 $path = CRM_Utils_System::currentPath();
432 if ($this->_compContext == 'advanced') {
433 $urlString = 'civicrm/contact/search/advanced';
434 }
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'
441 ) {
442 $urlString = $path;
443 }
444 else {
445 $urlString = 'civicrm/activity/search';
446 }
447 $this->assign('searchKey', $qfKey);
448 }
449 elseif ($this->_context != 'caseActivity') {
450 $urlParams = "action=browse&reset=1&cid={$this->_currentlyViewedContactId}&selectedChild=activity";
451 $urlString = 'civicrm/contact/view';
452 }
453
454 if ($urlString) {
455 $session->pushUserContext(CRM_Utils_System::url($urlString, $urlParams));
456 }
457
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);
461 }
462
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.
467 // CRM-3014
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);
475 }
476
477 // add attachments part
478 CRM_Core_BAO_File::buildAttachment($this, 'civicrm_activity', $this->_activityId, NULL, TRUE);
479
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)
483 ) {
484 $this->assign('activityTypeFile', $this->_activityTypeFile);
485 $this->assign('crmDir', $this->_crmDir);
486 }
487
488 $this->setFields();
489
490 if ($this->_activityTypeFile) {
491 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
492 $className::preProcess($this);
493 }
494
495 $this->_values = $this->get('values');
496 if (!is_array($this->_values)) {
497 $this->_values = [];
498 if (isset($this->_activityId) && $this->_activityId) {
499 $params = ['id' => $this->_activityId];
500 CRM_Activity_BAO_Activity::retrieve($params, $this->_values);
501 }
502
503 $this->set('values', $this->_values);
504 }
505
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']) ?: '';
510 }
511
512 if ($this->_activityTypeName === 'Inbound Email' &&
513 !CRM_Core_Permission::check('edit inbound email basic information and content')
514 ) {
515 $this->_fields['details']['type'] = 'static';
516 }
517
518 CRM_Core_Form_RecurringEntity::preProcess('civicrm_activity');
519 }
520
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)')),
524 $url,
525 $this->_values['id'],
526 'Activity',
527 $this->_values['source_contact_id'],
528 $this->_values['source_contact']
529 );
530 }
531 }
532
533 /**
534 * Set default values for the form.
535 *
536 * For edit/view mode the default values are retrieved from the database.
537 *
538 * @return array
539 */
540 public function setDefaultValues() {
541
542 $defaults = $this->_values + CRM_Core_Form_RecurringEntity::setDefaultValues();
543 // if we're editing...
544 if (isset($this->_activityId)) {
545
546 if ($this->_context != 'standalone') {
547 $this->assign('target_contact_value',
548 CRM_Utils_Array::value('target_contact_value', $defaults)
549 );
550 $this->assign('assignee_contact_value',
551 CRM_Utils_Array::value('assignee_contact_value', $defaults)
552 );
553 }
554
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);
558
559 // set default tags if exists
560 $defaults['tag'] = implode(',', CRM_Core_BAO_EntityTag::getTag($this->_activityId, 'civicrm_activity'));
561 }
562 else {
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;
566
567 $defaults['source_contact_id'] = $this->_sourceContactId;
568 $defaults['target_contact_id'] = $this->_targetContactId;
569 }
570
571 if (empty($defaults['activity_date_time'])) {
572 $defaults['activity_date_time'] = date('Y-m-d H:i:s');
573 }
574
575 if ($this->_activityTypeId) {
576 $defaults['activity_type_id'] = $this->_activityTypeId;
577 }
578
579 if (!$this->_single && !empty($this->_contactIds)) {
580 $defaults['target_contact_id'] = $this->_contactIds;
581 }
582
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']));
586 if ($count > 50) {
587 $this->freeze(['target_contact_id']);
588 }
589 }
590
591 if ($this->_action & (CRM_Core_Action::DELETE | CRM_Core_Action::RENEW)) {
592 $this->assign('delName', CRM_Utils_Array::value('subject', $defaults));
593 }
594
595 if ($this->_activityTypeFile) {
596 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
597 $defaults += $className::setDefaultValues($this);
598 }
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);
602 }
603 if (empty($defaults['status_id'])) {
604 $defaults['status_id'] = CRM_Core_OptionGroup::getDefaultValue('activity_status');
605 }
606 return $defaults;
607 }
608
609 /**
610 * Build Quick form.
611 *
612 * @throws \CRM_Core_Exception
613 * @throws \CiviCRM_API3_Exception
614 */
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);
619
620 $button = ts('Delete');
621 if ($this->_action & CRM_Core_Action::RENEW) {
622 $button = ts('Restore');
623 }
624 $this->addButtons([
625 [
626 'type' => 'next',
627 'name' => $button,
628 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
629 'isDefault' => TRUE,
630 ],
631 [
632 'type' => 'cancel',
633 'name' => ts('Cancel'),
634 ],
635 ]);
636 return;
637 }
638
639 // Build other activity links.
640 CRM_Activity_Form_ActivityLinks::commonBuildQuickForm($this);
641
642 // Enable form element (ActivityLinks sets this true).
643 $this->assign('suppressForm', FALSE);
644
645 $element = &$this->add('select', 'activity_type_id', ts('Activity Type'),
646 ['' => '- ' . ts('select') . ' -'] + $this->_fields['followup_activity_type_id']['attributes'],
647 FALSE, [
648 'onchange' => "CRM.buildCustomData( 'Activity', this.value, false, false, false, false, false, false, {$this->_currentlyViewedContactId});",
649 'class' => 'crm-select2 required',
650 ]
651 );
652
653 // Freeze for update mode.
654 if ($this->_action & CRM_Core_Action::UPDATE) {
655 $element->freeze();
656 }
657
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);
661 }
662
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']);
667
668 if ($values['type'] == 'select' && empty($attribute)) {
669 $this->addSelect($field, ['entity' => 'activity'], $required);
670 }
671 elseif ($values['type'] == 'entityRef') {
672 $this->addEntityRef($field, $values['label'], $attribute, $required);
673 }
674 else {
675 $this->add($values['type'], $field, $values['label'], $attribute, $required, CRM_Utils_Array::value('extra', $values));
676 }
677 }
678 }
679
680 // CRM-7362 --add campaigns.
681 CRM_Campaign_BAO_Campaign::addCampaign($this, CRM_Utils_Array::value('campaign_id', $this->_values));
682
683 // Add engagement level CRM-7775
684 $buildEngagementLevel = FALSE;
685 if (CRM_Campaign_BAO_Campaign::isCampaignEnable() &&
686 CRM_Campaign_BAO_Campaign::accessCampaign()
687 ) {
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).'),
692 'positiveInteger'
693 );
694 }
695 $this->assign('buildEngagementLevel', $buildEngagementLevel);
696
697 // check for survey activity
698 $this->_isSurveyActivity = FALSE;
699
700 if ($this->_activityId && CRM_Campaign_BAO_Campaign::isCampaignEnable() &&
701 CRM_Campaign_BAO_Campaign::accessCampaign()
702 ) {
703
704 $this->_isSurveyActivity = CRM_Campaign_BAO_Survey::isSurveyActivity($this->_activityId);
705 if ($this->_isSurveyActivity) {
706 $surveyId = CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity',
707 $this->_activityId,
708 'source_record_id'
709 );
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)
714 );
715 }
716 $surveyTitle = NULL;
717 if ($surveyId) {
718 $surveyTitle = CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $surveyId, 'title');
719 }
720 $this->assign('surveyTitle', $surveyTitle);
721 }
722 }
723 $this->assign('surveyActivity', $this->_isSurveyActivity);
724
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) {
729 $this->addRadio(
730 'separation',
731 ts('Activity Separation'),
732 [
733 'separate' => ts('Create separate activities for each contact'),
734 'combined' => ts('Create one activity with all contacts together'),
735 ]
736 );
737 }
738
739 $this->addRule('duration',
740 ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger'
741 );
742
743 // Add followup date.
744 $this->add('datepicker', 'followup_date', ts('in'));
745
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();
749 }
750
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);
755
756 $tags = CRM_Core_BAO_Tag::getColorTags('civicrm_activity');
757
758 if (!empty($tags)) {
759 $this->add('select2', 'tag', ts('Tags'), $tags, FALSE, [
760 'class' => 'huge',
761 'placeholder' => ts('- select -'),
762 'multiple' => TRUE,
763 ]);
764 }
765
766 // we need to hide activity tagset for special activities
767 $specialActivities = ['Open Case'];
768
769 if (!in_array($this->_activityTypeName, $specialActivities)) {
770 // build tag widget
771 $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_activity');
772 CRM_Core_Form_Tag::buildQuickForm($this, $parentNames, 'civicrm_activity', $this->_activityId);
773 }
774
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);
779 }
780 // form should be frozen for view mode
781 $this->freeze();
782
783 $this->addButtons([
784 [
785 'type' => 'cancel',
786 'name' => ts('Done'),
787 ],
788 ]);
789 }
790 else {
791 $this->addButtons([
792 [
793 'type' => 'upload',
794 'name' => ts('Save'),
795 'isDefault' => TRUE,
796 ],
797 [
798 'type' => 'cancel',
799 'name' => ts('Cancel'),
800 ],
801 ]);
802 }
803
804 if ($this->_activityTypeFile) {
805 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
806
807 $className::buildQuickForm($this);
808 $this->addFormRule([$className, 'formRule'], $this);
809 }
810
811 $this->addFormRule(['CRM_Activity_Form_Activity', 'formRule'], $this);
812
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);
818 }
819 else {
820 $this->assign('activityAssigneeNotification', TRUE);
821 }
822 $this->assign('doNotNotifyAssigneeFor', $doNotNotifyAssigneeFor);
823 }
824
825 /**
826 * Global form rule.
827 *
828 * @param array $fields
829 * The input form values.
830 * @param array $files
831 * The uploaded files if any.
832 * @param $self
833 *
834 * @return bool|array
835 * true if no errors, else array of errors
836 */
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') {
840 return TRUE;
841 }
842 $errors = [];
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');
845 }
846
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');
850
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.');
854 }
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');
857 }
858 }
859
860 if (!empty($fields['followup_activity_type_id']) && empty($fields['followup_date'])) {
861 $errors['followup_date'] = ts('Followup date is a required field.');
862 }
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.');
866 }
867
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.');
875 }
876
877 return $errors;
878 }
879
880 /**
881 * Process the form submission.
882 *
883 *
884 * @param array $params
885 *
886 * @return array|null
887 * @throws \CiviCRM_API3_Exception
888 */
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');
893 if (!$activityIds) {
894 // There are no repeat activities to delete - just this one.
895 $activityIds = [$this->_activityId];
896 }
897
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);
903
904 // delete tags for the entity
905 $tagParams = [
906 'entity_table' => 'civicrm_activity',
907 'entity_id' => $activityId,
908 ];
909
910 CRM_Core_BAO_EntityTag::del($tagParams);
911 }
912
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'
916 );
917
918 return NULL;
919 }
920
921 // store the submitted values in an array
922 if (!$params) {
923 $params = $this->controller->exportValues($this->_name);
924 }
925
926 // Set activity type id.
927 if (empty($params['activity_type_id'])) {
928 $params['activity_type_id'] = $this->_activityTypeId;
929 }
930
931 if (!empty($params['hidden_custom']) &&
932 !isset($params['custom'])
933 ) {
934 $customFields = CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
935 $this->_activityTypeId
936 );
937 $customFields = CRM_Utils_Array::crmArrayMerge($customFields,
938 CRM_Core_BAO_CustomField::getFields('Activity', FALSE, FALSE,
939 NULL, NULL, TRUE
940 )
941 );
942 $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
943 $this->_activityId,
944 'Activity'
945 );
946 }
947
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"]);
952 }
953 else {
954 $params["{$name}_contact_id"] = [];
955 }
956 }
957
958 // get ids for associated contacts
959 if (!$params['source_contact_id']) {
960 $params['source_contact_id'] = $this->_currentUserId;
961 }
962
963 if (isset($this->_activityId)) {
964 $params['id'] = $this->_activityId;
965 }
966
967 // add attachments as needed
968 CRM_Core_BAO_File::formatAttachment($params,
969 $params,
970 'civicrm_activity',
971 $this->_activityId
972 );
973
974 $params['is_multi_activity'] = CRM_Utils_Array::value('separation', $params) == 'separate';
975
976 $activity = [];
977 if (!empty($params['is_multi_activity']) &&
978 !CRM_Utils_Array::crmIsEmptyArray($params['target_contact_id'])
979 ) {
980 $targetContacts = $params['target_contact_id'];
981 foreach ($targetContacts as $targetContactId) {
982 $params['target_contact_id'] = [$targetContactId];
983 // save activity
984 $activity[] = $this->processActivity($params);
985 }
986 }
987 else {
988 // save activity
989 $activity = $this->processActivity($params);
990 }
991
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']);
996 }
997 else {
998 $url = CRM_Utils_System::url('civicrm/activity', ['action' => 'view', 'reset' => 1, 'id' => $this->_activityId]);
999 }
1000 CRM_Core_Session::singleton()->pushUserContext($url);
1001 }
1002
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']);
1013 }
1014 else {
1015 $params['parent_entity_id'] = $params['entity_id'];
1016 $scheduleReminderDetails = CRM_Core_BAO_RecurringEntity::getReminderDetailsByEntityId($params['entity_id'], $params['entity_table']);
1017 }
1018 if (property_exists($scheduleReminderDetails, 'id')) {
1019 $params['schedule_reminder_id'] = $scheduleReminderDetails->id;
1020 }
1021 }
1022 $params['dateColumns'] = ['activity_date_time'];
1023
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'];
1027 }
1028
1029 // unset activity id
1030 unset($params['id']);
1031 $linkedEntities = [
1032 [
1033 'table' => 'civicrm_activity_contact',
1034 'findCriteria' => [
1035 'activity_id' => $activityId,
1036 ],
1037 'linkedColumns' => ['activity_id'],
1038 'isRecurringEntityRecord' => FALSE,
1039 ],
1040 ];
1041 CRM_Core_Form_RecurringEntity::postProcess($params, 'civicrm_activity', $linkedEntities);
1042 }
1043
1044 return ['activity' => $activity];
1045 }
1046
1047 /**
1048 * Process activity creation.
1049 *
1050 * @param array $params
1051 * Associated array of submitted values.
1052 *
1053 * @return self|null|object
1054 */
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);
1067 }
1068 }
1069
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);
1073
1074 $activity = CRM_Activity_BAO_Activity::create($params);
1075
1076 // add tags if exists
1077 $tagParams = [];
1078 if (!empty($params['tag'])) {
1079 if (!is_array($params['tag'])) {
1080 $params['tag'] = explode(',', $params['tag']);
1081 }
1082
1083 $tagParams = array_fill_keys($params['tag'], 1);
1084 }
1085
1086 // Save static tags.
1087 CRM_Core_BAO_EntityTag::create($tagParams, 'civicrm_activity', $activity->id);
1088
1089 // Save free tags.
1090 if (isset($params['activity_taglist']) && !empty($params['activity_taglist'])) {
1091 CRM_Core_Form_Tag::postProcess($params['activity_taglist'], $activity->id, 'civicrm_activity', $this);
1092 }
1093
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);
1097
1098 // CRM-9590
1099 if (!empty($params['is_multi_activity'])) {
1100 $this->_activityIds[] = $activity->id;
1101 }
1102 else {
1103 $this->_activityId = $activity->id;
1104 }
1105
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.');
1112 }
1113
1114 // send copy to assignee contacts.CRM-4509
1115 $mailStatus = '';
1116
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]);
1123 }
1124 $assigneeContacts = CRM_Activity_BAO_ActivityAssignment::getAssigneeNames($activityIDs, TRUE, FALSE);
1125
1126 if (!CRM_Utils_Array::crmIsEmptyArray($params['assignee_contact_id'])) {
1127 $mailToContacts = [];
1128
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];
1133 }
1134 }
1135
1136 $sent = CRM_Activity_BAO_Activity::sendToAssignee($activity, $mailToContacts);
1137 if ($sent) {
1138 $mailStatus .= ts("A copy of the activity has also been sent to assignee contacts(s).");
1139 }
1140 }
1141
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;
1148 }
1149 }
1150
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).");
1154 }
1155 }
1156 }
1157
1158 // set status message
1159 $subject = '';
1160 if (!empty($params['subject'])) {
1161 $subject = "'" . $params['subject'] . "'";
1162 }
1163
1164 CRM_Core_Session::setStatus(ts('Activity %1 has been saved. %2 %3',
1165 [
1166 1 => $subject,
1167 2 => $followupStatus,
1168 3 => $mailStatus,
1169 ]
1170 ), ts('Saved'), 'success');
1171
1172 return $activity;
1173 }
1174
1175 /**
1176 * Shorthand for getting id by display name (makes code more readable)
1177 * @param $displayName
1178 * @return null|string
1179 */
1180 protected function _getIdByDisplayName($displayName) {
1181 return CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
1182 $displayName,
1183 'id',
1184 'sort_name'
1185 );
1186 }
1187
1188 /**
1189 * Shorthand for getting display name by id (makes code more readable)
1190 * @param $id
1191 * @return null|string
1192 */
1193 protected function _getDisplayNameById($id) {
1194 return CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
1195 $id,
1196 'sort_name',
1197 'id'
1198 );
1199 }
1200
1201 /**
1202 * Let injecting activity type file do any processing.
1203 * needed, before the activity is added/updated
1204 *
1205 * @param array $params
1206 */
1207 public function beginPostProcess(&$params) {
1208 if ($this->_activityTypeFile) {
1209 $className = "CRM_{$this->_crmDir}_Form_Activity_{$this->_activityTypeFile}";
1210 $className::beginPostProcess($this, $params);
1211 }
1212 }
1213
1214 /**
1215 * Let injecting activity type file do any processing
1216 * needed, after the activity has been added/updated
1217 *
1218 * @param array $params
1219 * @param $activity
1220 */
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);
1225 }
1226 }
1227
1228 /**
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.
1230 *
1231 * @return string[]
1232 */
1233 public function getActivityTypeDisplayLabels() {
1234 return CRM_Core_OptionGroup::values('activity_type', FALSE, FALSE, FALSE, 'AND v.value = ' . $this->_activityTypeId, 'label');
1235 }
1236
1237 /**
1238 * For the moment this is just pulled from preProcess
1239 */
1240 public function assignActivityType() {
1241 if ($this->_activityTypeId) {
1242 $activityTypeDisplayLabels = $this->getActivityTypeDisplayLabels();
1243 if ($activityTypeDisplayLabels[$this->_activityTypeId]) {
1244 $this->_activityTypeName = $activityTypeDisplayLabels[$this->_activityTypeId];
1245
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());
1249 }
1250 // Set title.
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);
1254
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') . ')';
1260 }
1261 CRM_Utils_System::setTitle($displayName . ' - ' . $activityTypeDisplayLabel);
1262 }
1263 else {
1264 CRM_Utils_System::setTitle(ts('%1 Activity', [1 => $activityTypeDisplayLabel]));
1265 }
1266 }
1267 }
1268 }
1269
1270 }