Merge remote-tracking branch 'upstream/4.5' into 4.5-master-2014-12-30-00-43-32
[civicrm-core.git] / CRM / Mailing / Form / Group.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
32 * $Id$
33 *
34 */
35
36 /**
37 * Choose include / exclude groups and mailings
38 *
39 */
40 class CRM_Mailing_Form_Group extends CRM_Contact_Form_Task {
41
42 /**
43 * The mailing ID of the mailing if we are resuming a mailing
44 *
45 * @var integer
46 */
47 protected $_mailingID;
48
49 /**
50 * Set variables up before form is built
51 *
52 * @return void
53 * @access public
54 */
55 public function preProcess() {
56 if (CRM_Core_BAO_MailSettings::defaultDomain() == "EXAMPLE.ORG") {
57 CRM_Core_Error::fatal(ts('The <a href="%1">default mailbox</a> has not been configured. You will find <a href="%2">more info in our online user and administrator guide.</a>', array(1 => CRM_Utils_System::url('civicrm/admin/mailSettings', 'reset=1'), 2 => "http://book.civicrm.org/user/advanced-configuration/email-system-configuration/")));
58 }
59
60 $this->_mailingID = CRM_Utils_Request::retrieve('mid', 'Integer', $this, FALSE, NULL);
61
62 // when user come from search context.
63 $this->_searchBasedMailing = CRM_Contact_Form_Search::isSearchContext($this->get('context'));
64 if ($this->_searchBasedMailing) {
65 $searchParams = $this->controller->exportValues();
66 // number of records that were selected - All or Few.
67 $this->_resultSelectOption = $searchParams['radio_ts'];
68 if (CRM_Utils_Array::value('task', $searchParams) == 20) {
69 parent::preProcess();
70 }
71 }
72
73 $session = CRM_Core_Session::singleton();
74 if ($this->_searchBasedMailing) {
75 $config = CRM_Core_Config::singleton();
76 $path = CRM_Utils_Array::value($config->userFrameworkURLVar, $_GET);
77 $qfKey = CRM_Utils_Array::value('qfKey', $_GET);
78 if ($qfKey) {
79 $session->pushUserContext(CRM_Utils_System::url($path, "qfKey=$qfKey"));
80 }
81 else {
82 $session->pushUserContext(CRM_Utils_System::url('civicrm/mailing', 'reset=1'));
83 }
84 }
85 elseif (strpos($session->readUserContext(), 'civicrm/mailing') === FALSE) {
86 // use previous context unless mailing is not schedule, CRM-4290
87 $session->pushUserContext(CRM_Utils_System::url('civicrm/mailing', 'reset=1'));
88 }
89 }
90
91 /**
92 * Set default values for the form.
93 * the default values are retrieved from the database
94 *
95 * @access public
96 *
97 * @return void
98 */
99 function setDefaultValues() {
100 $continue = CRM_Utils_Request::retrieve('continue', 'String', $this, FALSE, NULL);
101
102 $defaults = array();
103 $defaults['dedupe_email'] = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
104 'dedupe_email_default', NULL, FALSE
105 );
106 if ($this->_mailingID) {
107 // check that the user has permission to access mailing id
108 CRM_Mailing_BAO_Mailing::checkPermission($this->_mailingID);
109
110 $mailing = new CRM_Mailing_DAO_Mailing();
111 $mailing->id = $this->_mailingID;
112 $mailing->addSelect('name', 'campaign_id');
113 $mailing->find(TRUE);
114
115 $defaults['name'] = $mailing->name;
116 if (!$continue) {
117 $defaults['name'] = ts('Copy of %1', array(1 => $mailing->name));
118 }
119 else {
120 // CRM-7590, reuse same mailing ID if we are continuing
121 $this->set('mailing_id', $this->_mailingID);
122 }
123
124 $defaults['campaign_id'] = $mailing->campaign_id;
125 $defaults['dedupe_email'] = $mailing->dedupe_email;
126 $defaults['location_type_id'] = $mailing->location_type_id;
127 $defaults['email_selection_method'] = $mailing->email_selection_method;
128
129 $dao = new CRM_Mailing_DAO_MailingGroup();
130
131 $mailingGroups = array(
132 'civicrm_group' => array( ),
133 'civicrm_mailing' => array( )
134 );
135 $dao->mailing_id = $this->_mailingID;
136 $dao->find();
137 while ($dao->fetch()) {
138 // account for multi-lingual
139 // CRM-11431
140 $entityTable = 'civicrm_group';
141 if (substr($dao->entity_table, 0, 15) == 'civicrm_mailing') {
142 $entityTable = 'civicrm_mailing';
143 }
144 $mailingGroups[$entityTable][$dao->group_type][] = $dao->entity_id;
145 }
146
147 $defaults['includeGroups'] = CRM_Utils_Array::value('include', $mailingGroups['civicrm_group']);
148 $defaults['excludeGroups'] = CRM_Utils_Array::value('exclude', $mailingGroups['civicrm_group']);
149
150 if (!empty($mailingGroups['civicrm_mailing'])) {
151 $defaults['includeMailings'] = CRM_Utils_Array::value('Include', $mailingGroups['civicrm_mailing']);
152 $defaults['excludeMailings'] = CRM_Utils_Array::value('Exclude', $mailingGroups['civicrm_mailing']);
153 }
154 }
155
156 //when the context is search hide the mailing recipients.
157 $showHide = new CRM_Core_ShowHideBlocks();
158 $showGroupSelector = TRUE;
159 if ($this->_searchBasedMailing) {
160 $showGroupSelector = FALSE;
161 $formElements = array('includeGroups', 'excludeGroups', 'includeMailings', 'excludeMailings');
162 $formValues = $this->controller->exportValues($this->_name);
163 foreach ($formElements as $element) {
164 if (!empty($formValues[$element])) {
165 $showGroupSelector = TRUE;
166 break;
167 }
168 }
169 }
170
171 if ($showGroupSelector) {
172 $showHide->addShow("id-additional");
173 $showHide->addHide("id-additional-show");
174 }
175 else {
176 $showHide->addShow("id-additional-show");
177 $showHide->addHide("id-additional");
178 }
179 $showHide->addToTemplate();
180
181 return $defaults;
182 }
183
184 /**
185 * Build the form object
186 *
187 * @return void
188 * @access public
189 */
190 public function buildQuickForm() {
191
192 //get the context
193 $context = $this->get('context');
194 if ($this->_searchBasedMailing) {
195 $context = 'search';
196 }
197 $this->assign('context', $context);
198
199 $this->add('text', 'name', ts('Name Your Mailing'),
200 CRM_Core_DAO::getAttribute('CRM_Mailing_DAO_Mailing', 'name'),
201 TRUE
202 );
203
204 $hiddenMailingGroup = NULL;
205 $campaignId = NULL;
206
207 //CRM-7362 --add campaigns.
208 if ($this->_mailingID) {
209 $campaignId = CRM_Core_DAO::getFieldValue('CRM_Mailing_DAO_Mailing', $this->_mailingID, 'campaign_id');
210 $hiddenMailingGroup = CRM_Mailing_BAO_Mailing::hiddenMailingGroup($this->_mailingID);
211 }
212 CRM_Campaign_BAO_Campaign::addCampaign($this, $campaignId);
213
214 //dedupe on email option
215 $this->addElement('checkbox', 'dedupe_email', ts('Remove duplicate emails?'));
216
217 // location types
218 $locationTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id', array('id' => 'display_name'));
219 $this->addElement('select', 'location_type_id', ts("Location Type"), array('' => ts('Automatic')) + $locationTypes);
220
221 $methods = CRM_Core_SelectValues::emailSelectMethods();
222 $this->addElement('select', 'email_selection_method', ts("Email Selection Method"), $methods);
223
224 //get the mailing groups.
225 $groups = CRM_Core_PseudoConstant::nestedGroup('Mailing');
226 if ($hiddenMailingGroup) {
227 $groups[$hiddenMailingGroup] =
228 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $hiddenMailingGroup, 'title');
229 }
230
231 $mailings = CRM_Mailing_PseudoConstant::completed();
232 if (!$mailings) {
233 $mailings = array();
234 }
235
236 // run the groups through a hook so users can trim it if needed
237 CRM_Utils_Hook::mailingGroups($this, $groups, $mailings);
238
239 //when the context is search add base group's.
240 if ($this->_searchBasedMailing) {
241 //get the static groups
242 $staticGroups = CRM_Core_PseudoConstant::staticGroup(FALSE, 'Mailing');
243 $this->add('select', 'baseGroup',
244 ts('Unsubscription Group'),
245 array(
246 '' => ts('- select -')) + CRM_Contact_BAO_Group::getGroupsHierarchy($staticGroups, NULL, '&nbsp;&nbsp;', TRUE),
247 TRUE,
248 array('class' => 'crm-select2 huge')
249 );
250 }
251
252 $select2style = array(
253 'multiple' => TRUE,
254 'style' => 'width: 100%; max-width: 60em;',
255 'class' => 'crm-select2',
256 'placeholder' => ts('- select -'),
257 );
258
259 $this->add('select', 'includeGroups',
260 ts('Include Group(s)'),
261 $groups,
262 !$this->_searchBasedMailing,
263 $select2style
264 );
265
266 $this->add('select', 'excludeGroups',
267 ts('Exclude Group(s)'),
268 $groups,
269 FALSE,
270 $select2style
271 );
272
273 $this->add('select', 'includeMailings',
274 ts('INCLUDE Recipients of These Mailing(s)') . ' ',
275 $mailings,
276 FALSE,
277 $select2style
278 );
279 $this->add('select', 'excludeMailings',
280 ts('EXCLUDE Recipients of These Mailing(s)') . ' ',
281 $mailings,
282 FALSE,
283 $select2style
284 );
285
286 $this->addFormRule(array('CRM_Mailing_Form_Group', 'formRule'));
287
288 $buttons = array(
289 array('type' => 'next',
290 'name' => ts('Next >>'),
291 'spacing' => '&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;',
292 'isDefault' => TRUE,
293 ),
294 array(
295 'type' => 'submit',
296 'name' => ts('Save & Continue Later'),
297 ),
298 array(
299 'type' => 'cancel',
300 'name' => ts('Cancel'),
301 ),
302 );
303
304 $this->addButtons($buttons);
305
306 $this->assign('groupCount', count($groups));
307 $this->assign('mailingCount', count($mailings));
308 if(count($groups) == 0 && count($mailings) == 0 && !$this->_searchBasedMailing) {
309 CRM_Core_Error::statusBounce("To send a mailing, you must have a valid group of recipients - either at least one group that's a Mailing List or at least one previous mailing or start from a search");
310 }
311 }
312
313 public function postProcess() {
314 $values = $this->controller->exportValues($this->_name);
315
316 //build hidden smart group. when user want to send mailing
317 //through search contact-> more action -> send Mailing. CRM-3711
318 $groups = array();
319 if ($this->_searchBasedMailing && $this->_contactIds) {
320 $session = CRM_Core_Session::singleton();
321
322 if ($this->_resultSelectOption == 'ts_sel') {
323 // create a static grp if only a subset of result set was selected:
324
325 $randID = md5(time());
326 $grpTitle = "Hidden Group {$randID}";
327 $grpID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $grpTitle, 'id', 'title');
328
329 if (!$grpID) {
330 $groupParams = array(
331 'title' => $grpTitle,
332 'is_active' => 1,
333 'is_hidden' => 1,
334 'group_type' => array('2' => 1),
335 );
336
337 $group = CRM_Contact_BAO_Group::create($groupParams);
338 $grpID = $group->id;
339
340 CRM_Contact_BAO_GroupContact::addContactsToGroup($this->_contactIds, $group->id);
341
342 $newGroupTitle = "Hidden Group {$grpID}";
343 $groupParams = array(
344 'id' => $grpID,
345 'name' => CRM_Utils_String::titleToVar($newGroupTitle),
346 'title' => $newGroupTitle,
347 'group_type' => array('2' => 1),
348 );
349 $group = CRM_Contact_BAO_Group::create($groupParams);
350 }
351
352 // note at this point its a static group
353 $smartGroupId = $grpID;
354 }
355 else {
356 //get the hidden smart group id.
357 $ssId = $this->get('ssID');
358 $hiddenSmartParams = array('group_type' => array('2' => 1),
359 'form_values' => $this->get('formValues'),
360 'saved_search_id' => $ssId,
361 'search_custom_id' => $this->get('customSearchID'),
362 'search_context' => $this->get('context'),
363 );
364
365 list($smartGroupId, $savedSearchId) = CRM_Contact_BAO_Group::createHiddenSmartGroup($hiddenSmartParams);
366
367 //set the saved search id.
368 if (!$ssId) {
369 if ($savedSearchId) {
370 $this->set('ssID', $savedSearchId);
371 }
372 else {
373 CRM_Core_Error::fatal();
374 }
375 }
376 }
377
378 //get the base group for this mailing, CRM-3711
379 $groups['base'] = array($values['baseGroup']);
380 $values['includeGroups'][] = $smartGroupId;
381 }
382
383 foreach (
384 array('name', 'group_id', 'search_id', 'search_args', 'campaign_id', 'dedupe_email', 'location_type_id', 'email_selection_method') as $n
385 ) {
386 if (!empty($values[$n])) {
387 $params[$n] = $values[$n];
388 }
389 }
390
391
392 $qf_Group_submit = $this->controller->exportValue($this->_name, '_qf_Group_submit');
393 $this->set('name', $params['name']);
394
395 $inGroups = $values['includeGroups'];
396 $outGroups = $values['excludeGroups'];
397 $inMailings = $values['includeMailings'];
398 $outMailings = $values['excludeMailings'];
399
400 if (is_array($inGroups)) {
401 foreach ($inGroups as $key => $id) {
402 if ($id) {
403 $groups['include'][] = $id;
404 }
405 }
406 }
407 if (is_array($outGroups)) {
408 foreach ($outGroups as $key => $id) {
409 if ($id) {
410 $groups['exclude'][] = $id;
411 }
412 }
413 }
414
415 $mailings = array();
416 if (is_array($inMailings)) {
417 foreach ($inMailings as $key => $id) {
418 if ($id) {
419 $mailings['include'][] = $id;
420 }
421 }
422 }
423 if (is_array($outMailings)) {
424 foreach ($outMailings as $key => $id) {
425 if ($id) {
426 $mailings['exclude'][] = $id;
427 }
428 }
429 }
430
431 $session = CRM_Core_Session::singleton();
432 $params['groups'] = $groups;
433 $params['mailings'] = $mailings;
434 $ids = array();
435 if ($this->get('mailing_id')) {
436
437 // don't create a new mailing if already exists
438 $ids['mailing_id'] = $this->get('mailing_id');
439
440 $groupTableName = CRM_Contact_BAO_Group::getTableName();
441 $mailingTableName = CRM_Mailing_BAO_Mailing::getTableName();
442
443 // delete previous includes/excludes, if mailing already existed
444 foreach (array('groups', 'mailings') as $entity) {
445 $mg = new CRM_Mailing_DAO_MailingGroup();
446 $mg->mailing_id = $ids['mailing_id'];
447 $mg->entity_table = ($entity == 'groups') ? $groupTableName : $mailingTableName;
448 $mg->find();
449 while ($mg->fetch()) {
450 $mg->delete();
451 }
452 }
453 }
454 else {
455 // new mailing, so lets set the created_id
456 $session = CRM_Core_Session::singleton();
457 $params['created_id'] = $session->get('userID');
458 $params['created_date'] = date('YmdHis');
459 }
460 $mailing = CRM_Mailing_BAO_Mailing::create($params, $ids);
461 $this->set('mailing_id', $mailing->id);
462
463 $dedupeEmail = FALSE;
464 if (isset($params['dedupe_email'])) {
465 $dedupeEmail = $params['dedupe_email'];
466 }
467
468 // mailing id should be added to the form object
469 $this->_mailingID = $mailing->id;
470
471 // also compute the recipients and store them in the mailing recipients table
472 CRM_Mailing_BAO_Mailing::getRecipients(
473 $mailing->id,
474 $mailing->id,
475 NULL,
476 NULL,
477 TRUE,
478 $dedupeEmail
479 );
480
481 $count = CRM_Mailing_BAO_Recipients::mailingSize($mailing->id);
482 $this->set('count', $count);
483 $this->assign('count', $count);
484 $this->set('groups', $groups);
485 $this->set('mailings', $mailings);
486
487 if ($qf_Group_submit) {
488 //when user perform mailing from search context
489 //redirect it to search result CRM-3711.
490 $ssID = $this->get('ssID');
491 $context = $this->get('context');
492 if ($ssID && $this->_searchBasedMailing) {
493 if ($this->_action == CRM_Core_Action::BASIC) {
494 $fragment = 'search';
495 }
496 elseif ($this->_action == CRM_Core_Action::PROFILE) {
497 $fragment = 'search/builder';
498 }
499 elseif ($this->_action == CRM_Core_Action::ADVANCED) {
500 $fragment = 'search/advanced';
501 }
502 else {
503 $fragment = 'search/custom';
504 }
505
506 $context = $this->get('context');
507 if (!CRM_Contact_Form_Search::isSearchContext($context)) {
508 $context = 'search';
509 }
510 $urlParams = "force=1&reset=1&ssID={$ssID}&context={$context}";
511
512 $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
513 if (CRM_Utils_Rule::qfKey($qfKey)) {
514 $urlParams .= "&qfKey=$qfKey";
515 }
516
517 $draftURL = CRM_Utils_System::url('civicrm/mailing/browse/unscheduled', 'scheduled=false&reset=1');
518 $status = ts("You can continue later by clicking the 'Continue' action to resume working on it.<br />From <a href='%1'>Draft and Unscheduled Mailings</a>.", array(1 => $draftURL));
519
520 // Redirect user to search.
521 $url = CRM_Utils_System::url('civicrm/contact/' . $fragment, $urlParams);
522 }
523 else {
524 $status = ts("Click the 'Continue' action to resume working on it.");
525 $url = CRM_Utils_System::url('civicrm/mailing/browse/unscheduled', 'scheduled=false&reset=1');
526 }
527 CRM_Core_Session::setStatus($status, ts('Mailing Saved'), 'success');
528 return $this->controller->setDestination($url);
529 }
530 }
531
532 /**
533 * Display Name of the form
534 *
535 * @access public
536 *
537 * @return string
538 */
539 public function getTitle() {
540 return ts('Select Recipients');
541 }
542
543 /**
544 * Global validation rules for the form
545 *
546 * @param array $fields posted values of the form
547 *
548 * @return array list of errors to be posted back to the form
549 * @static
550 * @access public
551 */
552 static function formRule($fields) {
553 $errors = array();
554 if (isset($fields['includeGroups']) &&
555 is_array($fields['includeGroups']) &&
556 isset($fields['excludeGroups']) &&
557 is_array($fields['excludeGroups'])
558 ) {
559 $checkGroups = array();
560 $checkGroups = array_intersect($fields['includeGroups'], $fields['excludeGroups']);
561 if (!empty($checkGroups)) {
562 $errors['excludeGroups'] = ts('Cannot have same groups in Include Group(s) and Exclude Group(s).');
563 }
564 }
565
566 if (isset($fields['includeMailings']) &&
567 is_array($fields['includeMailings']) &&
568 isset($fields['excludeMailings']) &&
569 is_array($fields['excludeMailings'])
570 ) {
571 $checkMailings = array();
572 $checkMailings = array_intersect($fields['includeMailings'], $fields['excludeMailings']);
573 if (!empty($checkMailings)) {
574 $errors['excludeMailings'] = ts('Cannot have same mail in Include mailing(s) and Exclude mailing(s).');
575 }
576 }
577
578 if (!empty($fields['search_id']) &&
579 empty($fields['group_id'])
580 ) {
581 $errors['group_id'] = ts('You must select a group to filter on');
582 }
583
584 if (empty($fields['search_id']) &&
585 !empty($fields['group_id'])
586 ) {
587 $errors['search_id'] = ts('You must select a search to filter');
588 }
589
590 if (!empty($fields['location_type_id'])) {
591 if ($fields['email_selection_method'] == 'automatic') {
592 $errors['location_type_id'] = ts("If 'Email Selection Method' is automatic, you are not allowed to choose any 'Location Type'");
593 }
594 }
595 elseif ($fields['email_selection_method'] != 'automatic') {
596 $errors['email_selection_method'] = ts("If 'Location Type' is not selected, you must set the 'Email Selection Method' to automatic as well.");
597 }
598
599 return empty($errors) ? TRUE : $errors;
600 }
601 }