Merge pull request #18559 from agileware/CIVICRM-1567
[civicrm-core.git] / CRM / Campaign / Form / Task / Reserve.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
3819f101 19 * This class provides the functionality to add contacts for voter reservation.
6a488035
TO
20 */
21class CRM_Campaign_Form_Task_Reserve extends CRM_Campaign_Form_Task {
22
23 /**
6a488035 24 * @var int
67d19299 25 * Survey id.
6a488035
TO
26 */
27 protected $_surveyId;
28
29 /**
100fef9d 30 * Interviewer id
6a488035
TO
31 *
32 * @var int
33 */
34 protected $_interviewerId;
35
36 /**
100fef9d 37 * Survey details
6a488035
TO
38 *
39 * @var object
40 */
41 protected $_surveyDetails;
42
43 /**
100fef9d 44 * Number of voters
6a488035
TO
45 *
46 * @var int
47 */
48 protected $_numVoters;
49
50 /**
fe482240 51 * Build all the data structures needed to build the form.
6a488035 52 */
00be9182 53 public function preProcess() {
6a488035
TO
54 parent::preProcess();
55
56 //get the survey id from user submitted values.
57 $this->_surveyId = $this->get('surveyId');
58 $this->_interviewerId = $this->get('interviewerId');
59 if (!$this->_surveyId) {
60 CRM_Core_Error::statusBounce(ts("Could not find Survey Id."));
61 }
62 if (!$this->_interviewerId) {
63 CRM_Core_Error::statusBounce(ts("Missing Interviewer contact."));
64 }
65 if (!is_array($this->_contactIds) || empty($this->_contactIds)) {
66 CRM_Core_Error::statusBounce(ts("Could not find contacts for reservation."));
67 }
68
be2fb01f 69 $params = ['id' => $this->_surveyId];
6a488035
TO
70 CRM_Campaign_BAO_Survey::retrieve($params, $this->_surveyDetails);
71
72 //get the survey activities.
73 $activityStatus = CRM_Core_PseudoConstant::activityStatus('name');
be2fb01f
CW
74 $statusIds = [];
75 foreach (['Scheduled'] as $name) {
6a488035
TO
76 if ($statusId = array_search($name, $activityStatus)) {
77 $statusIds[] = $statusId;
78 }
79 }
80
81 // these are the activities count that are linked to the current
82 // interviewer and current survey and not the list of ALL survey activities
83 $this->_numVoters = CRM_Campaign_BAO_Survey::getSurveyActivities($this->_surveyId,
84 $this->_interviewerId,
85 $statusIds,
86 NULL,
87 TRUE
88 );
89 //validate the selected survey.
90 $this->validateSurvey();
91 $this->assign('surveyTitle', $this->_surveyDetails['title']);
92 $this->assign('activityType', $this->_surveyDetails['activity_type_id']);
93 $this->assign('surveyId', $this->_surveyId);
94
95 //append breadcrumb to survey dashboard.
96 if (CRM_Campaign_BAO_Campaign::accessCampaign()) {
97 $url = CRM_Utils_System::url('civicrm/campaign', 'reset=1&subPage=survey');
be2fb01f 98 CRM_Utils_System::appendBreadCrumb([['title' => ts('Survey(s)'), 'url' => $url]]);
6a488035
TO
99 }
100
101 //set the title.
102 CRM_Utils_System::setTitle(ts('Reserve Respondents'));
103 }
104
00be9182 105 public function validateSurvey() {
6a488035 106 $errorMsg = NULL;
9c1bc317 107 $maxVoters = $this->_surveyDetails['max_number_of_contacts'] ?? NULL;
6a488035
TO
108 if ($maxVoters) {
109 if ($maxVoters <= $this->_numVoters) {
110 $errorMsg = ts('The maximum number of contacts is already reserved for this interviewer.');
111 }
112 elseif (count($this->_contactIds) > ($maxVoters - $this->_numVoters)) {
99483ce8 113 $errorMsg = ts('You can reserve a maximum of %count contact at a time for this survey.',
be2fb01f 114 [
99483ce8
CW
115 'plural' => 'You can reserve a maximum of %count contacts at a time for this survey.',
116 'count' => $maxVoters - $this->_numVoters,
be2fb01f 117 ]
6a488035
TO
118 );
119 }
120 }
121
9c1bc317 122 $defaultNum = $this->_surveyDetails['default_number_of_contacts'] ?? NULL;
6a488035 123 if (!$errorMsg && $defaultNum && (count($this->_contactIds) > $defaultNum)) {
99483ce8 124 $errorMsg = ts('You can reserve a maximum of %count contact at a time for this survey.',
be2fb01f 125 [
99483ce8
CW
126 'plural' => 'You can reserve a maximum of %count contacts at a time for this survey.',
127 'count' => $defaultNum,
be2fb01f 128 ]
6a488035
TO
129 );
130 }
131
132 if ($errorMsg) {
133 CRM_Core_Error::statusBounce($errorMsg);
134 }
135 }
136
137 /**
fe482240 138 * Build the form object.
6a488035 139 */
00be9182 140 public function buildQuickForm() {
6a488035
TO
141 // allow to add contact to either new or existing group.
142 $this->addElement('text', 'ActivityType', ts('Activity Type'));
143 $this->addElement('text', 'newGroupName', ts('Name for new group'));
144 $this->addElement('text', 'newGroupDesc', ts('Description of new group'));
24431f7b 145 $groups = CRM_Core_PseudoConstant::nestedGroup();
6a488035
TO
146 $hasExistingGroups = FALSE;
147 if (is_array($groups) && !empty($groups)) {
148 $hasExistingGroups = TRUE;
149 $this->addElement('select', 'groups', ts('Add respondent(s) to existing group(s)'),
be2fb01f 150 $groups, ['multiple' => "multiple", 'class' => 'crm-select2']
6a488035
TO
151 );
152 }
153 $this->assign('hasExistingGroups', $hasExistingGroups);
154
be2fb01f
CW
155 $buttons = [
156 [
353ffa53 157 'type' => 'done',
6a488035
TO
158 'name' => ts('Reserve'),
159 'subName' => 'reserve',
160 'isDefault' => TRUE,
be2fb01f
CW
161 ],
162 ];
6a488035
TO
163
164 if (CRM_Core_Permission::check('manage campaign') ||
165 CRM_Core_Permission::check('administer CiviCampaign') ||
166 CRM_Core_Permission::check('interview campaign contacts')
167 ) {
be2fb01f 168 $buttons[] = [
6a488035
TO
169 'type' => 'next',
170 'name' => ts('Reserve and Interview'),
171 'subName' => 'reserveToInterview',
be2fb01f 172 ];
6a488035 173 }
be2fb01f 174 $buttons[] = [
6a488035
TO
175 'type' => 'back',
176 'name' => ts('Cancel'),
be2fb01f 177 ];
6a488035
TO
178
179 $this->addButtons($buttons);
be2fb01f 180 $this->addFormRule(['CRM_Campaign_Form_Task_Reserve', 'formRule'], $this);
6a488035
TO
181 }
182
183 /**
fe482240 184 * Global validation rules for the form.
6a488035 185 *
7aaf6db0
TO
186 * @param array $fields
187 * Posted values of the form.
6a488035 188 *
da6b46f4
EM
189 * @param $files
190 * @param $self
191 *
a6c01b45
CW
192 * @return array
193 * list of errors to be posted back to the form
6a488035 194 */
00be9182 195 public static function formRule($fields, $files, $self) {
be2fb01f 196 $errors = [];
6a488035 197 $invalidGroupName = FALSE;
a7488080 198 if (!empty($fields['newGroupName'])) {
353ffa53
TO
199 $title = trim($fields['newGroupName']);
200 $name = CRM_Utils_String::titleToVar($title);
201 $query = 'select count(*) from civicrm_group where name like %1 OR title like %2';
be2fb01f
CW
202 $grpCnt = CRM_Core_DAO::singleValueQuery($query, [
203 1 => [$name, 'String'],
204 2 => [$title, 'String'],
205 ]);
6a488035
TO
206 if ($grpCnt) {
207 $invalidGroupName = TRUE;
be2fb01f 208 $errors['newGroupName'] = ts('Group \'%1\' already exists.', [1 => $fields['newGroupName']]);
6a488035
TO
209 }
210 }
211 $self->assign('invalidGroupName', $invalidGroupName);
212
213 return empty($errors) ? TRUE : $errors;
214 }
215
216 /**
fe482240 217 * Process the form after the input has been submitted and validated.
67d19299 218 */
6a488035
TO
219 public function postProcess() {
220 //add reservation.
353ffa53 221 $countVoters = 0;
9c1bc317 222 $maxVoters = $this->_surveyDetails['max_number_of_contacts'] ?? NULL;
6a488035 223 $activityStatus = CRM_Core_PseudoConstant::activityStatus('name');
353ffa53 224 $statusHeld = array_search('Scheduled', $activityStatus);
6a488035 225
be2fb01f 226 $reservedVoterIds = [];
6a488035 227 foreach ($this->_contactIds as $cid) {
58438e5b 228 $subject = $this->_surveyDetails['title'] . ' - ' . ts('Respondent Reservation');
353ffa53 229 $session = CRM_Core_Session::singleton();
be2fb01f 230 $activityParams = [
353ffa53 231 'source_contact_id' => $session->get('userID'),
be2fb01f
CW
232 'assignee_contact_id' => [$this->_interviewerId],
233 'target_contact_id' => [$cid],
6a488035
TO
234 'source_record_id' => $this->_surveyId,
235 'activity_type_id' => $this->_surveyDetails['activity_type_id'],
236 'subject' => $subject,
237 'activity_date_time' => date('YmdHis'),
238 'status_id' => $statusHeld,
239 'skipRecentView' => 1,
6b409353 240 'campaign_id' => $this->_surveyDetails['campaign_id'] ?? NULL,
be2fb01f 241 ];
6a488035
TO
242 $activity = CRM_Activity_BAO_Activity::create($activityParams);
243 if ($activity->id) {
244 $countVoters++;
245 $reservedVoterIds[$cid] = $cid;
246 }
247 if ($maxVoters && ($maxVoters <= ($this->_numVoters + $countVoters))) {
248 break;
249 }
250 }
251
252 //add reserved voters to groups.
253 $groupAdditions = $this->_addRespondentToGroup($reservedVoterIds);
254
255 // Success message
256 if ($countVoters > 0) {
be2fb01f 257 $status = '<p>' . ts("%count contact has been reserved.", ['plural' => '%count contacts have been reserved.', 'count' => $countVoters]) . '</p>';
6a488035 258 if ($groupAdditions) {
99483ce8 259 $status .= '<p>' . ts('They have been added to %1.',
be2fb01f 260 [1 => implode(' ' . ts('and') . ' ', $groupAdditions)]
353ffa53 261 ) . '</p>';
6a488035
TO
262 }
263 CRM_Core_Session::setStatus($status, ts('Reservation Added'), 'success');
264 }
265 // Error message
266 if (count($this->_contactIds) > $countVoters) {
99483ce8 267 CRM_Core_Session::setStatus(ts('Reservation did not add for %count contact.',
be2fb01f 268 [
99483ce8
CW
269 'plural' => 'Reservation did not add for %count contacts.',
270 'count' => (count($this->_contactIds) - $countVoters),
be2fb01f 271 ]
6a488035
TO
272 ), ts('Notice'));
273 }
274
275 //get ready to jump to voter interview form.
276 $buttonName = $this->controller->getButtonName();
277 if (!empty($reservedVoterIds) &&
278 $buttonName == '_qf_Reserve_next_reserveToInterview'
279 ) {
280 $this->controller->set('surveyId', $this->_surveyId);
281 $this->controller->set('contactIds', $reservedVoterIds);
282 $this->controller->set('interviewerId', $this->_interviewerId);
283 $this->controller->set('reserveToInterview', TRUE);
284 }
285 }
286
30c4e065
EM
287 /**
288 * @param $contactIds
289 *
290 * @return array
291 */
6a488035 292 private function _addRespondentToGroup($contactIds) {
be2fb01f 293 $groupAdditions = [];
6a488035
TO
294 if (empty($contactIds)) {
295 return $groupAdditions;
296 }
297
353ffa53 298 $params = $this->controller->exportValues($this->_name);
be2fb01f 299 $groups = CRM_Utils_Array::value('groups', $params, []);
9c1bc317
CW
300 $newGroupName = $params['newGroupName'] ?? NULL;
301 $newGroupDesc = $params['newGroupDesc'] ?? NULL;
6a488035
TO
302
303 $newGroupId = NULL;
304 //create new group.
305 if ($newGroupName) {
be2fb01f 306 $grpParams = [
6a488035
TO
307 'title' => $newGroupName,
308 'description' => $newGroupDesc,
309 'is_active' => TRUE,
be2fb01f 310 ];
6a488035
TO
311 $group = CRM_Contact_BAO_Group::create($grpParams);
312 $groups[] = $newGroupId = $group->id;
313 }
314
315 //add the respondents to groups.
316 if (is_array($groups)) {
317 $existingGroups = CRM_Core_PseudoConstant::group();
318 foreach ($groups as $groupId) {
319 $addCount = CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $groupId);
9c1bc317 320 $totalCount = $addCount[1] ?? NULL;
6a488035
TO
321 if ($groupId == $newGroupId) {
322 $name = $newGroupName;
323 $new = TRUE;
324 }
325 else {
326 $name = $existingGroups[$groupId];
327 $new = FALSE;
328 }
329 if ($totalCount) {
330 $url = CRM_Utils_System::url('civicrm/group/search',
331 'reset=1&force=1&context=smog&gid=' . $groupId
332 );
333 $groupAdditions[] = '<a href="' . $url . '">' . $name . '</a>';
334 }
335 }
336 }
337
338 return $groupAdditions;
339 }
96025800 340
6a488035 341}