Update version numbers to 4.4 and lint php
[civicrm-core.git] / CRM / Contact / Form / Task / Batch.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
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 * This class provides the functionality for batch profile update
38 */
39 class CRM_Contact_Form_Task_Batch extends CRM_Contact_Form_Task {
40
41 /**
42 * the title of the group
43 *
44 * @var string
45 */
46 protected $_title;
47
48 /**
49 * maximum contacts that should be allowed to update
50 *
51 */
52 protected $_maxContacts = 100;
53
54 /**
55 * maximum profile fields that will be displayed
56 *
57 */
58 protected $_maxFields = 9;
59
60 /**
61 * variable to store redirect path
62 *
63 */
64 protected $_userContext;
65
66 /**
67 * when not to reset sort_name
68 */
69 protected $_preserveDefault = TRUE;
70
71 /**
72 * the internal QF names for the state/country/county fields
73 */
74
75 protected $_stateCountryCountyFields = array();
76
77 /**
78 * build all the data structures needed to build the form
79 *
80 * @return void
81 * @access public
82 */
83 function preProcess() {
84 /*
85 * initialize the task and row fields
86 */
87 parent::preProcess();
88 }
89
90 /**
91 * Build the form
92 *
93 * @access public
94 *
95 * @return void
96 */
97 function buildQuickForm() {
98 $ufGroupId = $this->get('ufGroupId');
99
100 if (!$ufGroupId) {
101 CRM_Core_Error::fatal('ufGroupId is missing');
102 }
103 $this->_title = ts('Batch Update') . ' - ' . CRM_Core_BAO_UFGroup::getTitle($ufGroupId);
104 CRM_Utils_System::setTitle($this->_title);
105
106 $this->addDefaultButtons(ts('Save'));
107 $this->_fields = CRM_Core_BAO_UFGroup::getFields($ufGroupId, FALSE, CRM_Core_Action::VIEW);
108
109 // remove file type field and then limit fields
110 $suppressFields = FALSE;
111 $removehtmlTypes = array('File', 'Autocomplete-Select');
112 foreach ($this->_fields as $name => $field) {
113 if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name) &&
114 in_array($this->_fields[$name]['html_type'], $removehtmlTypes)
115 ) {
116 $suppressFields = TRUE;
117 unset($this->_fields[$name]);
118 }
119 }
120
121 //FIX ME: phone ext field is added at the end and it gets removed because of below code
122 //$this->_fields = array_slice($this->_fields, 0, $this->_maxFields);
123
124 $this->addButtons(array(
125 array(
126 'type' => 'submit',
127 'name' => ts('Update Contact(s)'),
128 'isDefault' => TRUE,
129 ),
130 array(
131 'type' => 'cancel',
132 'name' => ts('Cancel'),
133 ),
134 )
135 );
136
137
138 $this->assign('profileTitle', $this->_title);
139 $this->assign('componentIds', $this->_contactIds);
140
141 // if below fields are missing we should not reset sort name / display name
142 // CRM-6794
143 $preserveDefaultsArray = array(
144 'first_name', 'last_name', 'middle_name',
145 'organization_name',
146 'household_name',
147 );
148
149 $stateCountryMap = array();
150
151 foreach ($this->_contactIds as $contactId) {
152 $profileFields = $this->_fields;
153 CRM_Core_BAO_Address::checkContactSharedAddressFields($profileFields, $contactId);
154 foreach ($profileFields as $name => $field) {
155
156 // Link state to country, county to state per location per contact
157 list($prefixName, $index) = CRM_Utils_System::explode('-', $name, 2);
158 if ($prefixName == 'state_province' || $prefixName == 'country' || $prefixName == 'county') {
159 $stateCountryMap["$index-$contactId"][$prefixName] = "field_{$contactId}_{$field['name']}";
160 $this->_stateCountryCountyFields["$index-$contactId"][$prefixName] = "field[{$contactId}][{$field['name']}]";
161 }
162
163 CRM_Core_BAO_UFGroup::buildProfile($this, $field, NULL, $contactId);
164
165 if (in_array($field['name'], $preserveDefaultsArray)) {
166 $this->_preserveDefault = FALSE;
167 }
168 }
169 }
170
171 CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
172
173
174
175 $this->assign('fields', $this->_fields);
176
177 // don't set the status message when form is submitted.
178 $buttonName = $this->controller->getButtonName('submit');
179
180 if ($suppressFields && $buttonName != '_qf_BatchUpdateProfile_next') {
181 CRM_Core_Session::setStatus(ts("File or Autocomplete Select type field(s) in the selected profile are not supported for Batch Update and have been excluded."), ts('Some Fields Excluded'), 'info');
182 }
183
184 $this->addDefaultButtons(ts('Update Contacts'));
185 $this->addFormRule(array('CRM_Contact_Form_Task_Batch', 'formRule'));
186 }
187
188 /**
189 * This function sets the default values for the form.
190 *
191 * @access public
192 *
193 * @return None
194 */
195 function setDefaultValues() {
196 if (empty($this->_fields)) {
197 return;
198 }
199
200 $defaults = $sortName = array();
201 foreach ($this->_contactIds as $contactId) {
202 $details[$contactId] = array();
203
204 //build sortname
205 $sortName[$contactId] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
206 $contactId,
207 'sort_name'
208 );
209
210 CRM_Core_BAO_UFGroup::setProfileDefaults($contactId, $this->_fields, $defaults, FALSE);
211 }
212
213 $this->assign('sortName', $sortName);
214
215 // now fix all state country selectors
216 CRM_Core_BAO_Address::fixAllStateSelects($this, $defaults, $this->_stateCountryCountyFields);
217
218 return $defaults;
219 }
220
221 /**
222 * global form rule
223 *
224 * @param array $fields the input form values
225 *
226 * @return true if no errors, else array of errors
227 * @access public
228 * @static
229 */
230 static function formRule($fields) {
231 $errors = array();
232 $externalIdentifiers = array();
233 foreach ($fields['field'] as $componentId => $field) {
234 foreach ($field as $fieldName => $fieldValue) {
235 if ($fieldName == 'external_identifier') {
236 if (in_array($fieldValue, $externalIdentifiers)) {
237 $errors["field[$componentId][external_identifier]"] = ts('Duplicate value for External Identifier.');
238 }
239 else {
240 $externalIdentifiers[$componentId] = $fieldValue;
241 }
242 }
243 }
244 }
245
246 return $errors;
247 }
248
249 /**
250 * process the form after the input has been submitted and validated
251 *
252 * @access public
253 *
254 * @return None
255 */
256 public function postProcess() {
257 $params = $this->exportValues();
258
259 $ufGroupId = $this->get('ufGroupId');
260 $notify = NULL;
261 $inValidSubtypeCnt = 0;
262 //send profile notification email if 'notify' field is set
263 $notify = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $ufGroupId, 'notify');
264 foreach ($params['field'] as $key => $value) {
265
266 //CRM-5521
267 //validate subtype before updating
268 if (CRM_Utils_Array::value('contact_sub_type', $value) && !CRM_Contact_BAO_ContactType::isAllowEdit($key)) {
269 unset($value['contact_sub_type']);
270 $inValidSubtypeCnt++;
271 }
272
273 $value['preserveDBName'] = $this->_preserveDefault;
274
275 //parse street address, CRM-7768
276 self::parseStreetAddress($value, $this);
277
278 CRM_Contact_BAO_Contact::createProfileContact($value, $this->_fields, $key, NULL, $ufGroupId);
279 if ($notify) {
280 $values = CRM_Core_BAO_UFGroup::checkFieldsEmptyValues($ufGroupId, $key, NULL);
281 CRM_Core_BAO_UFGroup::commonSendMail($key, $values);
282 }
283 }
284
285 CRM_Core_Session::setStatus('', ts("Updates Saved"), 'success');
286 if ($inValidSubtypeCnt) {
287 CRM_Core_Session::setStatus(ts('Contact SubType field of 1 contact has not been updated.', array('plural' => 'Contact SubType field of %count contacts has not been updated.', 'count' => $inValidSubtypeCnt)), ts('Invalid Subtype'));
288 }
289 }
290 //end of function
291
292 /**
293 * parse street address
294 * @param array $contactValues contact values
295 * @param object $form form object
296 */
297 public static function parseStreetAddress(&$contactValues, &$form) {
298 if (!is_array($contactValues) || !is_array($form->_fields)) {
299 return;
300 }
301
302 static $parseAddress;
303 $addressFldKey = 'street_address';
304 if (!isset($parseAddress)) {
305 $parseAddress = FALSE;
306 foreach ($form->_fields as $key => $fld) {
307 if (strpos($key, $addressFldKey) !== FALSE) {
308 $parseAddress = CRM_Utils_Array::value('street_address_parsing',
309 CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
310 'address_options'
311 ),
312 FALSE
313 );
314 break;
315 }
316 }
317 }
318
319 if (!$parseAddress) {
320 return;
321 }
322
323 $allParseValues = array();
324 foreach ($contactValues as $key => $value) {
325 if (strpos($key, $addressFldKey) !== FALSE) {
326 $locTypeId = substr($key, strlen($addressFldKey) + 1);
327
328 // parse address field.
329 $parsedFields = CRM_Core_BAO_Address::parseStreetAddress($value);
330
331 //street address consider to be parsed properly,
332 //If we get street_name and street_number.
333 if (!CRM_Utils_Array::value('street_name', $parsedFields) ||
334 !CRM_Utils_Array::value('street_number', $parsedFields)
335 ) {
336 $parsedFields = array_fill_keys(array_keys($parsedFields), '');
337 }
338
339 //merge parse values.
340 foreach ($parsedFields as $fldKey => $parseVal) {
341 if ($locTypeId) {
342 $fldKey .= "-{$locTypeId}";
343 }
344 $allParseValues[$fldKey] = $parseVal;
345 }
346 }
347 }
348
349 //finally merge all parse values
350 if (!empty($allParseValues)) {
351 $contactValues += $allParseValues;
352 }
353 }
354 }
355