commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / CRM / Utils / Rule.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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-2015
32 * $Id$
33 *
34 */
35
36 require_once 'HTML/QuickForm/Rule/Email.php';
37
38 /**
39 * Class CRM_Utils_Rule
40 */
41 class CRM_Utils_Rule {
42
43 /**
44 * @param $str
45 * @param int $maxLength
46 *
47 * @return bool
48 */
49 public static function title($str, $maxLength = 127) {
50
51 // check length etc
52 if (empty($str) || strlen($str) > $maxLength) {
53 return FALSE;
54 }
55
56 // Make sure it include valid characters, alpha numeric and underscores
57 if (!preg_match('/^\w[\w\s\'\&\,\$\#\-\.\"\?\!]+$/i', $str)) {
58 return FALSE;
59 }
60
61 return TRUE;
62 }
63
64 /**
65 * @param $str
66 *
67 * @return bool
68 */
69 public static function longTitle($str) {
70 return self::title($str, 255);
71 }
72
73 /**
74 * @param $str
75 *
76 * @return bool
77 */
78 public static function variable($str) {
79 // check length etc
80 if (empty($str) || strlen($str) > 31) {
81 return FALSE;
82 }
83
84 // make sure it include valid characters, alpha numeric and underscores
85 if (!preg_match('/^[\w]+$/i', $str)) {
86 return FALSE;
87 }
88
89 return TRUE;
90 }
91
92 /**
93 * Validate that a string is a valid MySQL column name or alias.
94 *
95 * @param $str
96 *
97 * @return bool
98 */
99 public static function mysqlColumnNameOrAlias($str) {
100 // Check not empty.
101 if (empty($str)) {
102 return FALSE;
103 }
104
105 // Ensure the string contains only valid characters:
106 // For column names: alphanumeric and underscores
107 // For aliases: backticks, alphanumeric hyphens and underscores.
108 if (!preg_match('/^((`[\w-]{1,64}`|[\w-]{1,64})\.)?(`[\w-]{1,64}`|[\w-]{1,64})$/i', $str)) {
109 return FALSE;
110 }
111
112 return TRUE;
113 }
114
115 /**
116 * Validate that a string is ASC or DESC.
117 *
118 * Empty string should be treated as invalid and ignored => default = ASC.
119 *
120 * @param $str
121 * @return bool
122 */
123 public static function mysqlOrderByDirection($str) {
124 if (!preg_match('/^(asc|desc)$/i', $str)) {
125 return FALSE;
126 }
127
128 return TRUE;
129 }
130
131 /**
132 * Validate that a string is valid order by clause.
133 *
134 * @param $str
135 * @return bool
136 */
137 public static function mysqlOrderBy($str) {
138 // Making a regex for a comma separated list is quite hard and not readable
139 // at all, so we split and loop over.
140 $parts = explode(',', $str);
141 foreach ($parts as $part) {
142 if (!preg_match('/^((`[\w-]{1,64}`|[\w-]{1,64})\.)?(`[\w-]{1,64}`|[\w-]{1,64})( (asc|desc))?$/i', trim($part))) {
143 return FALSE;
144 }
145 }
146
147 return TRUE;
148 }
149
150 /**
151 * @param $str
152 *
153 * @return bool
154 */
155 public static function qfVariable($str) {
156 // check length etc
157 //if ( empty( $str ) || strlen( $str ) > 31 ) {
158 if (strlen(trim($str)) == 0 || strlen($str) > 31) {
159 return FALSE;
160 }
161
162 // make sure it include valid characters, alpha numeric and underscores
163 // added (. and ,) option (CRM-1336)
164 if (!preg_match('/^[\w\s\.\,]+$/i', $str)) {
165 return FALSE;
166 }
167
168 return TRUE;
169 }
170
171 /**
172 * @param $phone
173 *
174 * @return bool
175 */
176 public static function phone($phone) {
177 // check length etc
178 if (empty($phone) || strlen($phone) > 16) {
179 return FALSE;
180 }
181
182 // make sure it include valid characters, (, \s and numeric
183 if (preg_match('/^[\d\(\)\-\.\s]+$/', $phone)) {
184 return TRUE;
185 }
186 return FALSE;
187 }
188
189 /**
190 * @param $query
191 *
192 * @return bool
193 */
194 public static function query($query) {
195 // check length etc
196 if (empty($query) || strlen($query) < 3 || strlen($query) > 127) {
197 return FALSE;
198 }
199
200 // make sure it include valid characters, alpha numeric and underscores
201 if (!preg_match('/^[\w\s\%\'\&\,\$\#]+$/i', $query)) {
202 return FALSE;
203 }
204
205 return TRUE;
206 }
207
208 /**
209 * @param $url
210 *
211 * @return bool
212 */
213 public static function url($url) {
214 if (preg_match('/^\//', $url)) {
215 // allow relative URL's (CRM-15598)
216 $url = 'http://' . $_SERVER['HTTP_HOST'] . $url;
217 }
218 return (bool) filter_var($url, FILTER_VALIDATE_URL);
219 }
220
221 /**
222 * @param $string
223 *
224 * @return bool
225 */
226 public static function wikiURL($string) {
227 $items = explode(' ', trim($string), 2);
228 return self::url($items[0]);
229 }
230
231 /**
232 * @param $domain
233 *
234 * @return bool
235 */
236 public static function domain($domain) {
237 // not perfect, but better than the previous one; see CRM-1502
238 if (!preg_match('/^[A-Za-z0-9]([A-Za-z0-9\.\-]*[A-Za-z0-9])?$/', $domain)) {
239 return FALSE;
240 }
241 return TRUE;
242 }
243
244 /**
245 * @param $value
246 * @param null $default
247 *
248 * @return null
249 */
250 public static function date($value, $default = NULL) {
251 if (is_string($value) &&
252 preg_match('/^\d\d\d\d-?\d\d-?\d\d$/', $value)
253 ) {
254 return $value;
255 }
256 return $default;
257 }
258
259 /**
260 * @param $value
261 * @param null $default
262 *
263 * @return null|string
264 */
265 public static function dateTime($value, $default = NULL) {
266 $result = $default;
267 if (is_string($value) &&
268 preg_match('/^\d\d\d\d-?\d\d-?\d\d(\s\d\d:\d\d(:\d\d)?|\d\d\d\d(\d\d)?)?$/', $value)
269 ) {
270 $result = $value;
271 }
272
273 return $result;
274 }
275
276 /**
277 * Check the validity of the date (in qf format)
278 * note that only a year is valid, or a mon-year is
279 * also valid in addition to day-mon-year. The date
280 * specified has to be beyond today. (i.e today or later)
281 *
282 * @param array $date
283 * @param bool $monthRequired
284 * Check whether month is mandatory.
285 *
286 * @return bool
287 * true if valid date
288 */
289 public static function currentDate($date, $monthRequired = TRUE) {
290 $config = CRM_Core_Config::singleton();
291
292 $d = CRM_Utils_Array::value('d', $date);
293 $m = CRM_Utils_Array::value('M', $date);
294 $y = CRM_Utils_Array::value('Y', $date);
295
296 if (!$d && !$m && !$y) {
297 return TRUE;
298 }
299
300 // CRM-9017 CiviContribute/CiviMember form with expiration date format 'm Y'
301 if (!$m && !empty($date['m'])) {
302 $m = CRM_Utils_Array::value('m', $date);
303 }
304
305 $day = $mon = 1;
306 $year = 0;
307 if ($d) {
308 $day = $d;
309 }
310 if ($m) {
311 $mon = $m;
312 }
313 if ($y) {
314 $year = $y;
315 }
316
317 // if we have day we need mon, and if we have mon we need year
318 if (($d && !$m) ||
319 ($d && !$y) ||
320 ($m && !$y)
321 ) {
322 return FALSE;
323 }
324
325 $result = FALSE;
326 if (!empty($day) || !empty($mon) || !empty($year)) {
327 $result = checkdate($mon, $day, $year);
328 }
329
330 if (!$result) {
331 return FALSE;
332 }
333
334 // ensure we have month if required
335 if ($monthRequired && !$m) {
336 return FALSE;
337 }
338
339 // now make sure this date is greater that today
340 $currentDate = getdate();
341 if ($year > $currentDate['year']) {
342 return TRUE;
343 }
344 elseif ($year < $currentDate['year']) {
345 return FALSE;
346 }
347
348 if ($m) {
349 if ($mon > $currentDate['mon']) {
350 return TRUE;
351 }
352 elseif ($mon < $currentDate['mon']) {
353 return FALSE;
354 }
355 }
356
357 if ($d) {
358 if ($day > $currentDate['mday']) {
359 return TRUE;
360 }
361 elseif ($day < $currentDate['mday']) {
362 return FALSE;
363 }
364 }
365
366 return TRUE;
367 }
368
369 /**
370 * Check the validity of a date or datetime (timestamp)
371 * value which is in YYYYMMDD or YYYYMMDDHHMMSS format
372 *
373 * Uses PHP checkdate() - params are ( int $month, int $day, int $year )
374 *
375 * @param string $date
376 *
377 * @return bool
378 * true if valid date
379 */
380 public static function mysqlDate($date) {
381 // allow date to be null
382 if ($date == NULL) {
383 return TRUE;
384 }
385
386 if (checkdate(substr($date, 4, 2), substr($date, 6, 2), substr($date, 0, 4))) {
387 return TRUE;
388 }
389
390 return FALSE;
391 }
392
393 /**
394 * @param $value
395 *
396 * @return bool
397 */
398 public static function integer($value) {
399 if (is_int($value)) {
400 return TRUE;
401 }
402
403 // CRM-13460
404 // ensure number passed is always a string numeral
405 if (!is_numeric($value)) {
406 return FALSE;
407 }
408
409 // note that is_int matches only integer type
410 // and not strings which are only integers
411 // hence we do this here
412 if (preg_match('/^\d+$/', $value)) {
413 return TRUE;
414 }
415
416 if ($value < 0) {
417 $negValue = -1 * $value;
418 if (is_int($negValue)) {
419 return TRUE;
420 }
421 }
422
423 return FALSE;
424 }
425
426 /**
427 * @param $value
428 *
429 * @return bool
430 */
431 public static function positiveInteger($value) {
432 if (is_int($value)) {
433 return ($value < 0) ? FALSE : TRUE;
434 }
435
436 // CRM-13460
437 // ensure number passed is always a string numeral
438 if (!is_numeric($value)) {
439 return FALSE;
440 }
441
442 if (preg_match('/^\d+$/', $value)) {
443 return TRUE;
444 }
445
446 return FALSE;
447 }
448
449 /**
450 * @param $value
451 *
452 * @return bool
453 */
454 public static function numeric($value) {
455 // lets use a php gatekeeper to ensure this is numeric
456 if (!is_numeric($value)) {
457 return FALSE;
458 }
459
460 return preg_match('/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/', $value) ? TRUE : FALSE;
461 }
462
463 /**
464 * @param $value
465 * @param $noOfDigit
466 *
467 * @return bool
468 */
469 public static function numberOfDigit($value, $noOfDigit) {
470 return preg_match('/^\d{' . $noOfDigit . '}$/', $value) ? TRUE : FALSE;
471 }
472
473 /**
474 * @param $value
475 *
476 * @return mixed
477 */
478 public static function cleanMoney($value) {
479 // first remove all white space
480 $value = str_replace(array(' ', "\t", "\n"), '', $value);
481
482 $config = CRM_Core_Config::singleton();
483
484 //CRM-14868
485 $currencySymbols = CRM_Core_PseudoConstant::get(
486 'CRM_Contribute_DAO_Contribution',
487 'currency', array(
488 'keyColumn' => 'name',
489 'labelColumn' => 'symbol',
490 )
491 );
492 $value = str_replace($currencySymbols, '', $value);
493
494 if ($config->monetaryThousandSeparator) {
495 $mon_thousands_sep = $config->monetaryThousandSeparator;
496 }
497 else {
498 $mon_thousands_sep = ',';
499 }
500
501 // ugly fix for CRM-6391: do not drop the thousand separator if
502 // it looks like it’s separating decimal part (because a given
503 // value undergoes a second cleanMoney() call, for example)
504 // CRM-15835 - in case the amount/value contains 0 after decimal
505 // eg 150.5 the following if condition will pass
506 if ($mon_thousands_sep != '.' or (substr($value, -3, 1) != '.' && substr($value, -2, 1) != '.')) {
507 $value = str_replace($mon_thousands_sep, '', $value);
508 }
509
510 if ($config->monetaryDecimalPoint) {
511 $mon_decimal_point = $config->monetaryDecimalPoint;
512 }
513 else {
514 $mon_decimal_point = '.';
515 }
516 $value = str_replace($mon_decimal_point, '.', $value);
517
518 return $value;
519 }
520
521 /**
522 * @param $value
523 *
524 * @return bool
525 */
526 public static function money($value) {
527 $config = CRM_Core_Config::singleton();
528
529 //only edge case when we have a decimal point in the input money
530 //field and not defined in the decimal Point in config settings
531 if ($config->monetaryDecimalPoint &&
532 $config->monetaryDecimalPoint != '.' &&
533 /* CRM-7122 also check for Thousands Separator in config settings */
534 $config->monetaryThousandSeparator != '.' &&
535 substr_count($value, '.')
536 ) {
537 return FALSE;
538 }
539
540 $value = self::cleanMoney($value);
541
542 if (self::integer($value)) {
543 return TRUE;
544 }
545
546 return preg_match('/(^-?\d+\.\d?\d?$)|(^-?\.\d\d?$)/', $value) ? TRUE : FALSE;
547 }
548
549 /**
550 * @param $value
551 * @param int $maxLength
552 *
553 * @return bool
554 */
555 public static function string($value, $maxLength = 0) {
556 if (is_string($value) &&
557 ($maxLength === 0 || strlen($value) <= $maxLength)
558 ) {
559 return TRUE;
560 }
561 return FALSE;
562 }
563
564 /**
565 * @param $value
566 *
567 * @return bool
568 */
569 public static function boolean($value) {
570 return preg_match(
571 '/(^(1|0)$)|(^(Y(es)?|N(o)?)$)|(^(T(rue)?|F(alse)?)$)/i', $value
572 ) ? TRUE : FALSE;
573 }
574
575 /**
576 * @param $value
577 *
578 * @return bool
579 */
580 public static function email($value) {
581 return (bool) filter_var($value, FILTER_VALIDATE_EMAIL);
582 }
583
584 /**
585 * @param $list
586 *
587 * @return bool
588 */
589 public static function emailList($list) {
590 $emails = explode(',', $list);
591 foreach ($emails as $email) {
592 $email = trim($email);
593 if (!self::email($email)) {
594 return FALSE;
595 }
596 }
597 return TRUE;
598 }
599
600 /**
601 * allow between 4-6 digits as postal code since india needs 6 and US needs 5 (or
602 * if u disregard the first 0, 4 (thanx excel!)
603 * FIXME: we need to figure out how to localize such rules
604 * @param $value
605 *
606 * @return bool
607 */
608 public static function postalCode($value) {
609 if (preg_match('/^\d{4,6}(-\d{4})?$/', $value)) {
610 return TRUE;
611 }
612 return FALSE;
613 }
614
615 /**
616 * See how file rules are written in HTML/QuickForm/file.php
617 * Checks to make sure the uploaded file is ascii
618 *
619 * @return bool
620 * true if file has been uploaded, false otherwise
621 */
622 public static function asciiFile($elementValue) {
623 if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
624 (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
625 ) {
626 return CRM_Utils_File::isAscii($elementValue['tmp_name']);
627 }
628 return FALSE;
629 }
630
631 /**
632 * Checks to make sure the uploaded file is in UTF-8, recodes if it's not
633 *
634 * @return bool
635 * whether file has been uploaded properly and is now in UTF-8
636 */
637 public static function utf8File($elementValue) {
638 $success = FALSE;
639
640 if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
641 (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
642 ) {
643
644 $success = CRM_Utils_File::isAscii($elementValue['tmp_name']);
645
646 // if it's a file, but not UTF-8, let's try and recode it
647 // and then make sure it's an UTF-8 file in the end
648 if (!$success) {
649 $success = CRM_Utils_File::toUtf8($elementValue['tmp_name']);
650 if ($success) {
651 $success = CRM_Utils_File::isAscii($elementValue['tmp_name']);
652 }
653 }
654 }
655 return $success;
656 }
657
658 /**
659 * See how file rules are written in HTML/QuickForm/file.php
660 * Checks to make sure the uploaded file is html
661 *
662 * @return bool
663 * true if file has been uploaded, false otherwise
664 */
665 public static function htmlFile($elementValue) {
666 if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
667 (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')
668 ) {
669 return CRM_Utils_File::isHtmlFile($elementValue['tmp_name']);
670 }
671 return FALSE;
672 }
673
674 /**
675 * Check if there is a record with the same name in the db.
676 *
677 * @param string $value
678 * The value of the field we are checking.
679 * @param array $options
680 * The daoName and fieldName (optional ).
681 *
682 * @return bool
683 * true if object exists
684 */
685 public static function objectExists($value, $options) {
686 $name = 'name';
687 if (isset($options[2])) {
688 $name = $options[2];
689 }
690
691 return CRM_Core_DAO::objectExists($value, CRM_Utils_Array::value(0, $options), CRM_Utils_Array::value(1, $options), CRM_Utils_Array::value(2, $options, $name));
692 }
693
694 /**
695 * @param $value
696 * @param $options
697 *
698 * @return bool
699 */
700 public static function optionExists($value, $options) {
701 return CRM_Core_OptionValue::optionExists($value, $options[0], $options[1], $options[2], CRM_Utils_Array::value(3, $options, 'name'));
702 }
703
704 /**
705 * @param $value
706 * @param $type
707 *
708 * @return bool
709 */
710 public static function creditCardNumber($value, $type) {
711 require_once 'Validate/Finance/CreditCard.php';
712 return Validate_Finance_CreditCard::number($value, $type);
713 }
714
715 /**
716 * @param $value
717 * @param $type
718 *
719 * @return bool
720 */
721 public static function cvv($value, $type) {
722 require_once 'Validate/Finance/CreditCard.php';
723
724 return Validate_Finance_CreditCard::cvv($value, $type);
725 }
726
727 /**
728 * @param $value
729 *
730 * @return bool
731 */
732 public static function currencyCode($value) {
733 static $currencyCodes = NULL;
734 if (!$currencyCodes) {
735 $currencyCodes = CRM_Core_PseudoConstant::currencyCode();
736 }
737 if (in_array($value, $currencyCodes)) {
738 return TRUE;
739 }
740 return FALSE;
741 }
742
743 /**
744 * @param $value
745 *
746 * @return bool
747 */
748 public static function xssString($value) {
749 if (is_string($value)) {
750 return preg_match('!<(vb)?script[^>]*>.*</(vb)?script.*>!ims',
751 $value
752 ) ? FALSE : TRUE;
753 }
754 else {
755 return TRUE;
756 }
757 }
758
759 /**
760 * @param $path
761 *
762 * @return bool
763 */
764 public static function fileExists($path) {
765 return file_exists($path);
766 }
767
768 /**
769 * @param $value
770 * @param $options
771 *
772 * @return bool
773 */
774 public static function autocomplete($value, $options) {
775 if ($value) {
776 $selectOption = CRM_Core_BAO_CustomOption::valuesByID($options['fieldID'], $options['optionGroupID']);
777
778 if (!in_array($value, $selectOption)) {
779 return FALSE;
780 }
781 }
782 return TRUE;
783 }
784
785 /**
786 * @param $value
787 * @param null $actualElementValue
788 *
789 * @return bool
790 */
791 public static function validContact($value, $actualElementValue = NULL) {
792 if ($actualElementValue) {
793 $value = $actualElementValue;
794 }
795
796 return CRM_Utils_Rule::positiveInteger($value);
797 }
798
799 /**
800 * Check the validity of the date (in qf format)
801 * note that only a year is valid, or a mon-year is
802 * also valid in addition to day-mon-year
803 *
804 * @param array $date
805 *
806 * @return bool
807 * true if valid date
808 */
809 public static function qfDate($date) {
810 $config = CRM_Core_Config::singleton();
811
812 $d = CRM_Utils_Array::value('d', $date);
813 $m = CRM_Utils_Array::value('M', $date);
814 $y = CRM_Utils_Array::value('Y', $date);
815 if (isset($date['h']) ||
816 isset($date['g'])
817 ) {
818 $m = CRM_Utils_Array::value('M', $date);
819 }
820
821 if (!$d && !$m && !$y) {
822 return TRUE;
823 }
824
825 $day = $mon = 1;
826 $year = 0;
827 if ($d) {
828 $day = $d;
829 }
830 if ($m) {
831 $mon = $m;
832 }
833 if ($y) {
834 $year = $y;
835 }
836
837 // if we have day we need mon, and if we have mon we need year
838 if (($d && !$m) ||
839 ($d && !$y) ||
840 ($m && !$y)
841 ) {
842 return FALSE;
843 }
844
845 if (!empty($day) || !empty($mon) || !empty($year)) {
846 return checkdate($mon, $day, $year);
847 }
848 return FALSE;
849 }
850
851 /**
852 * @param $key
853 *
854 * @return bool
855 */
856 public static function qfKey($key) {
857 return ($key) ? CRM_Core_Key::valid($key) : FALSE;
858 }
859
860 }