INFRA-132 - Docblock formatting fixes
[civicrm-core.git] / CRM / Custom / 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 * form to process actions on the set aspect of Custom Data
38 */
39 class CRM_Custom_Form_Group extends CRM_Core_Form {
40
41 /**
42 * The set id saved to the session for an update
43 *
44 * @var int
45 */
46 protected $_id;
47
48 /**
49 * set is empty or not
50 *
51 * @var bool
52 */
53 protected $_isGroupEmpty = TRUE;
54
55 /**
56 * Array of existing subtypes set for a custom set
57 *
58 * @var array
59 */
60 protected $_subtypes = array();
61
62 /**
63 * Array of default params
64 *
65 * @var array
66 */
67 protected $_defaults = array();
68
69 /**
70 * Set variables up before form is built
71 *
72 * @param null
73 *
74 * @return void
75 */
76 public function preProcess() {
77 // current set id
78 $this->_id = $this->get('id');
79
80 if ($this->_id && $isReserved = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_id, 'is_reserved', 'id')) {
81 CRM_Core_Error::fatal("You cannot edit the settings of a reserved custom field-set.");
82 }
83 // setting title for html page
84 if ($this->_action == CRM_Core_Action::UPDATE) {
85 $title = CRM_Core_BAO_CustomGroup::getTitle($this->_id);
86 CRM_Utils_System::setTitle(ts('Edit %1', array(1 => $title)));
87 }
88 elseif ($this->_action == CRM_Core_Action::VIEW) {
89 $title = CRM_Core_BAO_CustomGroup::getTitle($this->_id);
90 CRM_Utils_System::setTitle(ts('Preview %1', array(1 => $title)));
91 }
92 else {
93 CRM_Utils_System::setTitle(ts('New Custom Field Set'));
94 }
95
96 if (isset($this->_id)) {
97 $params = array('id' => $this->_id);
98 CRM_Core_BAO_CustomGroup::retrieve($params, $this->_defaults);
99
100 $subExtends = CRM_Utils_Array::value('extends_entity_column_value', $this->_defaults);
101 if (!empty($subExtends)) {
102 $this->_subtypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, substr($subExtends, 1, -1));
103 }
104 }
105 }
106
107 /**
108 * Global form rule
109 *
110 * @param array $fields
111 * The input form values.
112 * @param array $files
113 * The uploaded files if any.
114 * @param $self
115 *
116 *
117 * @return bool|array
118 * true if no errors, else array of errors
119 * @static
120 */
121 public static function formRule($fields, $files, $self) {
122 $errors = array();
123
124 //validate group title as well as name.
125 $title = $fields['title'];
126 $name = CRM_Utils_String::munge($title, '_', 64);
127 $query = 'select count(*) from civicrm_custom_group where ( name like %1 OR title like %2 ) and id != %3';
128 $grpCnt = CRM_Core_DAO::singleValueQuery($query, array(
129 1 => array($name, 'String'),
130 2 => array($title, 'String'),
131 3 => array((int) $self->_id, 'Integer'),
132 ));
133 if ($grpCnt) {
134 $errors['title'] = ts('Custom group \'%1\' already exists in Database.', array(1 => $title));
135 }
136
137 if (!empty($fields['extends'][1])) {
138 if (in_array('', $fields['extends'][1]) && count($fields['extends'][1]) > 1) {
139 $errors['extends'] = ts("Cannot combine other option with 'Any'.");
140 }
141 }
142
143 if (empty($fields['extends'][0])) {
144 $errors['extends'] = ts("You need to select the type of record that this set of custom fields is applicable for.");
145 }
146
147 $extends = array('Activity', 'Relationship', 'Group', 'Contribution', 'Membership', 'Event', 'Participant');
148 if (in_array($fields['extends'][0], $extends) && $fields['style'] == 'Tab') {
149 $errors['style'] = ts("Display Style should be Inline for this Class");
150 $self->assign('showStyle', TRUE);
151 }
152
153 if (!empty($fields['is_multiple'])) {
154 $self->assign('showMultiple', TRUE);
155 }
156
157 if (empty($fields['is_multiple']) && $fields['style'] == 'Tab with table') {
158 $errors['style'] = ts("Display Style 'Tab with table' is only supported for multiple-record custom field sets.");
159 }
160
161 //checks the given custom set doesnot start with digit
162 $title = $fields['title'];
163 if (!empty($title)) {
164 // gives the ascii value
165 $asciiValue = ord($title{0});
166 if ($asciiValue >= 48 && $asciiValue <= 57) {
167 $errors['title'] = ts("Name cannot not start with a digit");
168 }
169 }
170
171 return empty($errors) ? TRUE : $errors;
172 }
173
174 /**
175 * add the rules (mainly global rules) for form.
176 * All local rules are added near the element
177 *
178 * @param null
179 *
180 * @return void
181 * @see valid_date
182 */
183 public function addRules() {
184 $this->addFormRule(array('CRM_Custom_Form_Group', 'formRule'), $this);
185 }
186
187 /**
188 * Build the form object
189 *
190 * @param null
191 *
192 * @return void
193 */
194 public function buildQuickForm() {
195 $this->applyFilter('__ALL__', 'trim');
196
197 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_CustomGroup');
198
199 //title
200 $this->add('text', 'title', ts('Set Name'), $attributes['title'], TRUE);
201
202 //Fix for code alignment, CRM-3058
203 $contactTypes = array('Contact', 'Individual', 'Household', 'Organization');
204 $this->assign('contactTypes', json_encode($contactTypes));
205
206 $sel1 = array("" => "- select -") + CRM_Core_SelectValues::customGroupExtends();
207 $sel2 = array();
208 $activityType = CRM_Core_PseudoConstant::activityType(FALSE, TRUE, FALSE, 'label', TRUE);
209
210 $eventType = CRM_Core_OptionGroup::values('event_type');
211 $grantType = CRM_Core_OptionGroup::values('grant_type');
212 $campaignTypes = CRM_Campaign_PseudoConstant::campaignType();
213 $membershipType = CRM_Member_BAO_MembershipType::getMembershipTypes(FALSE);
214 $participantRole = CRM_Core_OptionGroup::values('participant_role');
215 $relTypeInd = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, 'null', NULL, 'Individual');
216 $relTypeOrg = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, 'null', NULL, 'Organization');
217 $relTypeHou = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, 'null', NULL, 'Household');
218
219 ksort($sel1);
220 asort($activityType);
221 asort($eventType);
222 asort($grantType);
223 asort($membershipType);
224 asort($participantRole);
225 $allRelationshipType = array();
226 $allRelationshipType = array_merge($relTypeInd, $relTypeOrg);
227 $allRelationshipType = array_merge($allRelationshipType, $relTypeHou);
228
229 //adding subtype specific relationships CRM-5256
230 $subTypes = CRM_Contact_BAO_ContactType::subTypeInfo();
231
232 foreach ($subTypes as $subType => $val) {
233 $subTypeRelationshipTypes = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, NULL, NULL, $val['parent'],
234 FALSE, 'label', TRUE, $subType
235 );
236 $allRelationshipType = array_merge($allRelationshipType, $subTypeRelationshipTypes);
237 }
238
239 $sel2['Event'] = $eventType;
240 $sel2['Grant'] = $grantType;
241 $sel2['Activity'] = $activityType;
242 $sel2['Campaign'] = $campaignTypes;
243 $sel2['Membership'] = $membershipType;
244 $sel2['ParticipantRole'] = $participantRole;
245 $sel2['ParticipantEventName'] = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )");
246 $sel2['ParticipantEventType'] = $eventType;
247 $sel2['Contribution'] = CRM_Contribute_PseudoConstant::financialType();
248 $sel2['Relationship'] = $allRelationshipType;
249
250 $sel2['Individual'] = CRM_Contact_BAO_ContactType::subTypePairs('Individual', FALSE, NULL);
251 $sel2['Household'] = CRM_Contact_BAO_ContactType::subTypePairs('Household', FALSE, NULL);
252 $sel2['Organization'] = CRM_Contact_BAO_ContactType::subTypePairs('Organization', FALSE, NULL);
253
254 CRM_Core_BAO_CustomGroup::getExtendedObjectTypes($sel2);
255
256 foreach ($sel2 as $main => $sub) {
257 if (!empty($sel2[$main])) {
258 if ($main == 'Relationship') {
259 $relName = self::getFormattedList($sel2[$main]);
260 $sel2[$main] = array(
261 '' => ts("- Any -")
262 ) + $relName;
263 }
264 else {
265 $sel2[$main] = array(
266 '' => ts("- Any -")
267 ) + $sel2[$main];
268 }
269 }
270 }
271
272 $cSubTypes = CRM_Core_Component::contactSubTypes();
273
274 if (!empty($cSubTypes)) {
275 $contactSubTypes = array();
276 foreach ($cSubTypes as $key => $value) {
277 $contactSubTypes[$key] = $key;
278 }
279 $sel2['Contact'] = array(
280 "" => "-- Any --"
281 ) + $contactSubTypes;
282 }
283 else {
284 if (!isset($this->_id)) {
285 $formName = 'document.forms.' . $this->_name;
286
287 $js = "<script type='text/javascript'>\n";
288 $js .= "{$formName}['extends_1'].style.display = 'none';\n";
289 $js .= "</script>";
290 $this->assign('initHideBlocks', $js);
291 }
292 }
293
294 $sel = &$this->add('hierselect',
295 'extends',
296 ts('Used For'),
297 array(
298 'name' => 'extends[0]',
299 'style' => 'vertical-align: top;',
300 ),
301 TRUE
302 );
303 $sel->setOptions(array($sel1, $sel2));
304 if (is_a($sel->_elements[1], 'HTML_QuickForm_select')) {
305 // make second selector a multi-select -
306 $sel->_elements[1]->setMultiple(TRUE);
307 $sel->_elements[1]->setSize(5);
308 }
309 if ($this->_action == CRM_Core_Action::UPDATE) {
310 $subName = CRM_Utils_Array::value('extends_entity_column_id', $this->_defaults);
311 if ($this->_defaults['extends'] == 'Participant') {
312 if ($subName == 1) {
313 $this->_defaults['extends'] = 'ParticipantRole';
314 }
315 elseif ($subName == 2) {
316 $this->_defaults['extends'] = 'ParticipantEventName';
317 }
318 elseif ($subName == 3) {
319 $this->_defaults['extends'] = 'ParticipantEventType';
320 }
321 }
322
323 //allow to edit settings if custom set is empty CRM-5258
324 $this->_isGroupEmpty = CRM_Core_BAO_CustomGroup::isGroupEmpty($this->_id);
325 if (!$this->_isGroupEmpty) {
326 if (!empty($this->_subtypes)) {
327 // we want to allow adding / updating subtypes for this case,
328 // and therefore freeze the first selector only.
329 $sel->_elements[0]->freeze();
330 }
331 else {
332 // freeze both the selectors
333 $sel->freeze();
334 }
335 }
336 $this->assign('isCustomGroupEmpty', $this->_isGroupEmpty);
337 $this->assign('gid', $this->_id);
338 }
339 $this->assign('defaultSubtypes', json_encode($this->_subtypes));
340
341 // help text
342 $this->addWysiwyg('help_pre', ts('Pre-form Help'), $attributes['help_pre']);
343 $this->addWysiwyg('help_post', ts('Post-form Help'), $attributes['help_post']);
344
345 // weight
346 $this->add('text', 'weight', ts('Order'), $attributes['weight'], TRUE);
347 $this->addRule('weight', ts('is a numeric field'), 'numeric');
348
349 // display style
350 $this->add('select', 'style', ts('Display Style'), CRM_Core_SelectValues::customGroupStyle());
351
352 // is this set collapsed or expanded ?
353 $this->addElement('checkbox', 'collapse_display', ts('Collapse this set on initial display'));
354
355 // is this set collapsed or expanded ? in advanced search
356 $this->addElement('checkbox', 'collapse_adv_display', ts('Collapse this set in Advanced Search'));
357
358 // is this set active ?
359 $this->addElement('checkbox', 'is_active', ts('Is this Custom Data Set active?'));
360
361 // does this set have multiple record?
362 $multiple = $this->addElement('checkbox', 'is_multiple',
363 ts('Does this Custom Field Set allow multiple records?'), NULL);
364
365 // $min_multiple = $this->add('text', 'min_multiple', ts('Minimum number of multiple records'), $attributes['min_multiple'] );
366 // $this->addRule('min_multiple', ts('is a numeric field') , 'numeric');
367
368 $max_multiple = $this->add('text', 'max_multiple', ts('Maximum number of multiple records'), $attributes['max_multiple']);
369 $this->addRule('max_multiple', ts('is a numeric field'), 'numeric');
370
371 //allow to edit settings if custom set is empty CRM-5258
372 $this->assign('isGroupEmpty', $this->_isGroupEmpty);
373 if (!$this->_isGroupEmpty) {
374 $multiple->freeze();
375 //$min_multiple->freeze();
376 $max_multiple->freeze();
377 }
378
379 $this->assign('showStyle', FALSE);
380 $this->assign('showMultiple', FALSE);
381 $buttons = array(
382 array(
383 'type' => 'next',
384 'name' => ts('Save'),
385 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
386 'isDefault' => TRUE,
387 ),
388 array(
389 'type' => 'cancel',
390 'name' => ts('Cancel'),
391 ),
392 );
393 if (!$this->_isGroupEmpty && !empty($this->_subtypes)) {
394 $buttons[0]['js'] = array('onclick' => "return warnDataLoss()");
395 }
396 $this->addButtons($buttons);
397
398 // views are implemented as frozen form
399 if ($this->_action & CRM_Core_Action::VIEW) {
400 $this->freeze();
401 $this->addElement('button', 'done', ts('Done'), array('onclick' => "location.href='civicrm/admin/custom/group?reset=1&action=browse'"));
402 }
403 }
404
405 /**
406 * Set default values for the form. Note that in edit/view mode
407 * the default values are retrieved from the database
408 *
409 * @param null
410 *
411 * @return array
412 * array of default values
413 */
414 public function setDefaultValues() {
415 $defaults = &$this->_defaults;
416 $this->assign('showMaxMultiple', TRUE);
417 if ($this->_action == CRM_Core_Action::ADD) {
418 $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_CustomGroup');
419
420 $defaults['is_multiple'] = $defaults['min_multiple'] = 0;
421 $defaults['is_active'] = $defaults['collapse_display'] = 1;
422 $defaults['style'] = 'Inline';
423 }
424 elseif (empty($defaults['max_multiple']) && !$this->_isGroupEmpty) {
425 $this->assign('showMaxMultiple', FALSE);
426 }
427
428 if (($this->_action & CRM_Core_Action::UPDATE) && !empty($defaults['is_multiple'])) {
429 $defaults['collapse_display'] = 0;
430 }
431
432 if (isset($defaults['extends'])) {
433 $extends = $defaults['extends'];
434 unset($defaults['extends']);
435
436 $defaults['extends'][0] = $extends;
437
438 if (!empty($this->_subtypes)) {
439 $defaults['extends'][1] = $this->_subtypes;
440 }
441 else {
442 $defaults['extends'][1] = array(0 => '');
443 }
444
445 if ($extends == 'Relationship' && !empty($this->_subtypes)) {
446 $relationshipDefaults = array();
447 foreach ($defaults['extends'][1] as $donCare => $rel_type_id) {
448 $relationshipDefaults[] = $rel_type_id;
449 }
450
451 $defaults['extends'][1] = $relationshipDefaults;
452 }
453 }
454
455 return $defaults;
456 }
457
458 /**
459 * Process the form
460 *
461 * @param null
462 *
463 * @return void
464 */
465 public function postProcess() {
466 // get the submitted form values.
467 $params = $this->controller->exportValues('Group');
468 $params['overrideFKConstraint'] = 0;
469 if ($this->_action & CRM_Core_Action::UPDATE) {
470 $params['id'] = $this->_id;
471 if ($this->_defaults['extends'][0] != $params['extends'][0]) {
472 $params['overrideFKConstraint'] = 1;
473 }
474
475 if (!empty($this->_subtypes)) {
476 $subtypesToBeRemoved = array_diff($this->_subtypes, array_intersect($this->_subtypes, $params['extends'][1]));
477 CRM_Contact_BAO_ContactType::deleteCustomRowsOfSubtype($this->_id, $subtypesToBeRemoved);
478 }
479 }
480 elseif ($this->_action & CRM_Core_Action::ADD) {
481 //new custom set , so lets set the created_id
482 $session = CRM_Core_Session::singleton();
483 $params['created_id'] = $session->get('userID');
484 $params['created_date'] = date('YmdHis');
485 }
486
487 $group = CRM_Core_BAO_CustomGroup::create($params);
488
489 // reset the cache
490 CRM_Core_BAO_Cache::deleteGroup('contact fields');
491
492 if ($this->_action & CRM_Core_Action::UPDATE) {
493 CRM_Core_Session::setStatus(ts('Your custom field set \'%1 \' has been saved.', array(1 => $group->title)), ts('Saved'), 'success');
494 }
495 else {
496 // Jump directly to adding a field if popups are disabled
497 $action = CRM_Core_Resources::singleton()->ajaxPopupsEnabled ? '' : '/add';
498 $url = CRM_Utils_System::url("civicrm/admin/custom/group/field$action", 'reset=1&new=1&gid=' . $group->id . '&action=' . ($action ? 'add' : 'browse'));
499 CRM_Core_Session::setStatus(ts("Your custom field set '%1' has been added. You can add custom fields now.",
500 array(1 => $group->title)
501 ), ts('Saved'), 'success');
502 $session = CRM_Core_Session::singleton();
503 $session->replaceUserContext($url);
504 }
505
506 // prompt Drupal Views users to update $db_prefix in settings.php, if necessary
507 global $db_prefix;
508 $config = CRM_Core_Config::singleton();
509 if (is_array($db_prefix) && $config->userSystem->is_drupal && module_exists('views')) {
510 // get table_name for each custom group
511 $tables = array();
512 $sql = "SELECT table_name FROM civicrm_custom_group WHERE is_active = 1";
513 $result = CRM_Core_DAO::executeQuery($sql);
514 while ($result->fetch()) {
515 $tables[$result->table_name] = $result->table_name;
516 }
517
518 // find out which tables are missing from the $db_prefix array
519 $missingTableNames = array_diff_key($tables, $db_prefix);
520
521 if (!empty($missingTableNames)) {
522 CRM_Core_Session::setStatus(ts("To ensure that all of your custom data groups are available to Views, you may need to add the following key(s) to the db_prefix array in your settings.php file: '%1'.",
523 array(1 => implode(', ', $missingTableNames))
524 ), ts('Note'), 'info');
525 }
526 }
527 }
528
529 /**
530 * Return a formatted list of relationship name.
531 *
532 * @param array $list
533 * Array of relationship name.
534 *
535 * @return array
536 * Array of relationship name.
537 */
538 public static function getFormattedList(&$list) {
539 $relName = array();
540
541 foreach ($list as $listItemKey => $itemValue) {
542 // Extract the relationship ID.
543 $key = substr($listItemKey, 0, strpos($listItemKey, '_'));
544 if (isset($list["{$key}_b_a"])) {
545 $relName["$key"] = $list["{$key}_a_b"];
546 // Are the two labels different?
547 if ($list["{$key}_a_b"] != $list["{$key}_b_a"]) {
548 $relName["$key"] = $list["{$key}_a_b"] . ' / ' . $list["{$key}_b_a"];
549 }
550 unset($list["{$key}_b_a"]);
551 unset($list["{$key}_a_b"]);
552 }
553 else {
554 // If no '_b_a' label exists save the '_a_b' one and unset it from the list
555 $relName["{$key}"] = $list["{$key}_a_b"];
556 unset($list["{$key}_a_b"]);
557 }
558 }
559 return $relName;
560 }
561 }