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