3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
37 * This class if for search builder processing
39 class CRM_Contact_Form_Search_Builder
extends CRM_Contact_Form_Search
{
42 * number of columns in where
50 * number of blocks to be shown
58 * Function to actually build the form
63 public function preProcess() {
64 $this->set('searchFormName', 'Builder');
66 $this->set('context', 'builder');
69 // Get the block count
70 $this->_blockCount
= $this->get('blockCount');
71 // Initialize new form
72 if (!$this->_blockCount
) {
73 $this->_blockCount
= 4;
74 $this->set('newBlock', 1);
77 //get the column count
78 $this->_columnCount
= $this->get('columnCount');
80 for ($i = 1; $i < $this->_blockCount
; $i++
) {
81 if (empty($this->_columnCount
[$i])) {
82 $this->_columnCount
[$i] = 5;
86 $this->_loadedMappingId
= $this->get('savedMapping');
88 if ($this->get('showSearchForm')) {
89 $this->assign('showSearchForm', TRUE);
92 $this->assign('showSearchForm', FALSE);
96 public function buildQuickForm() {
97 $fields = self
::fields();
98 // Get fields of type date
99 // FIXME: This is a hack until our fields contain this meta-data
100 $dateFields = array();
101 foreach ($fields as $name => $field) {
102 if (strpos($name, '_date') || CRM_Utils_Array
::value('data_type', $field) == 'Date') {
103 $dateFields[] = $name;
107 CRM_Core_Resources
::singleton()
108 ->addScriptFile('civicrm', 'templates/CRM/Contact/Form/Search/Builder.js')
110 'searchBuilder' => array(
111 // Index of newly added/expanded block (1-based index)
112 'newBlock' => $this->get('newBlock'),
113 'dateFields' => $dateFields,
114 'fieldOptions' => self
::fieldOptions(),
117 //get the saved search mapping id
120 $mappingId = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_SavedSearch', $this->_ssID
, 'mapping_id');
123 CRM_Core_BAO_Mapping
::buildMappingForm($this, 'Search Builder', $mappingId, $this->_columnCount
, $this->_blockCount
);
125 parent
::buildQuickForm();
129 * Add local and global form rules
135 function addRules() {
136 $this->addFormRule(array('CRM_Contact_Form_Search_Builder', 'formRule'), $this);
140 * global validation rules for the form
142 * @param array $fields posted values of the form
144 * @return array list of errors to be posted back to the form
148 static function formRule($values, $files, $self) {
149 if (!empty($values['addMore']) ||
!empty($values['addBlock'])) {
152 $fields = self
::fields();
153 $fld = CRM_Core_BAO_Mapping
::formattedFields($values, TRUE);
156 foreach ($fld as $k => $v) {
158 $errorMsg["operator[$v[3]][$v[4]]"] = ts("Please enter the operator.");
162 $v[2] = self
::checkArrayKeyEmpty($v[2]);
164 if (in_array($v[1], array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY')) &&
166 $errorMsg["value[$v[3]][$v[4]]"] = ts('Please clear your value if you want to use %1 operator.', array(1 => $v[1]));
168 elseif (($v[0] == 'group' ||
$v[0] == 'tag') && !empty($v[2])) {
169 $grpId = array_keys($v[2]);
171 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value.");
174 if (count($grpId) > 1) {
175 if ($v[1] != 'IN' && $v[1] != 'NOT IN') {
176 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a valid value.");
178 foreach ($grpId as $val) {
179 $error = CRM_Utils_Type
::validate($val, 'Integer', FALSE);
180 if ($error != $val) {
181 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter valid value.");
187 $error = CRM_Utils_Type
::validate($grpId[0], 'Integer', FALSE);
188 if ($error != $grpId[0]) {
189 $errorMsg["value[$v[3]][$v[4]]"] = ts('Please enter valid %1 id.', array(1 => $v[0]));
193 elseif (substr($v[0], 0, 7) === 'do_not_' or substr($v[0], 0, 3) === 'is_') {
197 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value.");
200 $error = CRM_Utils_Type
::validate($v2[0], 'Integer', FALSE);
201 if ($error != $v2[0]) {
202 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a valid value.");
206 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value.");
210 if (substr($v[0], 0, 7) == 'custom_') {
211 $type = $fields[$v[0]]['data_type'];
213 // hack to handle custom data of type state and country
214 if (in_array($type, array(
215 'Country', 'StateProvince'))) {
221 // FIXME: no idea at this point what to do with this,
222 // FIXME: but definitely needs fixing.
223 if (substr($v[0], 0, 13) == 'contribution_') {
224 $fldName = substr($v[0], 13);
227 $fldValue = CRM_Utils_Array
::value($fldName, $fields);
228 $fldType = CRM_Utils_Array
::value('type', $fldValue);
229 $type = CRM_Utils_Type
::typeToString($fldType);
230 // Check Empty values for Integer Or Boolean Or Date type For operators other than IS NULL and IS NOT NULL.
232 array('IS NULL', 'IS NOT NULL', 'IS EMPTY', 'IS NOT EMPTY'))) {
233 if ((($type == 'Int' ||
$type == 'Boolean') && !trim($v[2])) && $v[2] != '0') {
234 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value.");
236 elseif ($type == 'Date' && !trim($v[2])) {
237 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a value.");
242 if ($type && empty($errorMsg)) {
243 // check for valid format while using IN Operator
245 $inVal = trim($v[2]);
246 //checking for format to avoid db errors
247 if ($type == 'Int') {
248 if (!preg_match('/^[(]([A-Za-z0-9\,]+)[)]$/', $inVal)) {
249 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter correct Data (in valid format).");
253 if (!(substr($inVal, 0, 1) == '(' && substr($inVal, -1, 1) == ')') && !preg_match('/^[(]([A-Za-z0-9åäöÅÄÖüÜœŒæÆøØ\,\s]+)[)]$/', $inVal)) {
254 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter correct Data (in valid format).");
258 // Validate each value in parenthesis to avoid db errors
259 if (empty($errorMsg)) {
260 $parenValues = array();
261 $parenValues = explode(',', trim($inVal, "(..)"));
262 foreach ($parenValues as $val) {
264 if (!$val && $val != '0') {
265 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter the values correctly.");
267 if (empty($errorMsg)) {
268 $error = CRM_Utils_Type
::validate($val, $type, FALSE);
269 if ($error != $val) {
270 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a valid value.");
276 elseif (trim($v[2])) {
277 //else check value for rest of the Operators
278 $error = CRM_Utils_Type
::validate($v[2], $type, FALSE);
279 if ($error != $v[2]) {
280 $errorMsg["value[$v[3]][$v[4]]"] = ts("Please enter a valid value.");
288 if (!empty($errorMsg)) {
289 $self->set('showSearchForm', TRUE);
290 $self->assign('rows', NULL);
297 public function normalizeFormValues() {}
299 public function &convertFormValues(&$formValues) {
300 return CRM_Core_BAO_Mapping
::formattedFields($formValues);
303 public function &returnProperties() {
304 return CRM_Core_BAO_Mapping
::returnProperties($this->_formValues
);
308 * Process the uploaded file
313 public function postProcess() {
314 $this->set('isAdvanced', '2');
315 $this->set('isSearchBuilder', '1');
316 $this->set('showSearchForm', FALSE);
318 $params = $this->controller
->exportValues($this->_name
);
319 if (!empty($params)) {
321 if (!empty($params['addBlock'])) {
322 $this->set('newBlock', $this->_blockCount
);
323 $this->_blockCount +
= 3;
324 $this->set('blockCount', $this->_blockCount
);
325 $this->set('showSearchForm', TRUE);
329 $addMore = CRM_Utils_Array
::value('addMore', $params);
330 for ($x = 1; $x <= $this->_blockCount
; $x++
) {
331 if (!empty($addMore[$x])) {
332 $this->set('newBlock', $x);
333 $this->_columnCount
[$x] = $this->_columnCount
[$x] +
5;
334 $this->set('columnCount', $this->_columnCount
);
335 $this->set('showSearchForm', TRUE);
339 $this->set('newBlock', NULL);
341 foreach ($params['mapper'] as $key => $value) {
342 foreach ($value as $k => $v) {
350 $this->set('newBlock', 1);
351 CRM_Utils_System
::redirect(CRM_Utils_System
::url('civicrm/contact/search/builder', '_qf_Builder_display=true'));
355 // get user submitted values
356 // get it from controller only if form has been submitted, else preProcess has set this
357 if (!empty($_POST)) {
358 $this->_formValues
= $this->controller
->exportValues($this->_name
);
360 // set the group if group is submitted
361 if (!empty($this->_formValues
['uf_group_id'])) {
362 $this->set('id', $this->_formValues
['uf_group_id']);
365 $this->set('id', '');
369 // we dont want to store the sortByCharacter in the formValue, it is more like
370 // a filter on the result set
371 // this filter is reset if we click on the search button
372 if ($this->_sortByCharacter
!== NULL && empty($_POST)) {
373 if (strtolower($this->_sortByCharacter
) == 'all') {
374 $this->_formValues
['sortByCharacter'] = NULL;
377 $this->_formValues
['sortByCharacter'] = $this->_sortByCharacter
;
381 $this->_sortByCharacter
= NULL;
384 $this->_params
= &$this->convertFormValues($this->_formValues
);
385 $this->_returnProperties
= &$this->returnProperties();
387 // CRM-10338 check if value is empty array
388 foreach ($this->_params
as $k => $v) {
389 $this->_params
[$k][2] = self
::checkArrayKeyEmpty($v[2]);
392 parent
::postProcess();
395 static function fields() {
396 $fields = array_merge(
397 CRM_Contact_BAO_Contact
::exportableFields('All', FALSE, TRUE),
398 CRM_Core_Component
::getQueryFields(),
399 CRM_Contact_BAO_Query_Hook
::singleton()->getFields(),
400 CRM_Activity_BAO_Activity
::exportableFields()
406 * CRM-9434 Hackish function to fetch fields with options.
407 * FIXME: When our core fields contain reliable metadata this will be much simpler.
408 * @return array: (string => string) key: field_name value: api entity name
409 * Note: options are fetched via ajax using the api "getoptions" method
411 static function fieldOptions() {
412 // Hack to add options not retrieved by getfields
413 // This list could go on and on, but it would be better to fix getfields
415 'group' => 'group_contact',
416 'tag' => 'entity_tag',
417 'on_hold' => 'yesno',
418 'is_bulkmail' => 'yesno',
419 'payment_instrument' => 'contribution',
420 'membership_status' => 'membership',
421 'membership_type' => 'membership',
422 'member_campaign_id' => 'membership',
423 'member_is_test' => 'yesno',
424 'member_is_pay_later' => 'yesno',
425 'is_override' => 'yesno',
427 $entities = array('contact', 'address', 'activity', 'participant', 'pledge', 'member', 'contribution');
428 CRM_Contact_BAO_Query_Hook
::singleton()->alterSearchBuilderOptions($entities, $options);
429 foreach ($entities as $entity) {
430 $fields = civicrm_api3($entity, 'getfields');
431 foreach ($fields['values'] as $field => $info) {
432 if (!empty($info['options']) ||
!empty($info['pseudoconstant']) ||
!empty($info['option_group_id'])) {
433 $options[$field] = $entity;
434 if (substr($field, -3) == '_id') {
435 $options[substr($field, 0, -3)] = $entity;
438 elseif (in_array(substr($field, 0, 3), array('is_', 'do_')) || CRM_Utils_Array
::value('data_type', $info) == 'Boolean') {
439 $options[$field] = 'yesno';
440 if ($entity != 'contact') {
441 $options[$entity . '_' . $field] = 'yesno';
444 elseif (strpos($field, '_is_')) {
445 $options[$field] = 'yesno';
454 * tags and groups use array keys for selection list.
455 * if using IS NULL/NOT NULL, an array with no array key is created
456 * convert that to simple null so processing can proceed
458 static function checkArrayKeyEmpty($val) {
459 if (is_array($val)) {
461 foreach ($val as $vk => $vv) {