1<?php
2
3/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
4
5/**
6 * Class ilDclBaseFieldModel
7 *
8 * @author  Martin Studer <ms@studer-raimann.ch>
9 * @author  Marcel Raimann <mr@studer-raimann.ch>
10 * @author  Fabian Schmid <fs@studer-raimann.ch>
11 * @author  Oskar Truffer <ot@studer-raimann.ch>
12 * @author  Stefan Wanzenried <sw@studer-raimann.ch>
13 * @version $Id:
14 *
15 * @ingroup ModulesDataCollection
16 */
17class ilDclTable
18{
19
20    /**
21     * @var int
22     */
23    protected $id = 0;
24    /**
25     * @var int
26     */
27    protected $objId;
28    /**
29     * @var ilObjDataCollection
30     */
31    protected $obj;
32    /**
33     * @var string
34     */
35    protected $title;
36    /**
37     * @var array ilDclBaseFieldModel[]
38     */
39    protected $fields;
40    /**
41     * @var array ilDclStandardField[]
42     */
43    protected $stdFields;
44    /**
45     * @var array ilDclBaseRecordModel[]
46     */
47    protected $records;
48    /**
49     * @var bool
50     */
51    protected $is_visible;
52    /**
53     * @var bool
54     */
55    protected $add_perm;
56    /**
57     * @var bool
58     */
59    protected $edit_perm;
60    /**
61     * @var bool
62     */
63    protected $delete_perm;
64    /**
65     * @var bool
66     */
67    protected $edit_by_owner;
68    /**
69     * @var bool
70     */
71    protected $delete_by_owner;
72    /**
73     * @var bool
74     */
75    protected $save_confirmation;
76    /**
77     * @var bool
78     */
79    protected $limited;
80    /**
81     * @var string
82     */
83    protected $limit_start;
84    /**
85     * @var string
86     */
87    protected $limit_end;
88    /**
89     * @var bool
90     */
91    protected $export_enabled;
92    /**
93     * @var integer
94     */
95    protected $table_order;
96    /**
97     * @var bool
98     */
99    protected $import_enabled;
100    /**
101     * ID of the default sorting field. Can be a DB field (int) or a standard field (string)
102     *
103     * @var string
104     */
105    protected $default_sort_field = 0;
106    /**
107     * Default sort-order (asc|desc)
108     *
109     * @var string
110     */
111    protected $default_sort_field_order = 'asc';
112    /**
113     * Description for this table displayed above records
114     *
115     * @var string
116     */
117    protected $description = '';
118    /**
119     * True if users can add comments on each record of this table
120     *
121     * @var bool
122     */
123    protected $public_comments = 0;
124    /**
125     * True if user can only view his/her own entries in the table
126     *
127     * @var bool
128     */
129    protected $view_own_records_perm = 0;
130    /**
131     * table fields and std fields combined
132     *
133     * @var null|array
134     */
135    protected $all_fields = null;
136
137
138    /**
139     * @param int $a_id
140     */
141    public function __construct($a_id = 0)
142    {
143        if ($a_id != 0) {
144            $this->id = $a_id;
145            $this->doRead();
146        }
147    }
148
149
150    /**
151     * Read table
152     */
153    public function doRead()
154    {
155        global $DIC;
156        $ilDB = $DIC['ilDB'];
157
158        $query = "SELECT * FROM il_dcl_table WHERE id = " . $ilDB->quote($this->getId(), "integer");
159        $set = $ilDB->query($query);
160        $rec = $ilDB->fetchAssoc($set);
161
162        $this->setObjId($rec["obj_id"]);
163        $this->setTitle($rec["title"]);
164        $this->setAddPerm($rec["add_perm"]);
165        $this->setEditPerm($rec["edit_perm"]);
166        $this->setDeletePerm($rec["delete_perm"]);
167        $this->setEditByOwner($rec["edit_by_owner"]);
168        $this->setExportEnabled($rec["export_enabled"]);
169        $this->setImportEnabled($rec["import_enabled"]);
170        $this->setLimited($rec["limited"]);
171        $this->setLimitStart($rec["limit_start"]);
172        $this->setLimitEnd($rec["limit_end"]);
173        $this->setIsVisible($rec["is_visible"]);
174        $this->setDescription($rec['description']);
175        $this->setDefaultSortField($rec['default_sort_field_id']);
176        $this->setDefaultSortFieldOrder($rec['default_sort_field_order']);
177        $this->setPublicCommentsEnabled($rec['public_comments']);
178        $this->setViewOwnRecordsPerm($rec['view_own_records_perm']);
179        $this->setDeleteByOwner($rec['delete_by_owner']);
180        $this->setSaveConfirmation($rec['save_confirmation']);
181        $this->setOrder($rec['table_order']);
182    }
183
184
185    /**
186     * Delete table
187     * Attention this does not delete the maintable of it's the maintable of the collection.
188     * unlink the the maintable in the collections object to make this work.
189     *
190     * @param boolean $delete_main_table true to delete table anyway
191     */
192    public function doDelete($delete_only_content = false, $omit_notification = false)
193    {
194        global $DIC;
195        $ilDB = $DIC['ilDB'];
196
197        /** @var $ilDB ilDB */
198        foreach ($this->getRecords() as $record) {
199            $record->doDelete($omit_notification);
200        }
201
202        foreach ($this->getRecordFields() as $field) {
203            $field->doDelete();
204        }
205
206        //		// SW: Fix #12794 und #11405
207        //		// Problem is that when the DC object gets deleted, $this::getCollectionObject() tries to load the DC but it's not in the DB anymore
208        //		// If $delete_main_table is true, avoid getting the collection object
209        //		$exec_delete = false;
210        //		if ($delete_main_table) {
211        //			$exec_delete = true;
212        //		}
213        //		if (!$exec_delete && $this->getCollectionObject()->getFirstVisibleTableId() != $this->getId()) {
214        //			$exec_delete = true;
215        //		}
216        if (!$delete_only_content) {
217            $query = "DELETE FROM il_dcl_table WHERE id = " . $ilDB->quote($this->getId(), "integer");
218            $ilDB->manipulate($query);
219        }
220    }
221
222
223    /**
224     * @param bool $create_views
225     */
226    public function doCreate($create_tablefield_setting = true, $create_standardview = true)
227    {
228        global $DIC;
229        $ilDB = $DIC['ilDB'];
230
231        $id = $ilDB->nextId("il_dcl_table");
232        $this->setId($id);
233        $query = "INSERT INTO il_dcl_table (" . "id" . ", obj_id" . ", title" . ", add_perm" . ", edit_perm" . ", delete_perm" . ", edit_by_owner"
234            . ", limited" . ", limit_start" . ", limit_end" . ", is_visible" . ", export_enabled" . ", import_enabled" . ", default_sort_field_id"
235            . ", default_sort_field_order" . ", description" . ", public_comments" . ", view_own_records_perm"
236            . ", delete_by_owner, save_confirmation , table_order ) VALUES (" . $ilDB->quote($this->getId(), "integer") . ","
237            . $ilDB->quote($this->getObjId(), "integer") . "," . $ilDB->quote($this->getTitle(), "text") . ","
238            . $ilDB->quote($this->getAddPerm() ? 1 : 0, "integer") . "," . $ilDB->quote($this->getEditPerm() ? 1 : 0, "integer") . ","
239            . $ilDB->quote($this->getDeletePerm() ? 1 : 0, "integer") . "," . $ilDB->quote($this->getEditByOwner() ? 1 : 0, "integer") . ","
240            . $ilDB->quote($this->getLimited() ? 1 : 0, "integer") . "," . $ilDB->quote($this->getLimitStart(), "timestamp") . ","
241            . $ilDB->quote($this->getLimitEnd(), "timestamp") . "," . $ilDB->quote($this->getIsVisible() ? 1 : 0, "integer") . ","
242            . $ilDB->quote($this->getExportEnabled() ? 1 : 0, "integer") . "," . $ilDB->quote($this->getImportEnabled() ? 1 : 0, "integer") . ","
243            . $ilDB->quote($this->getDefaultSortField(), "text") . "," . $ilDB->quote($this->getDefaultSortFieldOrder(), "text") . ","
244            . $ilDB->quote($this->getDescription(), "text") . "," . $ilDB->quote($this->getPublicCommentsEnabled(), "integer") . ","
245            . $ilDB->quote($this->getViewOwnRecordsPerm(), "integer") . "," . $ilDB->quote($this->getDeleteByOwner() ? 1 : 0, 'integer') . ","
246            . $ilDB->quote($this->getSaveConfirmation() ? 1 : 0, 'integer') . "," . $ilDB->quote($this->getOrder(), 'integer') . ")";
247
248        $ilDB->manipulate($query);
249
250        if ($create_standardview) {
251            //standard tableview
252            ilDclTableView::createOrGetStandardView($this->id);
253        }
254
255        if ($create_tablefield_setting) {
256            $this->buildOrderFields();
257        }
258    }
259
260
261    /*
262     * doUpdate
263     */
264    public function doUpdate()
265    {
266        global $DIC;
267        $ilDB = $DIC['ilDB'];
268
269        $ilDB->update(
270            "il_dcl_table",
271            array(
272            "obj_id" => array("integer", $this->getObjId()),
273            "title" => array("text", $this->getTitle()),
274            "add_perm" => array("integer", (int) $this->getAddPerm()),
275            "edit_perm" => array("integer", (int) $this->getEditPerm()),
276            "delete_perm" => array("integer", (int) $this->getDeletePerm()),
277            "edit_by_owner" => array("integer", (int) $this->getEditByOwner()),
278            "limited" => array("integer", $this->getLimited()),
279            "limit_start" => array("timestamp", $this->getLimitStart()),
280            "limit_end" => array("timestamp", $this->getLimitEnd()),
281            "is_visible" => array("integer", $this->getIsVisible() ? 1 : 0),
282            "export_enabled" => array("integer", $this->getExportEnabled() ? 1 : 0),
283            "import_enabled" => array("integer", $this->getImportEnabled() ? 1 : 0),
284            "description" => array("text", $this->getDescription()),
285            "default_sort_field_id" => array("text", $this->getDefaultSortField()),
286            "default_sort_field_order" => array("text", $this->getDefaultSortFieldOrder()),
287            "public_comments" => array("integer", $this->getPublicCommentsEnabled() ? 1 : 0),
288            "view_own_records_perm" => array("integer", $this->getViewOwnRecordsPerm()),
289            'delete_by_owner' => array('integer', $this->getDeleteByOwner() ? 1 : 0),
290            'save_confirmation' => array('integer', $this->getSaveConfirmation() ? 1 : 0),
291            'table_order' => array('integer', $this->getOrder()),
292        ),
293            array(
294                "id" => array("integer", $this->getId()),
295            )
296        );
297    }
298
299
300    /**
301     * Set table id
302     *
303     * @param int $a_id
304     */
305    public function setId($a_id)
306    {
307        $this->id = $a_id;
308    }
309
310
311    /**
312     * Get table id
313     *
314     * @return int
315     */
316    public function getId()
317    {
318        return $this->id;
319    }
320
321
322    /**
323     * @param $a_id
324     */
325    public function setObjId($a_id)
326    {
327        $this->objId = $a_id;
328    }
329
330
331    /**
332     * @return int
333     */
334    public function getObjId()
335    {
336        return $this->objId;
337    }
338
339
340    /**
341     * @param $a_title
342     */
343    public function setTitle($a_title)
344    {
345        $this->title = $a_title;
346    }
347
348
349    /**
350     * @return string
351     */
352    public function getTitle()
353    {
354        return $this->title;
355    }
356
357
358    /**
359     * @return ilObjDataCollection
360     */
361    public function getCollectionObject()
362    {
363        $this->loadObj();
364
365        return $this->obj;
366    }
367
368
369    protected function loadObj()
370    {
371        if ($this->obj == null) {
372            $this->obj = new ilObjDataCollection($this->objId, false);
373        }
374    }
375
376
377    /**
378     * @return ilDclBaseRecordModel[]
379     */
380    public function getRecords()
381    {
382        if ($this->records == null) {
383            $this->loadRecords();
384        }
385
386        return $this->records;
387    }
388
389
390    public function loadRecords()
391    {
392        global $DIC;
393        $ilDB = $DIC['ilDB'];
394
395        $records = array();
396        $query = "SELECT id FROM il_dcl_record WHERE table_id = " . $ilDB->quote($this->id, "integer");
397        $set = $ilDB->query($query);
398
399        while ($rec = $ilDB->fetchAssoc($set)) {
400            $records[$rec['id']] = ilDclCache::getRecordCache($rec['id']);
401        }
402
403        $this->records = $records;
404    }
405
406
407    /**
408     * @param $field_id
409     */
410    public function deleteField($field_id)
411    {
412        $field = ilDclCache::getFieldCache($field_id);
413        $records = $this->getRecords();
414
415        foreach ($records as $record) {
416            $record->deleteField($field_id);
417        }
418
419        $field->doDelete();
420    }
421
422
423    /**
424     * @param $field_id
425     *
426     * @return ilDclBaseFieldModel|null
427     */
428    public function getField($field_id)
429    {
430        $fields = $this->getFields();
431        $field = null;
432        foreach ($fields as $field_1) {
433            if ($field_1->getId() == $field_id) {
434                $field = $field_1;
435            }
436        }
437
438        return $field;
439    }
440
441
442    /**
443     * @param bool $force_include_comments
444     *
445     * @return array
446     */
447    public function getFieldIds()
448    {
449        $field_ids = array();
450        foreach ($this->getFields() as $field) {
451            if ($field->getId()) {
452                $field_ids[] = $field->getId();
453            }
454        }
455
456        return $field_ids;
457    }
458
459
460    protected function loadCustomFields()
461    {
462        if (!$this->fields) {
463            global $DIC;
464            $ilDB = $DIC['ilDB'];
465            /**
466             * @var $ilDB ilDBInterface
467             */
468            $query
469                = "SELECT DISTINCT il_dcl_field.*, il_dcl_tfield_set.field_order
470						    FROM il_dcl_field
471						         INNER JOIN il_dcl_tfield_set
472						            ON (    il_dcl_tfield_set.field NOT IN ('owner',
473						                                                    'last_update',
474						                                                    'last_edit_by',
475						                                                    'id',
476						                                                    'create_date')
477						                AND il_dcl_tfield_set.table_id = il_dcl_field.table_id
478						                AND il_dcl_tfield_set.field = " . $ilDB->cast("il_dcl_field.id", "text") . ")
479						   WHERE il_dcl_field.table_id = %s
480						ORDER BY il_dcl_tfield_set.field_order ASC";
481
482            $set = $ilDB->queryF($query, array('integer'), array((int) $this->getId()));
483            $fields = array();
484            while ($rec = $ilDB->fetchAssoc($set)) {
485                $field = ilDclCache::buildFieldFromRecord($rec);
486                $fields[] = $field;
487            }
488            $this->fields = $fields;
489
490            ilDclCache::preloadFieldProperties($fields);
491        }
492    }
493
494
495    public function getCustomFields()
496    {
497        if (!$this->fields) {
498            $this->loadCustomFields();
499        }
500
501        return $this->fields;
502    }
503
504
505    /**
506     * getNewOrder
507     *
508     * @return int returns the place where a new field should be placed.
509     */
510    public function getNewFieldOrder()
511    {
512        $fields = $this->getFields();
513        $place = 0;
514        foreach ($fields as $field) {
515            if (!$field->isStandardField()) {
516                $place = $field->getOrder() + 1;
517            }
518        }
519
520        return $place;
521    }
522
523
524    /**
525     * @return int
526     */
527    public function getNewTableviewOrder()
528    {
529        return (ilDclTableView::getCountForTableId($this->getId()) + 1) * 10;
530    }
531
532
533    /**
534     * @param ilDclTableView[] $tableviews
535     */
536    public function sortTableViews(array $tableviews = null)
537    {
538        if ($tableviews == null) {
539            $tableviews = $this->getTableViews();
540        }
541
542        $order = 10;
543        foreach ($tableviews as $tableview) {
544            $tableview->setTableviewOrder($order);
545            $tableview->update();
546            $order += 10;
547        }
548    }
549
550
551    /**
552     * Returns all fields of this table including the standard fields
553     *
554     * @param bool $force_include_comments by default false, so comments will only load when enabled in tablesettings
555     *
556     * @return ilDclBaseFieldModel[]
557     */
558    public function getFields()
559    {
560        if ($this->all_fields == null) {
561            $this->reloadFields();
562        }
563
564        return $this->all_fields;
565    }
566
567
568    public function reloadFields()
569    {
570        $this->loadCustomFields();
571        $this->stdFields = $this->getStandardFields();
572        $fields = array_merge($this->fields, $this->stdFields);
573
574        $this->sortByOrder($fields);
575
576        $this->all_fields = $fields;
577    }
578
579
580    /**
581     * @return ilDclTableView[] all tableviews ordered by tableview_order
582     */
583    public function getTableViews()
584    {
585        return ilDclTableView::getAllForTableId($this->getId());
586    }
587
588
589    /**
590     * For current user
591     *
592     * @param int $ref_id DataCollections reference
593     * @param int $user_id
594     *
595     * @return ilDclTableView[]
596     */
597    public function getVisibleTableViews($ref_id, $with_active_detailedview = false, $user_id = 0)
598    {
599        if (ilObjDataCollectionAccess::hasWriteAccess($ref_id, $user_id) && !$with_active_detailedview) {
600            return $this->getTableViews();
601        }
602
603        $visible_views = array();
604        foreach ($this->getTableViews() as $tableView) {
605            if (ilObjDataCollectionAccess::hasAccessToTableView($tableView, $user_id)) {
606                if (!$with_active_detailedview || ilDclDetailedViewDefinition::isActive($tableView->getId())) {
607                    $visible_views[] = $tableView;
608                }
609            }
610        }
611
612        return $visible_views;
613    }
614
615
616    /**
617     * get id of first (for current user) available view
618     *
619     * @param     $ref_id
620     * @param int $user_id
621     *
622     * @return bool
623     */
624    public function getFirstTableViewId($ref_id, $user_id = 0)
625    {
626        $tableview = array_shift($this->getVisibleTableViews($ref_id, false, $user_id));
627
628        return $tableview ? $tableview->getId() : false;
629    }
630
631
632    /**
633     * Returns all fields of this table including the standard fields, wich are supported for formulas
634     *
635     * @return ilDclBaseFieldModel[]
636     */
637    public function getFieldsForFormula()
638    {
639        $unsupported = array(
640            ilDclDatatype::INPUTFORMAT_ILIAS_REF,
641            ilDclDatatype::INPUTFORMAT_FORMULA,
642            ilDclDatatype::INPUTFORMAT_MOB,
643            ilDclDatatype::INPUTFORMAT_REFERENCELIST,
644            ilDclDatatype::INPUTFORMAT_REFERENCE,
645            ilDclDatatype::INPUTFORMAT_FILE,
646            ilDclDatatype::INPUTFORMAT_RATING,
647        );
648
649        $this->loadCustomFields();
650        $return = $this->getStandardFields();
651        /**
652         * @var $field ilDclBaseFieldModel
653         */
654        foreach ($this->fields as $field) {
655            if (!in_array($field->getDatatypeId(), $unsupported)) {
656                $return[] = $field;
657            }
658        }
659
660        return $return;
661    }
662
663
664    /**
665     * Returns the fields all datacollections have by default.
666     * Comments are only included if active in this table
667     *
668     * @return ilDclStandardField[]
669     */
670    public function getStandardFields()
671    {
672        if ($this->stdFields == null) {
673            $this->stdFields = ilDclStandardField::_getStandardFields($this->id);
674            // Don't return comments as field if this feature is not activated in the settings
675            if (!$this->getPublicCommentsEnabled()) {
676                /** @var $field ilDclStandardField */
677                foreach ($this->stdFields as $k => $field) {
678                    if ($field->getId() == 'comments') {
679                        unset($this->stdFields[$k]);
680                        break;
681                    }
682                }
683            }
684        }
685
686        return $this->stdFields;
687    }
688
689
690    /**
691     * Returns all fields of this table which are NOT standard fields.
692     *
693     * @return ilDclBaseFieldModel[]
694     */
695    public function getRecordFields()
696    {
697        $this->loadCustomFields();
698
699        return $this->fields;
700    }
701
702
703    /**
704     * @return array
705     */
706    public function getEditableFields()
707    {
708        $fields = $this->getRecordFields();
709        $editableFields = array();
710
711        foreach ($fields as $field) {
712            if (!$field->getLocked()) {
713                $editableFields[] = $field;
714            }
715        }
716
717        return $editableFields;
718    }
719
720
721    /**
722     * Return all the fields that are marked as exportable
723     *
724     * @return array ilDclBaseFieldModel
725     */
726    public function getExportableFields()
727    {
728        $fields = $this->getFields();
729        $exportableFields = array();
730        foreach ($fields as $field) {
731            if ($field->getExportable()) {
732                $exportableFields[] = $field;
733            }
734        }
735
736        return $exportableFields;
737    }
738
739
740    /**
741     * @param $ref_id int the reference id of the current datacollection object
742     * @param $record ilDclBaseRecordModel the record which will be edited
743     *
744     * @return bool
745     */
746    public function hasPermissionToEditRecord($ref_id, ilDclBaseRecordModel $record)
747    {
748        if ($this->getObjId() != ilObjDataCollection::_lookupObjectId($ref_id)) {
749            return false;
750        }
751        if (ilObjDataCollectionAccess::hasWriteAccess($ref_id) || ilObjDataCollectionAccess::hasEditAccess($ref_id)) {
752            return true;
753        }
754        if (!ilObjDataCollectionAccess::hasAddRecordAccess($ref_id)) {
755            return false;
756        }
757        if (!$this->checkLimit()) {
758            return false;
759        }
760        if ($this->getEditPerm() && !$this->getEditByOwner()) {
761            return true;
762        }
763        if ($this->getEditByOwner()) {
764            return $this->doesRecordBelongToUser($record);
765        }
766
767        return false;
768    }
769
770
771    /**
772     * @param $ref_id int the reference id of the current datacollection object
773     * @param $record ilDclBaseRecordModel the record which will be deleted
774     *
775     * @return bool
776     */
777    public function hasPermissionToDeleteRecord($ref_id, ilDclBaseRecordModel $record)
778    {
779        if ($this->getObjId() != ilObjDataCollection::_lookupObjectId($ref_id)) {
780            return false;
781        }
782        if (ilObjDataCollectionAccess::hasWriteAccess($ref_id)) {
783            return true;
784        }
785        if (!ilObjDataCollectionAccess::hasAddRecordAccess($ref_id)) {
786            return false;
787        }
788        if (!$this->checkLimit()) {
789            return false;
790        }
791        if ($this->getDeletePerm() && !$this->getDeleteByOwner()) {
792            return true;
793        }
794        if ($this->getDeleteByOwner()) {
795            return $this->doesRecordBelongToUser($record);
796        }
797
798        return false;
799    }
800
801
802    /**
803     * @param $ref_id
804     *
805     * @return bool
806     */
807    public function hasPermissionToDeleteRecords($ref_id)
808    {
809        if ($this->getObjId() != ilObjDataCollection::_lookupObjectId($ref_id)) {
810            return false;
811        }
812
813        return ((ilObjDataCollectionAccess::hasAddRecordAccess($ref_id) && $this->getDeletePerm())
814            || ilObjDataCollectionAccess::hasWriteAccess($ref_id));
815    }
816
817
818    /**
819     * @param int $ref_id
820     * @param     $record ilDclBaseRecordModel
821     * @param int $user_id
822     *
823     * @return bool
824     */
825    public function hasPermissionToViewRecord($ref_id, $record, $user_id = 0)
826    {
827        global $DIC;
828        $ilUser = $DIC['ilUser'];
829        if ($this->getObjId() != ilObjDataCollection::_lookupObjectId($ref_id)) {
830            return false;
831        }
832        if (ilObjDataCollectionAccess::hasWriteAccess($ref_id, $user_id) || ilObjDataCollectionAccess::hasEditAccess($ref_id, $user_id)) {
833            return true;
834        }
835        if (ilObjDataCollectionAccess::hasReadAccess($ref_id)) {
836            // Check for view only own entries setting
837            if ($this->getViewOwnRecordsPerm() && ($user_id ? $user_id : $ilUser->getId()) != $record->getOwner()) {
838                return false;
839            }
840
841            return true;
842        }
843
844        return false;
845    }
846
847
848    /**
849     * @param ilDclBaseRecordModel $record
850     *
851     * @return bool
852     */
853    protected function doesRecordBelongToUser(ilDclBaseRecordModel $record)
854    {
855        global $DIC;
856        $ilUser = $DIC['ilUser'];
857
858        return ($ilUser->getId() == $record->getOwner());
859    }
860
861
862    /**
863     * @return bool
864     */
865    public function checkLimit()
866    {
867        if ($this->getLimited()) {
868            $now = new ilDateTime(date("Y-m-d H:i:s"), IL_CAL_DATE);
869            $from = new ilDateTime($this->getLimitStart(), IL_CAL_DATE);
870            $to = new ilDateTime($this->getLimitEnd(), IL_CAL_DATE);
871
872            return ($from <= $now && $now <= $to);
873        }
874
875        return true;
876    }
877
878
879    /**
880     * Update fields
881     */
882    public function updateFields()
883    {
884        foreach ($this->getFields() as $field) {
885            $field->doUpdate();
886        }
887    }
888
889
890    /**
891     * sortFields
892     *
893     * @param $fields ilDclBaseFieldModel[]
894     */
895    public function sortFields(&$fields)
896    {
897        $this->sortByOrder($fields);
898        //After sorting the array loses it's keys respectivly their keys are set form $field->id to 1,2,3... so we reset the keys.
899        $named = array();
900        foreach ($fields as $field) {
901            $named[$field->getId()] = $field;
902        }
903
904        $fields = $named;
905    }
906
907
908    /**
909     *
910     * @param $array ilDclBaseFieldModel[] the array to sort
911     */
912    protected function sortByOrder(&$array)
913    {
914        // php-bug: https://bugs.php.net/bug.php?id=50688
915        // fixed in php 7 but for now we need the @ a workaround
916        @usort($array, array($this, "compareOrder"));
917    }
918
919
920    /**
921     * buildOrderFields
922     * orders the fields.
923     */
924    public function buildOrderFields()
925    {
926        $fields = $this->getFields();
927        $this->sortByOrder($fields);
928        $count = 10;
929        $offset = 10;
930        foreach ($fields as $field) {
931            if (!is_null($field->getOrder())) {
932                $field->setOrder($count);
933                $count = $count + $offset;
934                $field->doUpdate();
935            }
936        }
937    }
938
939
940    /**
941     * Get a field by title
942     *
943     * @param $title
944     *
945     * @return ilDclBaseFieldModel
946     */
947    public function getFieldByTitle($title)
948    {
949        $return = null;
950        foreach ($this->getFields() as $field) {
951            if ($field->getTitle() == $title) {
952                $return = $field;
953                break;
954            }
955        }
956
957        return $return;
958    }
959
960
961    /**
962     * @param boolean $add_perm
963     */
964    public function setAddPerm($add_perm)
965    {
966        $this->add_perm = $add_perm;
967    }
968
969
970    /**
971     * @return boolean
972     */
973    public function getAddPerm()
974    {
975        return (bool) $this->add_perm;
976    }
977
978
979    /**
980     * @param boolean $delete_perm
981     */
982    public function setDeletePerm($delete_perm)
983    {
984        $this->delete_perm = $delete_perm;
985        if (!$delete_perm) {
986            $this->setDeleteByOwner(false);
987        }
988    }
989
990
991    /**
992     * @return boolean
993     */
994    public function getDeletePerm()
995    {
996        return (bool) $this->delete_perm;
997    }
998
999
1000    /**
1001     * @param boolean $edit_by_owner
1002     */
1003    public function setEditByOwner($edit_by_owner)
1004    {
1005        $this->edit_by_owner = $edit_by_owner;
1006        if ($edit_by_owner) {
1007            $this->setEditPerm(true);
1008        }
1009    }
1010
1011
1012    /**
1013     * @return boolean
1014     */
1015    public function getEditByOwner()
1016    {
1017        return (bool) $this->edit_by_owner;
1018    }
1019
1020
1021    /**
1022     * @return boolean
1023     */
1024    public function getDeleteByOwner()
1025    {
1026        return (bool) $this->delete_by_owner;
1027    }
1028
1029
1030    /**
1031     * @param boolean $delete_by_owner
1032     */
1033    public function setDeleteByOwner($delete_by_owner)
1034    {
1035        $this->delete_by_owner = $delete_by_owner;
1036        if ($delete_by_owner) {
1037            $this->setDeletePerm(true);
1038        }
1039    }
1040
1041
1042    /**
1043     * @param boolean $edit_perm
1044     */
1045    public function setEditPerm($edit_perm)
1046    {
1047        $this->edit_perm = $edit_perm;
1048        if (!$edit_perm) {
1049            $this->setEditByOwner(false);
1050        }
1051    }
1052
1053
1054    /**
1055     * @return boolean
1056     */
1057    public function getEditPerm()
1058    {
1059        return (bool) $this->edit_perm;
1060    }
1061
1062
1063    /**
1064     * @param boolean $limited
1065     */
1066    public function setLimited($limited)
1067    {
1068        $this->limited = $limited;
1069    }
1070
1071
1072    /**
1073     * @return boolean
1074     */
1075    public function getLimited()
1076    {
1077        return $this->limited;
1078    }
1079
1080
1081    /**
1082     * @param string $limit_end
1083     */
1084    public function setLimitEnd($limit_end)
1085    {
1086        $this->limit_end = $limit_end;
1087    }
1088
1089
1090    /**
1091     * @return string
1092     */
1093    public function getLimitEnd()
1094    {
1095        return $this->limit_end;
1096    }
1097
1098
1099    /**
1100     * @param string $limit_start
1101     */
1102    public function setLimitStart($limit_start)
1103    {
1104        $this->limit_start = $limit_start;
1105    }
1106
1107
1108    /**
1109     * @return string
1110     */
1111    public function getLimitStart()
1112    {
1113        return $this->limit_start;
1114    }
1115
1116
1117    /**
1118     * @param boolean $is_visible
1119     */
1120    public function setIsVisible($is_visible)
1121    {
1122        $this->is_visible = $is_visible;
1123    }
1124
1125
1126    /**
1127     * @return boolean
1128     */
1129    public function getIsVisible()
1130    {
1131        return $this->is_visible;
1132    }
1133
1134
1135    /**
1136     * @param string $description
1137     */
1138    public function setDescription($description)
1139    {
1140        $this->description = $description;
1141    }
1142
1143
1144    /**
1145     * @return string
1146     */
1147    public function getDescription()
1148    {
1149        return $this->description;
1150    }
1151
1152
1153    /**
1154     *
1155     * /**
1156     * @param string $default_sort_field
1157     */
1158    public function setDefaultSortField($default_sort_field)
1159    {
1160        $default_sort_field = ($default_sort_field) ? $default_sort_field : 0; // Change null or empty strings to zero
1161        $this->default_sort_field = $default_sort_field;
1162    }
1163
1164
1165    /**
1166     * @return string
1167     */
1168    public function getDefaultSortField()
1169    {
1170        return $this->default_sort_field;
1171    }
1172
1173
1174    /**
1175     * @param string $default_sort_field_order
1176     */
1177    public function setDefaultSortFieldOrder($default_sort_field_order)
1178    {
1179        if (!in_array($default_sort_field_order, array('asc', 'desc'))) {
1180            $default_sort_field_order = 'asc';
1181        }
1182        $this->default_sort_field_order = $default_sort_field_order;
1183    }
1184
1185
1186    /**
1187     * @return string
1188     */
1189    public function getDefaultSortFieldOrder()
1190    {
1191        return $this->default_sort_field_order;
1192    }
1193
1194
1195    /**
1196     * @param boolean $public_comments
1197     */
1198    public function setPublicCommentsEnabled($public_comments)
1199    {
1200        $this->public_comments = $public_comments;
1201    }
1202
1203
1204    /**
1205     * @return boolean
1206     */
1207    public function getPublicCommentsEnabled()
1208    {
1209        return $this->public_comments;
1210    }
1211
1212
1213    /**
1214     * @param boolean $view_own_perm
1215     */
1216    public function setViewOwnRecordsPerm($view_own_perm)
1217    {
1218        $this->view_own_records_perm = (int) $view_own_perm;
1219    }
1220
1221
1222    /**
1223     * @return boolean
1224     */
1225    public function getViewOwnRecordsPerm()
1226    {
1227        return (bool) $this->view_own_records_perm;
1228    }
1229
1230
1231    /**
1232     * @return boolean
1233     */
1234    public function getSaveConfirmation()
1235    {
1236        return $this->save_confirmation;
1237    }
1238
1239
1240    /**
1241     * @param boolean $save_confirmation
1242     */
1243    public function setSaveConfirmation($save_confirmation)
1244    {
1245        $this->save_confirmation = $save_confirmation;
1246    }
1247
1248
1249    /**
1250     * hasCustomFields
1251     *
1252     * @return boolean
1253     */
1254    public function hasCustomFields()
1255    {
1256        $this->loadCustomFields();
1257
1258        return (count($this->fields) > 0) ? true : false;
1259    }
1260
1261
1262    /**
1263     * @param $a
1264     * @param $b
1265     *
1266     * @return int
1267     */
1268    public function compareOrder($a, $b)
1269    {
1270        if (is_null($a->getOrder() == null) && is_null($b->getOrder() == null)) {
1271            return 0;
1272        }
1273        if (is_null($a->getOrder())) {
1274            return 1;
1275        }
1276        if (is_null($b->getOrder())) {
1277            return -1;
1278        }
1279
1280        return $a->getOrder() < $b->getOrder() ? -1 : 1;
1281    }
1282
1283
1284    /**
1285     * @param ilDclTable $original
1286     */
1287    public function cloneStructure(ilDclTable $original)
1288    {
1289        $this->setTitle($original->getTitle());
1290        $this->setDescription($original->getDescription());
1291        $this->setIsVisible($original->getIsVisible());
1292        $this->setEditByOwner($original->getEditByOwner());
1293        $this->setAddPerm($original->getAddPerm());
1294        $this->setEditPerm($original->getEditPerm());
1295        $this->setDeleteByOwner($original->getDeleteByOwner());
1296        $this->setSaveConfirmation($original->getSaveConfirmation());
1297        $this->setDeletePerm($original->getDeletePerm());
1298        $this->setLimited($original->getLimited());
1299        $this->setLimitStart($original->getLimitStart());
1300        $this->setLimitEnd($original->getLimitEnd());
1301        $this->setViewOwnRecordsPerm($original->getViewOwnRecordsPerm());
1302        $this->setExportEnabled($original->getExportEnabled());
1303        $this->setImportEnabled($original->getImportEnabled());
1304        $this->setPublicCommentsEnabled($original->getPublicCommentsEnabled());
1305        $this->setDefaultSortFieldOrder($original->getDefaultSortFieldOrder());
1306        $this->setOrder($original->getOrder());
1307
1308        $this->doCreate(true, false);
1309        // reset stdFields to get new for the created object
1310
1311        $default_sort_field = 0;
1312        // Clone standard-fields
1313        $org_std_fields = $original->getStandardFields();
1314        foreach ($this->getStandardFields() as $element_key => $std_field) {
1315            $std_field->cloneStructure($org_std_fields[$element_key]);
1316            if ($std_field->getId() === $original->getDefaultSortField()) {
1317                $default_sort_field = $std_field->getId();
1318            }
1319        }
1320
1321        // Clone fields
1322        $new_fields = array();
1323        foreach ($original->getFields() as $orig_field) {
1324            if (!$orig_field->isStandardField()) {
1325                $class_name = get_class($orig_field);
1326                $new_field = new $class_name();
1327                $new_field->setTableId($this->getId());
1328                $new_field->cloneStructure($orig_field->getId());
1329                $new_fields[$orig_field->getId()] = $new_field;
1330
1331                if ($orig_field->getId() === $original->getDefaultSortField()) {
1332                    $default_sort_field = $new_field->getId();
1333                }
1334            }
1335        }
1336
1337        $this->setDefaultSortField($default_sort_field);
1338        $this->doUpdate();
1339
1340        // Clone Records with recordfields
1341        foreach ($original->getRecords() as $orig_record) {
1342            $new_record = new ilDclBaseRecordModel();
1343            $new_record->setTableId($this->getId());
1344            $new_record->cloneStructure($orig_record->getId(), $new_fields);
1345        }
1346
1347        //clone tableviews (includes pageobjects)
1348        foreach ($original->getTableViews() as $orig_tableview) {
1349            $new_tableview = new ilDclTableView();
1350            $new_tableview->setTableId($this->getId());
1351            $new_tableview->cloneStructure($orig_tableview, $new_fields);
1352        }
1353
1354        // mandatory for all cloning functions
1355        ilDclCache::setCloneOf($original->getId(), $this->getId(), ilDclCache::TYPE_TABLE);
1356    }
1357
1358
1359    /**
1360     *
1361     */
1362    public function afterClone()
1363    {
1364        foreach ($this->getFields() as $field) {
1365            $field->afterClone($this->getRecords());
1366        }
1367    }
1368
1369
1370    /**
1371     * _hasRecords
1372     *
1373     * @return boolean
1374     */
1375    public function _hasRecords()
1376    {
1377        return (count($this->getRecords()) > 0) ? true : false;
1378    }
1379
1380
1381    /**
1382     * @param $field ilDclBaseFieldModel add an already created field for eg. ordering.
1383     */
1384    public function addField($field)
1385    {
1386        $this->all_fields[$field->getId()] = $field;
1387    }
1388
1389
1390    /**
1391     * @param $table_id int
1392     *
1393     * @return bool returns true iff there exists a table with id $table_id
1394     */
1395    public static function _tableExists($table_id)
1396    {
1397        global $DIC;
1398        $ilDB = $DIC['ilDB'];
1399        $query = "SELECT * FROM il_dcl_table WHERE id = " . $table_id;
1400        $result = $ilDB->query($query);
1401
1402        return $result->numRows() != 0;
1403    }
1404
1405
1406    /**
1407     * @param $title  Title of table
1408     * @param $obj_id DataCollection object ID where the table belongs to
1409     *
1410     * @return int
1411     */
1412    public static function _getTableIdByTitle($title, $obj_id)
1413    {
1414        global $DIC;
1415        $ilDB = $DIC['ilDB'];
1416        $result = $ilDB->query(
1417            'SELECT id FROM il_dcl_table WHERE title = ' . $ilDB->quote($title, 'text') . ' AND obj_id = '
1418            . $ilDB->quote($obj_id, 'integer')
1419        );
1420        $id = 0;
1421        while ($rec = $ilDB->fetchAssoc($result)) {
1422            $id = $rec['id'];
1423        }
1424
1425        return $id;
1426    }
1427
1428
1429    /**
1430     * @param boolean $export_enabled
1431     */
1432    public function setExportEnabled($export_enabled)
1433    {
1434        $this->export_enabled = $export_enabled;
1435    }
1436
1437
1438    /**
1439     * @return boolean
1440     */
1441    public function getExportEnabled()
1442    {
1443        return $this->export_enabled;
1444    }
1445
1446
1447    /**
1448     * @return int
1449     */
1450    public function getOrder()
1451    {
1452        if (!$this->table_order) {
1453            $this->updateOrder();
1454        }
1455
1456        return $this->table_order;
1457    }
1458
1459
1460    /**
1461     *
1462     */
1463    public function updateOrder()
1464    {
1465        global $DIC;
1466        $ilDB = $DIC['ilDB'];
1467        $result = $ilDB->query('SELECT MAX(table_order) AS table_order FROM il_dcl_table WHERE obj_id = ' . $ilDB->quote($this->getCollectionObject()->getId(), 'integer'));
1468        $this->table_order = $ilDB->fetchObject($result)->table_order + 10;
1469        $ilDB->query('UPDATE il_dcl_table SET table_order = ' . $ilDB->quote($this->table_order, 'integer') . ' WHERE id = ' . $ilDB->quote($this->getId(), 'integer'));
1470    }
1471
1472
1473    /**
1474     * @param int $table_order
1475     */
1476    public function setOrder($table_order)
1477    {
1478        $this->table_order = $table_order;
1479    }
1480
1481
1482    /**
1483     * @param boolean $import_enabled
1484     */
1485    public function setImportEnabled($import_enabled)
1486    {
1487        $this->import_enabled = $import_enabled;
1488    }
1489
1490
1491    /**
1492     * @return boolean
1493     */
1494    public function getImportEnabled()
1495    {
1496        return $this->import_enabled;
1497    }
1498
1499
1500    /**
1501     * Checks if a table has a field with the given title
1502     *
1503     * @param $title  Title of field
1504     * @param $obj_id Obj-ID of the table
1505     *
1506     * @return bool
1507     */
1508    public static function _hasFieldByTitle($title, $obj_id)
1509    {
1510        global $DIC;
1511        $ilDB = $DIC['ilDB'];
1512        $result = $ilDB->query(
1513            'SELECT * FROM il_dcl_field WHERE table_id = ' . $ilDB->quote($obj_id, 'integer') . ' AND title = '
1514            . $ilDB->quote($title, 'text')
1515        );
1516
1517        return ($ilDB->numRows($result)) ? true : false;
1518    }
1519
1520
1521    /**
1522     * Return only the needed subset of record objects for the table, according to sorting, paging and filters
1523     *
1524     * @param string $sort      Title of a field where the ilTable2GUI is sorted
1525     * @param string $direction 'desc' or 'asc'
1526     * @param int    $limit     Limit of records
1527     * @param int    $offset    Offset from records
1528     * @param array  $filter    Containing the filter values
1529     *
1530     * @return array Array with two keys: 'record' => Contains the record objects, 'total' => Number of total records (without slicing)
1531     */
1532    public function getPartialRecords($sort, $direction, $limit, $offset, array $filter = array())
1533    {
1534        global $DIC;
1535        $ilDB = $DIC['ilDB'];
1536        /**
1537         * @var $ilDB ilDBInterface
1538         */
1539        $ilUser = $DIC['ilUser'];
1540        $rbacreview = $DIC['rbacreview'];
1541
1542        $sort_field = ($sort) ? $this->getFieldByTitle($sort) : $this->getField('id');
1543        $direction = strtolower($direction);
1544        $direction = (in_array($direction, array('desc', 'asc'))) ? $direction : 'asc';
1545
1546        // Sorting by a status from an ILIAS Ref field. This column is added dynamically to the table, there is no field model
1547        $sort_by_status = false;
1548        if (substr($sort, 0, 8) == '_status_') {
1549            $sort_by_status = true;
1550            $sort_field = $this->getFieldByTitle(substr($sort, 8));
1551        }
1552
1553        if (is_null($sort_field)) {
1554            $sort_field = $this->getField('id');
1555        }
1556
1557        $sort_query_object = $sort_field->getRecordQuerySortObject($direction, $sort_by_status);
1558
1559        $select_str = ($sort_query_object != null) ? $sort_query_object->getSelectStatement() : '';
1560        $join_str = ($sort_query_object != null) ? $sort_query_object->getJoinStatement() : '';
1561        $where_str = ($sort_query_object != null) ? $sort_query_object->getWhereStatement() : '';
1562        $order_str = ($sort_query_object != null) ? $sort_query_object->getOrderStatement() : '';
1563        $group_str = ($sort_query_object != null) ? $sort_query_object->getGroupStatement() : '';
1564
1565        if (count($filter)) {
1566            foreach ($filter as $key => $filter_value) {
1567                $filter_field_id = substr($key, 7);
1568                $filter_field = $this->getField($filter_field_id);
1569                $filter_record_query_object = $filter_field->getRecordQueryFilterObject($filter_value, $sort_field);
1570
1571                if ($filter_record_query_object) {
1572                    $select_str .= $filter_record_query_object->getSelectStatement();
1573                    $join_str .= $filter_record_query_object->getJoinStatement();
1574                    $where_str .= $filter_record_query_object->getWhereStatement();
1575                    $group_str .= $filter_record_query_object->getGroupStatement();
1576                }
1577            }
1578        }
1579
1580        // Build the query string
1581        $sql = "SELECT DISTINCT record.id, record.owner";
1582        if ($select_str) {
1583            $sql .= ', ';
1584        }
1585
1586        $as = ' AS ';
1587
1588        $sql .= rtrim($select_str, ',') . " FROM il_dcl_record {$as} record ";
1589        $sql .= $join_str;
1590        $sql .= " WHERE record.table_id = " . $ilDB->quote($this->getId(), 'integer');
1591
1592        if (strlen($where_str) > 0) {
1593            $sql .= $where_str;
1594        }
1595
1596        if (strlen($group_str) > 0) {
1597            $sql .= " GROUP BY " . $group_str;
1598        }
1599
1600        if (strlen($order_str) > 0) {
1601            $sql .= " ORDER BY " . $order_str;
1602        }
1603
1604        //var_dump($sql);
1605        /*global $DIC;
1606        /*$ilLog = $DIC['ilLog'];
1607        $ilLog->write($sql, ilLogLevel::CRITICAL);*/
1608
1609        $set = $ilDB->query($sql);
1610        $total_record_ids = array();
1611        // Save record-ids in session to enable prev/next links in detail view
1612        $_SESSION['dcl_record_ids'] = array();
1613        $_SESSION['dcl_table_id'] = $this->getId();
1614        $ref = filter_input(INPUT_GET, 'ref_id');
1615        $is_allowed_to_view = (ilObjDataCollectionAccess::hasWriteAccess($ref) || ilObjDataCollectionAccess::hasEditAccess($ref));
1616        while ($rec = $ilDB->fetchAssoc($set)) {
1617            // Quick check if the current user is allowed to view the record
1618            if (!$is_allowed_to_view && ($this->getViewOwnRecordsPerm() && $ilUser->getId() != $rec['owner'])) {
1619                continue;
1620            }
1621            $total_record_ids[] = $rec['id'];
1622            $_SESSION['dcl_record_ids'][] = $rec['id'];
1623        }
1624
1625        if ($sort_query_object != null) {
1626            $total_record_ids = $sort_query_object->applyCustomSorting($sort_field, $total_record_ids, $direction);
1627        }
1628
1629        // Now slice the array to load only the needed records in memory
1630        $record_ids = array_slice($total_record_ids, $offset, $limit);
1631
1632        $records = array();
1633        foreach ($record_ids as $id) {
1634            $records[] = ilDclCache::getRecordCache($id);
1635        }
1636
1637        return array('records' => $records, 'total' => count($total_record_ids));
1638    }
1639}
1640