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