Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.33% covered (danger)
48.33%
246 / 509
46.67% covered (danger)
46.67%
21 / 45
CRAP
0.00% covered (danger)
0.00%
0 / 2
SeedDMS_Core_Attribute
57.38% covered (warning)
57.38%
35 / 61
46.15% covered (danger)
46.15%
6 / 13
123.51
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 setDMS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDMS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getID
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __getParsedValue
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 getValueAsArray
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getValueAsString
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 setValue
66.67% covered (warning)
66.67%
20 / 30
0.00% covered (danger)
0.00%
0 / 1
25.48
 validate
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getValidationError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setValidationError
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAttributeDefinition
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
SeedDMS_Core_AttributeDefinition
47.10% covered (danger)
47.10%
211 / 448
46.88% covered (danger)
46.88%
15 / 32
8624.19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 setDMS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDMS
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getID
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setName
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getObjType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setObjType
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setType
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getMultipleValues
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMultipleValues
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getMinValues
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMinValues
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getMaxValues
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMaxValues
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getValueSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValueSetSeparator
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 getValueSetAsArray
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getValueSetValue
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 setValueSet
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
3.03
 getRegex
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRegex
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
4.02
 isUsed
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
56
 parseValue
36.36% covered (danger)
36.36%
16 / 44
0.00% covered (danger)
0.00%
0 / 1
146.73
 createValue
76.47% covered (warning)
76.47%
13 / 17
0.00% covered (danger)
0.00%
0 / 1
25.21
 getStatistics
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 1
1406
 remove
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getObjects
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
272
 removeValue
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
272
 validate
82.86% covered (warning)
82.86%
87 / 105
0.00% covered (danger)
0.00%
0 / 1
105.10
 getValidationError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3
4/**
5 * Implementation of the attribute object in the document management system
6 *
7 * @category   DMS
8 * @package    SeedDMS_Core
9 * @license    GPL 2
10 * @version    @version@
11 * @author     Uwe Steinmann <uwe@steinmann.cx>
12 * @copyright  Copyright (C) 2012-2024 Uwe Steinmann
13 * @version    Release: @package_version@
14 */
15
16/**
17 * Class to represent an attribute in the document management system
18 *
19 * Attributes are key/value pairs which can be attachted to documents,
20 * folders and document content. The number of attributes is unlimited.
21 * Each attribute has a value and is related to an attribute definition,
22 * which holds the name and other information about the attribute.
23 *
24 * @see SeedDMS_Core_AttributeDefinition
25 *
26 * @category   DMS
27 * @package    SeedDMS_Core
28 * @author     Uwe Steinmann <uwe@steinmann.cx>
29 * @copyright  Copyright (C) 2012-2024 Uwe Steinmann
30 * @version    Release: @package_version@
31 */
32class SeedDMS_Core_Attribute { /* {{{ */
33    /**
34     * @var integer id of attribute
35     *
36     * @access protected
37     */
38    protected $_id;
39
40    /**
41     * @var SeedDMS_Core_Folder|SeedDMS_Core_Document|SeedDMS_Core_DocumentContent SeedDMS_Core_Object folder, document or document content
42     * this attribute belongs to
43     *
44     * @access protected
45     */
46    protected $_obj;
47
48    /**
49     * @var SeedDMS_Core_AttributeDefinition definition of this attribute
50     *
51     * @access protected
52     */
53    protected $_attrdef;
54
55    /**
56     * @var mixed value of this attribute
57     *
58     * @access protected
59     */
60    protected $_value;
61
62    /**
63     * @var integer validation error
64     *
65     * @access protected
66     */
67    protected $_validation_error;
68
69    /**
70     * @var SeedDMS_Core_DMS reference to the dms instance this attribute belongs to
71     *
72     * @access protected
73     */
74    protected $_dms;
75
76    /**
77     * SeedDMS_Core_Attribute constructor.
78     * @param $id
79     * @param $obj
80     * @param $attrdef
81     * @param $value
82     */
83    public function __construct($id, $obj, $attrdef, $value) { /* {{{ */
84        $this->_id = $id;
85        $this->_obj = $obj;
86        $this->_attrdef = $attrdef;
87        $this->_value = $value;
88        $this->_validation_error = 0;
89        $this->_dms = null;
90    } /* }}} */
91
92    /**
93     * Set reference to dms
94     *
95     * @param SeedDMS_Core_DMS $dms
96     */
97    public function setDMS($dms) { /* {{{ */
98        $this->_dms = $dms;
99    } /* }}} */
100
101    /**
102     * Get dms of attribute
103     *
104     * @return object $dms
105     */
106    public function getDMS() { return $this->_dms; }
107
108    /**
109     * Get internal id of attribute
110     *
111     * @return integer id
112     */
113    public function getID() { return $this->_id; }
114
115    /**
116     * Return attribute value as stored in database
117     *
118     * This function will return the value of multi value attributes
119     * including the separator char.
120     *
121     * @return string the attribute value as it is stored in the database.
122     */
123    public function getValue() { return $this->_value; }
124
125    /**
126     * Return attribute value parsed into a php type or object
127     *
128     * This function will return the value of multi value attributes
129     * including the separator char.
130     *
131     * DEPRECATED
132     *
133     * @return string the attribute value as it is stored in the database.
134     */
135    public function __getParsedValue() { /* {{{ */
136        switch ($this->_attrdef->getType()) {
137            case SeedDMS_Core_AttributeDefinition::type_float:
138                return (float) $this->_value;
139                break;
140            case SeedDMS_Core_AttributeDefinition::type_boolean:
141                return (bool) $this->_value;
142                break;
143            case SeedDMS_Core_AttributeDefinition::type_int:
144                return (int) $this->_value;
145                break;
146            default:
147                return $this->_value;
148        }
149    } /* }}} */
150
151    /**
152     * Return attribute values as an array
153     *
154     * This function returns the attribute value as an array. The array
155     * has one element for non multi value attributes and n elements for
156     * multi value attributes.
157     *
158     * @return array the attribute values
159     */
160    public function getValueAsArray() { /* {{{ */
161        if (is_array($this->_value)) {
162            return $this->_value;
163        } else {
164            return [$this->_value];
165        }
166    } /* }}} */
167
168    public function getValueAsString() { /* {{{ */
169        if (is_array($this->_value)) {
170            return implode(', ', $this->_value);
171        } else {
172            return (string) $this->_value;
173        }
174    } /* }}} */
175
176    /**
177     * Set a value of an attribute
178     *
179     * The attribute is completely deleted if the value is an empty string
180     * or empty array. An array of values is only allowed if the attribute may
181     * have multiple values. If an array is passed and the attribute may
182     * have only a single value, then the first element of the array will
183     * be taken.
184     *
185     * @param string $values value as string or array to be set
186     * @return boolean true if operation was successfull, otherwise false
187     */
188    public function setValue($values) { /* {{{*/
189        $db = $this->_dms->getDB();
190
191        /* if $values is an array but the attribute definition does not allow
192         * multi values, then the first element of the array is taken.
193         */
194        if ($values && is_array($values) && !$this->_attrdef->getMultipleValues())
195            $values = $values[0];
196
197        /* Create a value to be stored in the database */
198        $value = $this->_attrdef->createValue($values);
199
200        switch (get_class($this->_obj)) {
201            case $this->_dms->getClassname('document'):
202                if (trim($value) === '') {
203                    $queryStr = "DELETE FROM `tblDocumentAttributes` WHERE `document` = " . $this->_obj->getID() . " AND `attrdef` = " . $this->_attrdef->getId();
204                } else {
205                    $queryStr = "UPDATE `tblDocumentAttributes` SET `value` = ".$db->qstr($value)." WHERE `document` = " . $this->_obj->getID() .    " AND `attrdef` = " . $this->_attrdef->getId();
206                }
207                break;
208            case $this->_dms->getClassname('documentcontent'):
209                if (trim($value) === '') {
210                    $queryStr = "DELETE FROM `tblDocumentContentAttributes` WHERE `content` = " . $this->_obj->getID() . " AND `attrdef` = " . $this->_attrdef->getId();
211                } else {
212                    $queryStr = "UPDATE `tblDocumentContentAttributes` SET `value` = ".$db->qstr($value)." WHERE `content` = " . $this->_obj->getID() .    " AND `attrdef` = " . $this->_attrdef->getId();
213                }
214                break;
215            case $this->_dms->getClassname('folder'):
216                if (trim($value) === '') {
217                    $queryStr = "DELETE FROM `tblFolderAttributes` WHERE `folder` = " . $this->_obj->getID() .    " AND `attrdef` = " . $this->_attrdef->getId();
218                } else {
219                    $queryStr = "UPDATE `tblFolderAttributes` SET `value` = ".$db->qstr($value)." WHERE `folder` = " . $this->_obj->getID() .    " AND `attrdef` = " . $this->_attrdef->getId();
220                }
221                break;
222            default:
223                return false;
224        }
225        if (!$db->getResult($queryStr))
226            return false;
227
228        $oldvalue = $this->_value;
229        $this->_value = $values;
230
231        /* Check if 'onPostUpdateAttribute' callback is set */
232        $kk = (trim($value) === '') ? 'Remove' : 'Update';
233        if (isset($this->_dms->callbacks['onPost'.$kk.'Attribute'])) {
234            foreach ($this->_dms->callbacks['onPost'.$kk.'Attribute'] as $callback) {
235                if (!call_user_func($callback[0], $callback[1], $this->_obj, $this->_attrdef, $value, $oldvalue)) {
236                }
237            }
238        }
239
240        return true;
241    } /* }}} */
242
243    /**
244     * Validate attribute value
245     *
246     * This function checks if the attribute values fits the attribute
247     * definition.
248     * If the validation fails the validation error will be set which
249     * can be requested by SeedDMS_Core_Attribute::getValidationError()
250     *
251     * @return boolean true if validation succeeds, otherwise false
252     */
253    public function validate() { /* {{{ */
254        /** @var SeedDMS_Core_AttributeDefinition $attrdef */
255        $attrdef = $this->_attrdef;
256        $result = $attrdef->validate($this->_value);
257        $this->_validation_error = $attrdef->getValidationError();
258        return $result;
259    } /* }}} */
260
261    /**
262     * Get validation error from last validation
263     *
264     * @return integer error code
265     */
266    public function getValidationError() { return $this->_validation_error; }
267
268    /**
269     * Set validation error
270     *
271     * @param integer error code
272     */
273    public function setValidationError($error) { $this->_validation_error = $error; }
274
275    /**
276     * Get definition of attribute
277     *
278     * @return object attribute definition
279     */
280    public function getAttributeDefinition() { return $this->_attrdef; }
281
282} /* }}} */
283
284/**
285 * Class to represent an attribute definition in the document management system
286 *
287 * Attribute definitions specify the name, type, object type, minimum and
288 * maximum values and a value set. The object type determines the object
289 * an attribute may be attached to. If the object type is set to object_all
290 * the attribute can be used for documents, document content and folders.
291 *
292 * The type of an attribute specifies the skalar data type.
293 *
294 * Attributes for which multiple values are allowed must have the
295 * multiple flag set to true and specify a value set. A value set
296 * is a string consisting of n separated values. The separator is the
297 * first char of the value set. A possible value could be '|REV-A|REV-B'
298 * If multiple values are allowed, then minvalues and maxvalues may
299 * restrict the allowed number of values.
300 *
301 * @see SeedDMS_Core_Attribute
302 *
303 * @category   DMS
304 * @package    SeedDMS_Core
305 * @author     Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
306 * @copyright  Copyright (C) 2012-2024 Uwe Steinmann
307 * @version    Release: @package_version@
308 */
309class SeedDMS_Core_AttributeDefinition { /* {{{ */
310    /**
311     * @var integer id of attribute definition
312     *
313     * @access protected
314     */
315    protected $_id;
316
317    /**
318     * @var string name of attribute definition
319     *
320     * @access protected
321     */
322    protected $_name;
323
324    /**
325     * @var string object type of attribute definition. This can be one of
326     * type_int, type_float, type_string, type_boolean, type_url, or type_email.
327     *
328     * @access protected
329     */
330    protected $_type;
331
332    /**
333     * @var string type of attribute definition. This can be one of objtype_all,
334     * objtype_folder, objtype_document, or objtype_documentcontent.
335     *
336     * @access protected
337     */
338    protected $_objtype;
339
340    /**
341     * @var boolean whether an attribute can have multiple values
342     *
343     * @access protected
344     */
345    protected $_multiple;
346
347    /**
348     * @var integer minimum values of an attribute
349     *
350     * @access protected
351     */
352    protected $_minvalues;
353
354    /**
355     * @var integer maximum values of an attribute
356     *
357     * @access protected
358     */
359    protected $_maxvalues;
360
361    /**
362     * @var string list of possible values of an attribute
363     *
364     * @access protected
365     */
366    protected $_valueset;
367
368    /**
369     * @var string regular expression the value must match
370     *
371     * @access protected
372     */
373    protected $_regex;
374
375    /**
376     * @var integer validation error
377     *
378     * @access protected
379     */
380    protected $_validation_error;
381
382    /**
383     * @var SeedDMS_Core_DMS reference to the dms instance this attribute definition belongs to
384     *
385     * @access protected
386     */
387    protected $_dms;
388
389    /**
390     * @var string just the separator of a value set (not used)
391     *
392     * @access protected
393     */
394    protected $_separator;
395
396    /*
397     * Possible skalar data types of an attribute
398     */
399    const type_int = 1;
400    const type_float = 2;
401    const type_string = 3;
402    const type_boolean = 4;
403    const type_url = 5;
404    const type_email = 6;
405    const type_date = 7;
406
407    /*
408     * Addtional data types of an attribute representing objects in seeddms
409     */
410    const type_folder = 101;
411    const type_document = 102;
412    //const type_documentcontent = 103;
413    const type_user = 104;
414    const type_group = 105;
415
416    /*
417     * The object type for which a attribute may be used
418     */
419    const objtype_all = 0;
420    const objtype_folder = 1;
421    const objtype_document = 2;
422    const objtype_documentcontent = 3;
423
424    /*
425     * The validation error codes
426     */
427    const val_error_none = 0;
428    const val_error_min_values = 1;
429    const val_error_max_values = 2;
430    const val_error_boolean = 8;
431    const val_error_int = 6;
432    const val_error_date = 9;
433    const val_error_float = 7;
434    const val_error_regex = 3;
435    const val_error_email = 5;
436    const val_error_url = 4;
437    const val_error_document = 10;
438    const val_error_folder = 11;
439    const val_error_user = 12;
440    const val_error_group = 13;
441    const val_error_valueset = 14;
442
443    /**
444     * Constructor
445     *
446     * @param integer $id internal id of attribute definition
447     * @param string $name name of attribute
448     * @param integer $objtype type of object for which this attribute definition
449     *        may be used.
450     * @param integer $type skalar type of attribute
451     * @param boolean $multiple set to true if multiple values are allowed
452     * @param integer $minvalues minimum number of values
453     * @param integer $maxvalues maximum number of values
454     * @param string $valueset separated list of allowed values, the first char
455     *        is taken as the separator
456     * @param $regex
457     */
458    public function __construct($id, $name, int $objtype, int $type, $multiple, $minvalues, $maxvalues, $valueset, $regex) { /* {{{ */
459        $this->_id = $id;
460        $this->_name = $name;
461        $this->_type = $type;
462        $this->_objtype = $objtype;
463        $this->_multiple = $multiple;
464        $this->_minvalues = $minvalues;
465        $this->_maxvalues = $maxvalues;
466        $this->_valueset = $valueset;
467        $this->_separator = substr($valueset, 0, 1);
468        $this->_regex = $regex;
469        $this->_dms = null;
470        $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_none;
471    } /* }}} */
472
473    /**
474     * Set reference to dms
475     *
476     * @param SeedDMS_Core_DMS $dms
477     */
478    public function setDMS($dms) { /* {{{ */
479        $this->_dms = $dms;
480    } /* }}} */
481
482    /**
483     * Get dms of attribute definition
484     *
485     * @return object $dms
486     */
487    public function getDMS() { return $this->_dms; }
488
489    /**
490     * Get internal id of attribute definition
491     *
492     * @return integer id
493     */
494    public function getID() { return $this->_id; }
495
496    /**
497     * Get name of attribute definition
498     *
499     * @return string name
500     */
501    public function getName() { return $this->_name; }
502
503    /**
504     * Set name of attribute definition
505     *
506     * @param string $name name of attribute definition
507     * @return boolean true on success, otherwise false
508     */
509    public function setName($name) { /* {{{ */
510        $db = $this->_dms->getDB();
511
512        $queryStr = "UPDATE `tblAttributeDefinitions` SET `name` =".$db->qstr($name)." WHERE `id` = " . $this->_id;
513        $res = $db->getResult($queryStr);
514        if (!$res)
515            return false;
516
517        $this->_name = $name;
518        return true;
519    } /* }}} */
520
521    /**
522     * Get object type of attribute definition
523     *
524     * This can be one of objtype_all,
525     * objtype_folder, objtype_document, or objtype_documentcontent.
526     *
527     * @return integer type
528     */
529    public function getObjType() { return $this->_objtype; }
530
531    /**
532     * Set object type of attribute definition
533     *
534     * This can be one of objtype_all,
535     * objtype_folder, objtype_document, or objtype_documentcontent.
536     *
537     * @param integer $objtype type
538     * @return bool
539     */
540    public function setObjType($objtype) { /* {{{ */
541        $db = $this->_dms->getDB();
542
543        $queryStr = "UPDATE `tblAttributeDefinitions` SET `objtype` =".intval($objtype)." WHERE `id` = " . $this->_id;
544        $res = $db->getResult($queryStr);
545        if (!$res)
546            return false;
547
548        $this->_objtype = $objtype;
549        return true;
550    } /* }}} */
551
552    /**
553     * Get type of attribute definition
554     *
555     * This can be one of type_int, type_float, type_string, type_boolean,
556     * type_url, type_email.
557     *
558     * @return integer type
559     */
560    public function getType() { return $this->_type; }
561
562    /**
563     * Set type of attribute definition
564     *
565     * This can be one of type_int, type_float, type_string, type_boolean,
566     * type_url, type_email.
567     *
568     * @param integer $type type
569     * @return bool
570     */
571    public function setType($type) { /* {{{ */
572        $db = $this->_dms->getDB();
573
574        $queryStr = "UPDATE `tblAttributeDefinitions` SET `type` =".intval($type)." WHERE `id` = " . $this->_id;
575        $res = $db->getResult($queryStr);
576        if (!$res)
577            return false;
578
579        $this->_type = $type;
580        return true;
581    } /* }}} */
582
583    /**
584     * Check if attribute definition allows multi values for attribute
585     *
586     * @return boolean true if attribute may have multiple values
587     */
588    public function getMultipleValues() { return $this->_multiple; }
589
590    /**
591     * Set if attribute definition allows multi values for attribute
592     *
593     * @param boolean $mv true if attribute may have multiple values, otherwise
594     * false
595     * @return bool
596     */
597    public function setMultipleValues($mv) { /* {{{ */
598        $db = $this->_dms->getDB();
599
600        $queryStr = "UPDATE `tblAttributeDefinitions` SET `multiple` =".intval($mv)." WHERE `id` = " . $this->_id;
601        $res = $db->getResult($queryStr);
602        if (!$res)
603            return false;
604
605        $this->_multiple = $mv;
606        return true;
607    } /* }}} */
608
609    /**
610     * Return minimum number of values for attributes
611     *
612     * Attributes with multiple values may be limited to a range
613     * of values. This functions returns the minimum number of values.
614     *
615     * @return integer minimum number of values
616     */
617    public function getMinValues() { return $this->_minvalues; }
618
619    public function setMinValues($minvalues) { /* {{{ */
620        $db = $this->_dms->getDB();
621
622        $queryStr = "UPDATE `tblAttributeDefinitions` SET `minvalues` =".intval($minvalues)." WHERE `id` = " . $this->_id;
623        $res = $db->getResult($queryStr);
624        if (!$res)
625            return false;
626
627        $this->_minvalues = $minvalues;
628        return true;
629    } /* }}} */
630
631    /**
632     * Return maximum number of values for attributes
633     *
634     * Attributes with multiple values may be limited to a range
635     * of values. This functions returns the maximum number of values.
636     *
637     * @return integer maximum number of values
638     */
639    public function getMaxValues() { return $this->_maxvalues; }
640
641    public function setMaxValues($maxvalues) { /* {{{ */
642        $db = $this->_dms->getDB();
643
644        $queryStr = "UPDATE `tblAttributeDefinitions` SET `maxvalues` =".intval($maxvalues)." WHERE `id` = " . $this->_id;
645        $res = $db->getResult($queryStr);
646        if (!$res)
647            return false;
648
649        $this->_maxvalues = $maxvalues;
650        return true;
651    } /* }}} */
652
653    /**
654     * Get the value set as saved in the database
655     *
656     * This is a string containing the list of valueÑ• separated by a
657     * delimiter which also precedes the whole string, e.g. '|Yes|No'
658     *
659     * Use {@link SeedDMS_Core_AttributeDefinition::getValueSetAsArray()}
660     * for a list of values returned as an array.
661     *
662     * @return string value set
663     */
664    public function getValueSet() { /* {{{ */
665        return $this->_valueset;
666    } /* }}} */
667
668    /**
669     * Get the separator used for the value set
670     *
671     * This is the first char of the value set string.
672     *
673     * @return string separator or an empty string if a value set is not set
674     */
675    public function getValueSetSeparator() { /* {{{ */
676        if (strlen($this->_valueset) > 1) {
677            return $this->_valueset[0];
678        } elseif ($this->_multiple) {
679            if ($this->_type == SeedDMS_Core_AttributeDefinition::type_boolean) {
680                return '';
681            } else {
682                return ',';
683            }
684        } else {
685            return '';
686        }
687    } /* }}} */
688
689    /**
690     * Get the whole value set as an array
691     *
692     * Each element is trimmed.
693     *
694     * @return array values of value set or false if the value set has
695     *         less than 2 chars
696     */
697    public function getValueSetAsArray() { /* {{{ */
698        if (strlen($this->_valueset) > 1) {
699            return array_map('trim', explode($this->_valueset[0], substr($this->_valueset, 1)));
700        } else {
701            return array();
702        }
703    } /* }}} */
704
705    /**
706     * Get the n'th trimmed value of a value set
707     *
708     * @param $ind starting from 0 for the first element in the value set
709     * @return string n'th value of value set or false if the index is
710     *         out of range or the value set has less than 2 chars
711     * @internal param int $index
712     */
713    public function getValueSetValue($ind) { /* {{{ */
714        if (strlen($this->_valueset) > 1) {
715            $tmp = explode($this->_valueset[0], substr($this->_valueset, 1));
716            if (isset($tmp[$ind])) {
717                return trim($tmp[$ind]);
718            } else {
719                return false;
720            }
721        } else {
722            return false;
723        }
724    } /* }}} */
725
726    /**
727     * Set the value set
728     *
729     * A value set is a list of values allowed for an attribute. The values
730     * are separated by a char which must also be the first char of the
731     * value set string. The method decomposes the value set, removes all
732     * leading and trailing white space from the elements and recombines them
733     * into a string.
734     *
735     * @param string $valueset
736     * @return boolean true if value set could be set, otherwise false
737     */
738    public function setValueSet($valueset) { /* {{{ */
739    /*
740        $tmp = array();
741        foreach ($valueset as $value) {
742            $tmp[] = str_replace('"', '""', $value);
743        }
744        $valuesetstr = implode(",", $tmp);
745     */
746        $valueset = trim($valueset);
747        if ($valueset) {
748            $valuesetarr = array_map('trim', explode($valueset[0], substr($valueset, 1)));
749            $valuesetstr = $valueset[0].implode($valueset[0], $valuesetarr);
750        } else {
751            $valuesetstr = '';
752        }
753
754        $db = $this->_dms->getDB();
755
756        $queryStr = "UPDATE `tblAttributeDefinitions` SET `valueset` =".$db->qstr($valuesetstr)." WHERE `id` = " . $this->_id;
757        $res = $db->getResult($queryStr);
758        if (!$res)
759            return false;
760
761        $this->_valueset = $valuesetstr;
762        $this->_separator = substr($valuesetstr, 0, 1);
763        return true;
764    } /* }}} */
765
766    /**
767     * Get the regular expression as saved in the database
768     *
769     * @return string regular expression
770     */
771    public function getRegex() { /* {{{ */
772        return $this->_regex;
773    } /* }}} */
774
775    /**
776     * Set the regular expression
777     *
778     * A value of the attribute must match this regular expression.
779     *
780     * The methods checks if the regular expression is valid by running
781     * preg_match() on an empty string and see if it fails. Trying to set
782     * an invalid regular expression will not overwrite the current
783     * regular expression.
784     *
785     * All leading and trailing spaces of $regex will be removed.
786     *
787     * @param string $regex
788     * @return boolean true if regex could be set or is invalid, otherwise false
789     */
790    public function setRegex($regex) { /* {{{ */
791        $db = $this->_dms->getDB();
792
793        $regex = trim($regex);
794        if ($regex && @preg_match($regex, '') === false)
795            return false;
796
797        $queryStr = "UPDATE `tblAttributeDefinitions` SET `regex` =".$db->qstr($regex)." WHERE `id` = " . $this->_id;
798        $res = $db->getResult($queryStr);
799        if (!$res)
800            return false;
801
802        $this->_regex = $regex;
803        return true;
804    } /* }}} */
805
806    /**
807     * Check if the attribute definition is used
808     *
809     * Checks all documents, folders and document content whether at least
810     * one of them referenceÑ• this attribute definition
811     *
812     * @return boolean true if attribute definition is used, otherwise false
813     */
814    public function isUsed() { /* {{{ */
815        $db = $this->_dms->getDB();
816
817        $queryStr = "SELECT * FROM `tblDocumentAttributes` WHERE `attrdef`=".$this->_id;
818        $resArr = $db->getResultArray($queryStr);
819        if (is_array($resArr) && count($resArr) == 0) {
820            $queryStr = "SELECT * FROM `tblFolderAttributes` WHERE `attrdef`=".$this->_id;
821            $resArr = $db->getResultArray($queryStr);
822            if (is_array($resArr) && count($resArr) == 0) {
823                $queryStr = "SELECT * FROM `tblDocumentContentAttributes` WHERE `attrdef`=".$this->_id;
824                $resArr = $db->getResultArray($queryStr);
825                if (is_array($resArr) && count($resArr) == 0) {
826                    return false;
827                }
828            }
829        }
830        return true;
831    } /* }}} */
832
833    /**
834     * Parse a given value stored in the database according to attribute definition
835     *
836     * The return value is an array, if the attribute allows multiple values.
837     * Otherwise it is a single value.
838     * If the type of attribute is any of document, folder, user,
839     * or group then this method will fetch each object from the database and
840     * return an array of SeedDMS_Core_Document, SeedDMS_Core_Folder, etc.
841     *
842     * @param $value string
843     * @return array|bool
844     */
845    public function parseValue(string $value) { /* {{{ */
846        if ($this->getMultipleValues()) {
847            /* If the value doesn't start with the separator used in the value set,
848             * then assume that the value was not saved with a leading separator.
849             * This can happen, if the value was previously a single value from
850             * the value set and later turned into a multi value attribute.
851             */
852            $sep = substr($value, 0, 1);
853            $vsep = $this->getValueSetSeparator();
854            if ($sep == $vsep) {
855                $values = explode($sep, substr($value, 1));
856            } else {
857                $values = array($value);
858            }
859        } else {
860            $values = array($value);
861        }
862
863        $tmp = [];
864        switch ((string) $this->getType()) {
865        case self::type_document:
866            foreach ($values as $value) {
867                if ($u = $this->_dms->getDocument((int) $value))
868                    $tmp[] = $u;
869            }
870            $values = $tmp;
871            break;
872        case self::type_folder:
873            foreach ($values as $value) {
874                if ($u = $this->_dms->getFolder((int) $value))
875                    $tmp[] = $u;
876            }
877            $values = $tmp;
878            break;
879        case self::type_user:
880            foreach ($values as $value) {
881                if ($u = $this->_dms->getUser((int) $value))
882                    $tmp[] = $u;
883            }
884            $values = $tmp;
885            break;
886        case self::type_group:
887            foreach ($values as $value) {
888                if ($u = $this->_dms->getGroup((int) $value))
889                    $tmp[] = $u;
890            }
891            $values = $tmp;
892            break;
893        case self::type_boolean:
894            foreach ($values as $value) {
895                $tmp[] = (bool) $value;
896            }
897            $values = $tmp;
898            break;
899        case self::type_int:
900            foreach ($values as $value) {
901                $tmp[] = (int) $value;
902            }
903            $values = $tmp;
904            break;
905        case self::type_float:
906            foreach ($values as $value) {
907                $tmp[] = (float) $value;
908            }
909            $values = $tmp;
910            break;
911        }
912
913        if ($this->getMultipleValues()) {
914            return $values;
915        } else {
916            return $values[0];
917        }
918    } /* }}} */
919
920    /**
921     * Create the value stored in the database
922     */
923    public function createValue($values) { /* {{{ */
924        if (is_array($values)) {
925            switch ($this->getType()) {
926            case SeedDMS_Core_AttributeDefinition::type_document:
927            case SeedDMS_Core_AttributeDefinition::type_folder:
928            case SeedDMS_Core_AttributeDefinition::type_user:
929            case SeedDMS_Core_AttributeDefinition::type_group:
930                $tmp = array_map(fn($value): int => is_object($value) ? (int) $value->getId() : (int) $value, $values);
931                break;
932            case SeedDMS_Core_AttributeDefinition::type_boolean:
933                $tmp = array_map(fn($value): int => $value ? '1' : '0', $values);
934                break;
935            default:
936                $tmp = $values;
937            }
938        } else {
939            switch ($this->getType()) {
940            case SeedDMS_Core_AttributeDefinition::type_document:
941            case SeedDMS_Core_AttributeDefinition::type_folder:
942            case SeedDMS_Core_AttributeDefinition::type_user:
943            case SeedDMS_Core_AttributeDefinition::type_group:
944                $tmp = is_object($values) ? [$values->getId()] : (is_numeric($values) ? [$values] : []);
945                break;
946            case SeedDMS_Core_AttributeDefinition::type_boolean:
947                $tmp = [$values ? 1 : 0];
948                break;
949            default:
950                $tmp = [$values];
951            }
952        }
953
954        if ($this->getMultipleValues()) {
955            $vsep = $this->getValueSetSeparator();
956        } else {
957            $vsep = '';
958        }
959        return $vsep.implode($vsep, $tmp);
960    } /* }}} */
961
962    /**
963     * Return a list of documents, folders, document contents where this
964     * attribute definition is used
965     *
966     * @param integer $limit return not more the n objects of each type
967     * @return array|bool
968     */
969    public function getStatistics($limit = 0) { /* {{{ */
970        $db = $this->_dms->getDB();
971
972        $result = array('docs'=>array(), 'folders'=>array(), 'contents'=>array());
973        if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all ||
974           $this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_document) {
975            $queryStr = "SELECT * FROM `tblDocumentAttributes` WHERE `attrdef`=".$this->_id;
976            if ($limit)
977                $queryStr .= " limit ".(int) $limit;
978            $resArr = $db->getResultArray($queryStr);
979            if ($resArr) {
980                foreach ($resArr as $rec) {
981                    if ($doc = $this->_dms->getDocument($rec['document'])) {
982                        $result['docs'][] = $doc;
983                    }
984                }
985            }
986            $valueset = $this->getValueSetAsArray();
987            $possiblevalues = array();
988            foreach ($valueset as $value) {
989                $possiblevalues[md5($value)] = array('value'=>$value, 'c'=>0);
990            }
991            $queryStr = "SELECT count(*) c, `value` FROM `tblDocumentAttributes` WHERE `attrdef`=".$this->_id." GROUP BY `value` ORDER BY c DESC";
992            $resArr = $db->getResultArray($queryStr);
993            if ($resArr) {
994                foreach ($resArr as $row) {
995                    $value = $this->parseValue($row['value']);
996                    $tmpattr = new SeedDMS_Core_Attribute(0, null, $this, $value);
997                    foreach ($tmpattr->getValueAsArray() as $value) {
998                        if (is_object($value)) {
999                            $key = md5((string) $value->getId());
1000                        } else {
1001                            $key = md5((string) $value);
1002                        }
1003                        if (isset($possiblevalues[$key])) {
1004                            $possiblevalues[$key]['c'] += $row['c'];
1005                        } else {
1006                            $possiblevalues[$key] = array('value'=>$value, 'c'=>$row['c']);
1007                        }
1008                    }
1009                }
1010                $result['frequencies']['document'] = $possiblevalues;
1011            }
1012        }
1013
1014        if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all ||
1015           $this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_folder) {
1016            $queryStr = "SELECT * FROM `tblFolderAttributes` WHERE `attrdef`=".$this->_id;
1017            if ($limit)
1018                $queryStr .= " limit ".(int) $limit;
1019            $resArr = $db->getResultArray($queryStr);
1020            if ($resArr) {
1021                foreach ($resArr as $rec) {
1022                    if ($folder = $this->_dms->getFolder($rec['folder'])) {
1023                        $result['folders'][] = $folder;
1024                    }
1025                }
1026            }
1027            $valueset = $this->getValueSetAsArray();
1028            $possiblevalues = array();
1029            foreach ($valueset as $value) {
1030                $possiblevalues[md5($value)] = array('value'=>$value, 'c'=>0);
1031            }
1032            $queryStr = "SELECT count(*) c, `value` FROM `tblFolderAttributes` WHERE `attrdef`=".$this->_id." GROUP BY `value` ORDER BY c DESC";
1033            $resArr = $db->getResultArray($queryStr);
1034            if ($resArr) {
1035                foreach ($resArr as $row) {
1036                    $value = $this->parseValue($row['value']);
1037                    $tmpattr = new SeedDMS_Core_Attribute(0, null, $this, $value);
1038                    foreach ($tmpattr->getValueAsArray() as $value) {
1039                        if (is_object($value)) {
1040                            $key = md5((string) $value->getId());
1041                        } else {
1042                            $key = md5((string) $value);
1043                        }
1044                        if (isset($possiblevalues[$key])) {
1045                            $possiblevalues[$key]['c'] += $row['c'];
1046                        } else {
1047                            $possiblevalues[$key] = array('value'=>$value, 'c'=>$row['c']);
1048                        }
1049                    }
1050                }
1051                $result['frequencies']['folder'] = $possiblevalues;
1052            }
1053        }
1054
1055        if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all ||
1056           $this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_documentcontent) {
1057            $queryStr = "SELECT * FROM `tblDocumentContentAttributes` WHERE `attrdef`=".$this->_id;
1058            if ($limit)
1059                $queryStr .= " limit ".(int) $limit;
1060            $resArr = $db->getResultArray($queryStr);
1061            if ($resArr) {
1062                foreach ($resArr as $rec) {
1063                    if ($content = $this->_dms->getDocumentContent($rec['content'])) {
1064                        $result['contents'][] = $content;
1065                    }
1066                }
1067            }
1068            $valueset = $this->getValueSetAsArray();
1069            $possiblevalues = array();
1070            foreach ($valueset as $value) {
1071                $possiblevalues[md5($value)] = array('value'=>$value, 'c'=>0);
1072            }
1073            $queryStr = "SELECT count(*) c, `value` FROM `tblDocumentContentAttributes` WHERE `attrdef`=".$this->_id." GROUP BY `value` ORDER BY c DESC";
1074            $resArr = $db->getResultArray($queryStr);
1075            if ($resArr) {
1076                foreach ($resArr as $row) {
1077                    $value = $this->parseValue($row['value']);
1078                    $tmpattr = new SeedDMS_Core_Attribute(0, null, $this, $value);
1079                    foreach ($tmpattr->getValueAsArray() as $value) {
1080                        if (is_object($value)) {
1081                            $key = md5((string) $value->getId());
1082                        } else {
1083                            $key = md5((string) $value);
1084                        }
1085                        if (isset($possiblevalues[$key])) {
1086                            $possiblevalues[$key]['c'] += $row['c'];
1087                        } else {
1088                            $possiblevalues[$key] = array('value'=>$value, 'c'=>$row['c']);
1089                        }
1090                    }
1091                }
1092                $result['frequencies']['content'] = $possiblevalues;
1093            }
1094        }
1095
1096        return $result;
1097    } /* }}} */
1098
1099    /**
1100     * Remove the attribute definition
1101     * Removal is only executed when the definition is not used anymore.
1102     *
1103     * @return boolean true on success or false in case of an error
1104     */
1105    public function remove() { /* {{{ */
1106        $db = $this->_dms->getDB();
1107
1108        if ($this->isUsed())
1109            return false;
1110
1111        // Delete user itself
1112        $queryStr = "DELETE FROM `tblAttributeDefinitions` WHERE `id` = " . $this->_id;
1113        if (!$db->getResult($queryStr)) return false;
1114
1115        return true;
1116    } /* }}} */
1117
1118    /**
1119     * Get all documents and folders by a given attribute value
1120     *
1121     * @param string $attrvalue value of attribute
1122     * @param integer $limit limit number of documents/folders
1123     * @return array array containing list of documents and folders
1124     */
1125    public function getObjects($attrvalue, $limit = 0, $op = O_EQ) { /* {{{ */
1126        $db = $this->_dms->getDB();
1127
1128        $result = array('docs'=>array(), 'folders'=>array(), 'contents'=>array());
1129        if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all ||
1130          $this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_document) {
1131            $queryStr = "SELECT * FROM `tblDocumentAttributes` WHERE `attrdef`=".$this->_id;
1132            if ($attrvalue != null) {
1133                $queryStr .= " AND ";
1134                if ($this->getMultipleValues()) {
1135                    $sep = $this->getValueSetSeparator();
1136                    $queryStr .= "(`value` like ".$db->qstr($sep.$attrvalue.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue.$sep.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue).")";
1137                } else {
1138                    $queryStr .= "`value`".$op.$db->qstr((string) $attrvalue);
1139                }
1140            }
1141            if ($limit)
1142                $queryStr .= " limit ".(int) $limit;
1143            $resArr = $db->getResultArray($queryStr);
1144            if ($resArr) {
1145                foreach ($resArr as $rec) {
1146                    if ($doc = $this->_dms->getDocument($rec['document'])) {
1147                        $result['docs'][] = $doc;
1148                    }
1149                }
1150            }
1151        }
1152
1153        if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all ||
1154           $this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_folder) {
1155            $queryStr = "SELECT * FROM `tblFolderAttributes` WHERE `attrdef`=".$this->_id." AND ";
1156            if ($this->getMultipleValues()) {
1157                $sep = $this->getValueSetSeparator();
1158                $queryStr .= "(`value` like ".$db->qstr($sep.$attrvalue.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue.$sep.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue).")";
1159            } else {
1160                $queryStr .= "`value`=".$db->qstr($attrvalue);
1161            }
1162            if ($limit)
1163                $queryStr .= " limit ".(int) $limit;
1164            $resArr = $db->getResultArray($queryStr);
1165            if ($resArr) {
1166                foreach ($resArr as $rec) {
1167                    if ($folder = $this->_dms->getFolder($rec['folder'])) {
1168                        $result['folders'][] = $folder;
1169                    }
1170                }
1171            }
1172        }
1173
1174        return $result;
1175    } /* }}} */
1176
1177    /**
1178     * Remove a given attribute value from all documents, versions and folders
1179     *
1180     * @param string $attrvalue value of attribute
1181     * @return array array containing list of documents and folders
1182     */
1183    public function removeValue($attrvalue) { /* {{{ */
1184        $db = $this->_dms->getDB();
1185
1186        foreach (array('document', 'documentcontent', 'folder') as $type) {
1187            if ($type == 'document') {
1188                $tablename = "tblDocumentAttributes";
1189                $objtype = SeedDMS_Core_AttributeDefinition::objtype_document;
1190            } elseif ($type == 'documentcontent') {
1191                $tablename = "tblDocumentContentAttributes";
1192                $objtype = SeedDMS_Core_AttributeDefinition::objtype_documentcontent;
1193            } elseif ($type == 'folder') {
1194                $tablename = "tblFolderAttributes";
1195                $objtype = SeedDMS_Core_AttributeDefinition::objtype_folder;
1196            }
1197            if ($this->_objtype == SeedDMS_Core_AttributeDefinition::objtype_all || $objtype) {
1198                $queryStr = "SELECT * FROM `".$tablename."` WHERE `attrdef`=".$this->_id." AND ";
1199                if ($this->getMultipleValues()) {
1200                    $sep = $this->getValueSetSeparator();
1201                    $queryStr .= "(`value` like ".$db->qstr($sep.$attrvalue.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue.$sep.'%')." OR `value` like ".$db->qstr('%'.$sep.$attrvalue).")";
1202                } else {
1203                    $queryStr .= "`value`=".$db->qstr($attrvalue);
1204                }
1205
1206                $resArr = $db->getResultArray($queryStr);
1207                if ($resArr) {
1208                    $db->startTransaction();
1209                    foreach ($resArr as $rec) {
1210                        if ($rec['value'] == $attrvalue) {
1211                            $queryStr = "DELETE FROM `".$tablename."` WHERE `id`=".$rec['id'];
1212                        } else {
1213                            if ($this->getMultipleValues()) {
1214                                $sep = substr($rec['value'], 0, 1);
1215                                $vsep = $this->getValueSetSeparator();
1216                                if ($sep == $vsep) {
1217                                    $values = explode($sep, substr($rec['value'], 1));
1218                                } else {
1219                                    $values = array($rec['value']);
1220                                }
1221                                if (($key = array_search($attrvalue, $values)) !== false) {
1222                                    unset($values[$key]);
1223                                }
1224                                if ($values) {
1225                                    $queryStr = "UPDATE `".$tablename."` SET `value`=".$db->qstr($sep.implode($sep, $values))." WHERE `id`=".$rec['id'];
1226                                } else {
1227                                    $queryStr = "DELETE FROM `".$tablename."` WHERE `id`=".$rec['id'];
1228                                }
1229                            } else {
1230                            }
1231                        }
1232                        if (!$db->getResult($queryStr)) {
1233                            $db->rollbackTransaction();
1234                            return false;
1235                        }
1236                    }
1237                    $db->commitTransaction();
1238                }
1239            }
1240        }
1241        return true;
1242    } /* }}} */
1243
1244    /**
1245     * Validate value against attribute definition
1246     *
1247     * This function checks if the given value fits the attribute
1248     * definition.
1249     * If the validation fails the validation error will be set which
1250     * can be requested by SeedDMS_Core_Attribute::getValidationError()
1251     * Set $new to true if the value to be checked isn't saved to the database
1252     * already. It will just be passed to the callback onAttributeValidate where
1253     * it could be used to, e.g. check if a value is unique once it is saved to
1254     * the database. $object is set to a folder, document or documentcontent
1255     * if the attribute belongs to such an object. This will be null, if a
1256     * new object is created.
1257     *
1258     * @param string|array $attrvalue attribute value
1259     * @param object $object set if the current attribute is saved for this object
1260     *   (this will only be passed to the onAttributeValidate callback)
1261     * @param boolean $new set to true if the value is new value and not taken from
1262     *   an existing attribute
1263     *   (this will only be passed to the onAttributeValidate callback)
1264     * @return boolean true if validation succeeds, otherwise false
1265     */
1266    public function validate($attrvalue, $object = null, $new = false) { /* {{{ */
1267        /* Check if 'onAttributeValidate' callback is set */
1268        if (isset($this->_dms->callbacks['onAttributeValidate'])) {
1269            foreach ($this->_dms->callbacks['onAttributeValidate'] as $callback) {
1270                $ret = call_user_func($callback[0], $callback[1], $this, $attrvalue, $object, $new);
1271                if (is_bool($ret))
1272                    return $ret;
1273            }
1274        }
1275
1276        /* Turn $attrvalue into an array of values. Checks if $attrvalue starts
1277         * with a separator char as set in the value set and use it to explode
1278         * the $attrvalue. If the separator doesn't match or this attribute
1279         * definition doesn't have a value set, then just create a one element
1280         * array. if $attrvalue is empty, then create an empty array.
1281         */
1282        if ($this->getMultipleValues()) {
1283            if (is_string($attrvalue) && $attrvalue) {
1284                $sep = $attrvalue[0];
1285                $vsep = $this->getValueSetSeparator();
1286                if ($sep == $vsep) {
1287                    $values = explode($attrvalue[0], substr($attrvalue, 1));
1288                } else {
1289                    $values = array($attrvalue);
1290                }
1291            } elseif (is_array($attrvalue)) {
1292                $values = $attrvalue;
1293            } elseif (is_string($attrvalue) && !$attrvalue) {
1294                $values = array();
1295            } else {
1296                $values = array($attrvalue);
1297            }
1298        } elseif ($attrvalue !== null) {
1299            $values = array($attrvalue);
1300        } else {
1301            $values = array();
1302        }
1303
1304        /* Check if attribute value has at least the minimum number of values */
1305        $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_none;
1306        if ($this->getMinValues() > count($values)) {
1307            $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_min_values;
1308            return false;
1309        }
1310        /* Check if attribute value has not more than maximum number of values */
1311        if ($this->getMaxValues() && $this->getMaxValues() < count($values)) {
1312            $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_max_values;
1313            return false;
1314        }
1315
1316        $success = true;
1317        switch ((string) $this->getType()) {
1318        case self::type_boolean:
1319            foreach ($values as $value) {
1320                $success = $success && (preg_match('/^[01]$/', (string) $value) || $value === true || $value === false);
1321            }
1322            if (!$success)
1323                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_boolean;
1324            break;
1325        case self::type_int:
1326            foreach ($values as $value) {
1327                $success = $success && (preg_match('/^[0-9]*$/', (string) $value) ? true : false);
1328            }
1329            if (!$success)
1330                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_int;
1331            break;
1332        case self::type_date:
1333            foreach ($values as $value) {
1334                $d = explode('-', $value, 3);
1335                $success = $success && (count($d) == 3) && checkdate((int) $d[1], (int) $d[2], (int) $d[0]);
1336            }
1337            if (!$success)
1338                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_date;
1339            break;
1340        case self::type_float:
1341            foreach ($values as $value) {
1342                $success = $success && is_numeric($value);
1343            }
1344            if (!$success)
1345                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_float;
1346            break;
1347        case self::type_string:
1348            if (trim($this->getRegex()) != '') {
1349                foreach ($values as $value) {
1350                    $success = $success && (preg_match($this->getRegex(), $value) ? true : false);
1351                }
1352            }
1353            if (!$success)
1354                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_regex;
1355            break;
1356        case self::type_email:
1357            foreach ($values as $value) {
1358                //$success &= filter_var($value, FILTER_VALIDATE_EMAIL) ? true : false;
1359                $success = $success && (preg_match('/^[a-z0-9._-]+@[a-z0-9-]{2,63}(\.[a-z0-9-]{2,63})*\.[a-z]{2,63}$/i', $value) ? true : false);
1360            }
1361            if (!$success)
1362                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_email;
1363            break;
1364        case self::type_url:
1365            foreach ($values as $value) {
1366                $success = $success && (preg_match('/^http(s)?:\/\/[a-z0-9_-]+(\.[a-z0-9-]{2,63})*(:[0-9]+)?(\/.*)?$/i', $value) ? true : false);
1367            }
1368            if (!$success)
1369                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_url;
1370            break;
1371        case self::type_document:
1372            $success = true;
1373            foreach ($values as $value) {
1374                if (!$value->isType('document'))
1375                    $success = $success && false;
1376            }
1377            if (!$success)
1378                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_document;
1379            break;
1380        case self::type_folder:
1381            $success = true;
1382            foreach ($values as $value) {
1383                if (!$value->isType('folder'))
1384                    $success = $success && false;
1385            }
1386            if (!$success)
1387                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_folder;
1388            break;
1389        case self::type_user:
1390            $success = true;
1391            foreach ($values as $value) {
1392                if (!$value->isType('user'))
1393                    $success = $success && false;
1394            }
1395            if (!$success)
1396                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_user;
1397            break;
1398        case self::type_group:
1399            $success = true;
1400            foreach ($values as $value) {
1401                if (!$value->isType('group'))
1402                    $success = $success && false;
1403            }
1404            if (!$success)
1405                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_group;
1406            break;
1407        }
1408
1409        if (!$success)
1410            return $success;
1411
1412        /* Check if value is in value set */
1413        if ($valueset = $this->getValueSetAsArray()) {
1414            /* An empty value cannot be the value set */
1415            if (!$values) {
1416                $success = false;
1417                $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_valueset;
1418            } else {
1419                foreach ($values as $value) {
1420                    if (!in_array($value, $valueset)) {
1421                        $success = false;
1422                        $this->_validation_error = SeedDMS_Core_AttributeDefinition::val_error_valueset;
1423                    }
1424                }
1425            }
1426        }
1427
1428        return $success;
1429    } /* }}} */
1430
1431    /**
1432     * Get validation error from last validation
1433     *
1434     * @return integer error code
1435     */
1436    public function getValidationError() { return $this->_validation_error; }
1437
1438} /* }}} */