ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilDclBaseFieldModel.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 {
23  protected string $id = "";
24  protected int $table_id = 0;
25  protected string $title = "";
26  protected string $description = "";
27  protected int $datatype_id = 0;
28  protected ?int $order = null;
30  protected array $property = [];
31  protected bool $exportable = false;
36  protected ?int $storage_location_override = null;
40  public const PROP_LENGTH = "lenght";
41  public const PROP_REGEX = "regex";
42  public const PROP_REFERENCE = "table_id";
43  public const PROP_URL = "url";
44  public const PROP_REFERENCE_LINK = "reference_link";
45  public const PROP_UNIQUE = "unique";
46  public const PROP_WIDTH = "width";
47  public const PROP_HEIGHT = "height";
48  public const PROP_LEARNING_PROGRESS = "learning_progress";
49  public const PROP_ILIAS_REFERENCE_LINK = "ILIAS_reference_link";
50  public const PROP_N_REFERENCE = "multiple_selection";
51  public const PROP_FORMULA_EXPRESSION = "expression";
52  public const PROP_DISPLAY_COPY_LINK_ACTION_MENU = "display_action_menu";
53  public const PROP_LINK_DETAIL_PAGE_TEXT = "link_detail_page_text";
54  public const PROP_LINK_DETAIL_PAGE_MOB = "link_detail_page_mob";
55  public const PROP_SUPPORTED_FILE_TYPES = "supported_file_types";
56  public const PROP_PLUGIN_HOOK_NAME = "plugin_hook_name";
57  // type of table il_dcl_view
58  public const EDIT_VIEW = 2;
59  public const EXPORTABLE_VIEW = 4;
60 
61  protected ilDBInterface $db;
62  protected ilLanguage $lng;
63 
64  public function __construct(int $a_id = 0)
65  {
66  global $DIC;
67  $this->db = $DIC->database();
68  $this->lng = $DIC->language();
69 
70  if ($a_id != 0) {
71  $this->id = (string) $a_id;
72  $this->doRead();
73  }
74  }
75 
79  public static function _getTitleInvalidChars(bool $a_as_regex = true): string
80  {
81  if ($a_as_regex) {
82  return '/^[^<>\\\\":]*$/i';
83  } else {
84  return '\ < > " :';
85  }
86  }
87 
88  public static function _getFieldIdByTitle(string $title, int $table_id): int
89  {
90  global $DIC;
91  $ilDB = $DIC->database();
92 
93  $result = $ilDB->query(
94  'SELECT id FROM il_dcl_field WHERE title = ' . $ilDB->quote($title, 'text') . ' AND table_id = '
95  . $ilDB->quote($table_id, 'integer')
96  );
97  $id = 0;
98  while ($rec = $ilDB->fetchAssoc($result)) {
99  $id = $rec['id'];
100  }
101 
102  return $id;
103  }
104 
109  public function setId($a_id): void
110  {
111  $this->id = (string) $a_id;
112  }
113 
117  public function getId(): string
118  {
119  return $this->id;
120  }
121 
125  public function setTableId(int $a_id): void
126  {
127  $this->table_id = $a_id;
128  }
129 
133  public function getTableId(): int
134  {
135  return $this->table_id;
136  }
137 
141  public function setTitle(string $a_title): void
142  {
143  //title cannot begin with _ as this is saved for other purposes. make __ instead.
144  if (substr($a_title, 0, 1) == "_" && substr($a_title, 0, 2) != "__") {
145  $a_title = "_" . $a_title;
146  }
147  $this->title = $a_title;
148  }
149 
153  public function getTitle(): string
154  {
155  return $this->title;
156  }
157 
161  public function setDescription(string $a_desc): void
162  {
163  $this->description = $a_desc;
164  }
165 
169  public function getDescription(): string
170  {
171  return $this->description;
172  }
173 
177  public function setDatatypeId(int $a_id): void
178  {
179  //unset the cached datatype.
180  $this->datatype = null;
181  $this->datatype_id = $a_id;
182  }
183 
187  public function getDatatypeId(): ?int
188  {
189  if ($this->isStandardField()) {
191  }
192 
193  return $this->datatype_id;
194  }
195 
196  public function getDatatype(): ilDclDatatype
197  {
198  $this->loadDatatype();
199 
200  return $this->datatype;
201  }
202 
203  public function getDatatypeTitle(): string
204  {
205  $this->loadDatatype();
206  return $this->datatype->getTitle();
207  }
208 
209  public function getPresentationTitle(): string
210  {
211  return $this->lng->txt('dcl_' . $this->getDatatypeTitle());
212  }
213 
214  public function getPresentationDescription(): string
215  {
216  return $this->lng->txt('dcl_' . $this->getDatatypeTitle() . '_desc');
217  }
218 
222  public function getStorageLocation(): ?int
223  {
224  if ($this->getStorageLocationOverride() !== null) {
225  return $this->getStorageLocationOverride();
226  }
227 
228  $this->loadDatatype();
229 
230  return $this->datatype->getStorageLocation();
231  }
232 
236  protected function loadDatatype(): void
237  {
238  if ($this->datatype == null) {
239  $this->datatype = ilDclCache::getDatatype($this->datatype_id);
240  }
241  }
242 
246  protected function loadTableFieldSetting(): void
247  {
248  $tablefield_setting = ilDclTableFieldSetting::getInstance($this->getTableId(), $this->getId());
249  $this->exportable = $tablefield_setting->isExportable();
250  $this->order = $tablefield_setting->getFieldOrder();
251  }
252 
256  public function getExportable(): bool
257  {
258  if (!isset($this->exportable)) {
259  $this->loadExportability();
260  }
261 
262  return $this->exportable;
263  }
264 
268  private function loadExportability(): void
269  {
270  if ($this->exportable == null) {
271  $this->loadTableFieldSetting();
272  }
273  }
274 
275  public function toArray(): array
276  {
277  return (array) $this;
278  }
279 
280  public function isStandardField(): bool
281  {
282  return false;
283  }
284 
285  public function doRead(): void
286  {
287  //THEN 1 ELSE 0 END AS has_options FROM il_dcl_field f WHERE id = ".$ilDB->quote($this->getId(),"integer");
288  $query = "SELECT * FROM il_dcl_field WHERE id = " . $this->db->quote($this->getId(), "integer");
289  $set = $this->db->query($query);
290  $rec = $this->db->fetchAssoc($set);
291 
292  if ($rec) {
293  $this->setTableId($rec["table_id"]);
294  if (null !== $rec["title"]) {
295  $this->setTitle($rec["title"]);
296  }
297  if (null !== $rec["description"]) {
298  $this->setDescription($rec["description"]);
299  }
300  $this->setDatatypeId($rec["datatype_id"]);
301  }
302 
303  $this->loadProperties();
304  $this->loadTableFieldSetting();
305  }
306 
310  public function buildFromDBRecord(array $rec): void
311  {
312  $this->setId($rec["id"]);
313  $this->setTableId($rec["table_id"]);
314  $this->setTitle($rec["title"]);
315  $this->setDescription($rec["description"]);
316  $this->setDatatypeId($rec["datatype_id"]);
317  }
318 
319  public function doCreate(): void
320  {
321  if (!ilDclTable::_tableExists($this->getTableId())) {
322  throw new ilException("The field does not have a related table!");
323  }
324 
325  $id = $this->db->nextId("il_dcl_field");
326  $this->setId($id);
327  $query = "INSERT INTO il_dcl_field (" . "id" . ", table_id" . ", datatype_id" . ", title" . ", description"
328  . " ) VALUES (" . $this->db->quote($this->getId(), "integer") . "," . $this->db->quote(
329  $this->getTableId(),
330  "integer"
331  ) . ","
332  . $this->db->quote($this->getDatatypeId(), "integer") . "," . $this->db->quote($this->getTitle(), "text") . ","
333  . $this->db->quote($this->getDescription(), "text") . ")";
334  $this->db->manipulate($query);
335 
336  $this->updateTableFieldSetting();
337 
338  $this->addToTableViews();
339  }
340 
344  protected function addToTableViews(): void
345  {
346  foreach (ilDclTableView::getAllForTableId($this->table_id) as $tableview) {
347  $tableview->createFieldSetting($this->id);
348  }
349  }
350 
351  public function doUpdate(): void
352  {
353  $this->db->update(
354  "il_dcl_field",
355  [
356  "table_id" => [
357  "integer",
358  $this->getTableId(),
359  ],
360  "datatype_id" => [
361  "text",
362  $this->getDatatypeId(),
363  ],
364  "title" => [
365  "text",
366  $this->getTitle(),
367  ],
368  "description" => [
369  "text",
370  $this->getDescription(),
371  ],
372  ],
373  [
374  "id" => [
375  "integer",
376  $this->getId(),
377  ],
378  ]
379  );
380  $this->updateTableFieldSetting();
381  $this->updateProperties();
382  }
383 
387  public function updateProperties(): void
388  {
389  foreach ($this->property as $prop) {
390  $prop->store();
391  }
392  }
393 
397  protected function updateTableFieldSetting(): void
398  {
399  $tablefield_setting = ilDclTableFieldSetting::getInstance($this->getTableId(), $this->getId());
400  $tablefield_setting->setExportable($this->exportable);
401  $tablefield_setting->setFieldOrder($this->getOrder());
402  $tablefield_setting->store();
403  }
404 
408  public function doDelete(): void
409  {
410  // delete tablefield setting.
411  ilDclTableFieldSetting::getInstance($this->getTableId(), $this->getId())->delete();
412 
413  $query = "DELETE FROM il_dcl_field_prop WHERE field_id = " . $this->db->quote($this->getId(), "text");
414  $this->db->manipulate($query);
415 
416  $query = "DELETE FROM il_dcl_field WHERE id = " . $this->db->quote($this->getId(), "text");
417  $this->db->manipulate($query);
418 
419  foreach ($this->getViewSettings() as $field_setting) {
420  $field_setting->delete();
421  }
422  }
423 
427  public function getViewSettings(): array
428  {
429  return ilDclTableViewFieldSetting::where(['field' => $this->getId()])->get();
430  }
431 
432  public function getViewSetting(int $tableview_id): ilDclTableViewFieldSetting
433  {
434  return ilDclTableViewFieldSetting::getTableViewFieldSetting($this->getId(), $tableview_id);
435  }
436 
437  public function getOrder(): int
438  {
439  if ($this->order == null) {
440  $this->loadTableFieldSetting();
441  }
442 
443  return !$this->order ? 0 : $this->order;
444  }
445 
446  public function setOrder(int $order): void
447  {
448  $this->order = $order;
449  }
450 
454  protected function loadProperties(): void
455  {
456  $this->property = ilDclCache::getFieldProperties($this->getId());
457  }
458 
462  public function hasProperty(string $key): bool
463  {
464  $this->loadProperties();
465 
466  return (isset($this->property[$key]) && $this->property[$key]->getValue() != null);
467  }
468 
469  public function getProperty(string $key): mixed
470  {
471  $instance = $this->getPropertyInstance($key);
472 
473  return ($instance !== null) ? $instance->getValue() : null;
474  }
475 
476  public function getPropertyInstance(string $key): ?ilDclFieldProperty
477  {
478  $this->loadProperties();
479  if ($this->hasProperty($key)) {
480  return $this->property[$key];
481  }
482 
483  return null;
484  }
485 
486  public function setProperty(string $key, $value): ?ilDclFieldProperty
487  {
488  $this->loadProperties();
489  if (isset($this->property[$key])) {
490  $this->property[$key]->setValue($value);
491  } else {
492  $property = new ilDclFieldProperty();
493  $property->setName($key);
494  $property->setFieldId((int) $this->getId());
495  $property->setValue($value);
496 
497  $this->property[$key] = $property;
498  }
499 
500  return $this->property[$key];
501  }
502 
506  public function getValidFieldProperties(): array
507  {
508  return [];
509  }
510 
511  public function checkValidityFromForm(ilPropertyFormGUI &$form, ?int $record_id): void
512  {
513  $value = $form->getInput('field_' . $this->getId());
514  $this->checkValidity($value, $record_id);
515  }
516 
522  public function checkValidity($value, ?int $record_id): bool
523  {
524  return true;
525  }
526 
527  protected function areEqual($value_1, $value_2): bool
528  {
529  return $this->normalizeValue($value_1) === $this->normalizeValue($value_2);
530  }
531 
532  protected function normalizeValue(mixed $value)
533  {
534  if (is_string($value)) {
535  $value = trim(preg_replace("/\\s+/uism", " ", $value));
536  }
537 
538  return $value;
539  }
540 
544  public function cloneStructure(int $original_id): void
545  {
546  $original = ilDclCache::getFieldCache($original_id);
547  $this->setTitle($original->getTitle());
548  $this->setDatatypeId($original->getDatatypeId());
549  $this->setDescription($original->getDescription());
550  $this->setOrder($original->getOrder());
551  $this->setExportable($original->getExportable());
552  $this->doCreate();
553  $this->cloneProperties($original);
554 
555  // mandatory for all cloning functions
556  ilDclCache::setCloneOf($original_id, (int) $this->getId(), ilDclCache::TYPE_FIELD);
557  }
558 
559  public function afterClone(array $records)
560  {
561  foreach ($records as $rec) {
562  ilDclCache::getRecordFieldCache($rec, $this)->afterClone();
563  }
564  }
565 
566  public function cloneProperties(ilDclBaseFieldModel $originalField): void
567  {
568  $orgProps = $originalField->getValidFieldProperties();
569  if (count($orgProps) == 0) {
570  return;
571  }
572  foreach ($orgProps as $prop_name) {
573  $fieldprop_obj = new ilDclFieldProperty();
574  $fieldprop_obj->setFieldId((int) $this->getId());
575  $fieldprop_obj->setName($prop_name);
576 
577  $value = $originalField->getProperty($prop_name);
578 
579  if (
580  $prop_name == ilDclBaseFieldModel::PROP_REFERENCE &&
581  (
584  )
585  ) {
586  $value = null;
587  }
588 
589  if ($value) {
590  $fieldprop_obj->setValue($value);
591  $fieldprop_obj->create();
592  }
593  }
594  }
595 
596  public function setExportable(bool $exportable): void
597  {
598  $this->exportable = $exportable;
599  }
600 
601  public function allowFilterInListView(): bool
602  {
603  return true;
604  }
605 
610  public function getRecordQuerySortObject(
611  string $direction = "asc",
612  bool $sort_by_status = false
614  $sql_obj = new ilDclRecordQueryObject();
615 
616  $select_str = "sort_stloc_{$this->getId()}.value AS field_{$this->getId()}";
617  $join_str
618  = "LEFT JOIN il_dcl_record_field AS sort_record_field_{$this->getId()} ON (sort_record_field_{$this->getId()}.record_id = record.id AND sort_record_field_{$this->getId()}.field_id = "
619  . $this->db->quote($this->getId(), 'integer') . ") ";
620  $join_str .= "LEFT JOIN il_dcl_stloc{$this->getStorageLocation()}_value AS sort_stloc_{$this->getId()} ON (sort_stloc_{$this->getId()}.record_field_id = sort_record_field_{$this->getId()}.id)";
621 
622  $sql_obj->setSelectStatement($select_str);
623  $sql_obj->setJoinStatement($join_str);
624  $sql_obj->setOrderStatement("field_{$this->getId()} $direction, ID ASC");
625 
626  return $sql_obj;
627  }
628 
633  public function getRecordQueryFilterObject(
634  $filter_value = "",
635  ?ilDclBaseFieldModel $sort_field = null
637  return null;
638  }
639 
643  public function getSortField(): string
644  {
645  return $this->getTitle();
646  }
647 
651  public function hasNumericSorting(): bool
652  {
654  return true;
655  }
656 
657  return false;
658  }
659 
665  public function checkFieldCreationInput(ilPropertyFormGUI $form): bool
666  {
667  return true;
668  }
669 
670  public function checkUniqueProp(ilPropertyFormGUI $form): bool
671  {
672  if ($this->getId() !== '' && $form->getInput('prop_' . ilDclBaseFieldModel::PROP_UNIQUE) === '1') {
673  $values = [];
674  foreach (ilDclCache::getTableCache($this->getTableId())->getRecords() as $record) {
675  $new = $record->getRecordFieldValue($this->getId());
676  foreach ($values as $value) {
677  if ($this->areEqual($new, $value)) {
678  $form->getItemByPostVar('prop_' . ilDclBaseFieldModel::PROP_UNIQUE)->setAlert($this->lng->txt('duplicate_entries_exist'));
679  return false;
680  }
681  }
682  $values[] = $new;
683  }
684  }
685  return true;
686  }
687 
688  public function checkUnique($value, ?int $record_id): bool
689  {
690  if ($value && $this->getProperty(ilDclBaseFieldModel::PROP_UNIQUE) === '1') {
691  foreach (ilDclCache::getTableCache($this->getTableId())->getRecords() as $record) {
692  if ($record->getId() !== $record_id) {
693  $x = $record->getRecordFieldValue($this->getId());
694  if ($this->areEqual($record->getRecordFieldValue($this->getId()), $value)) {
696  }
697  }
698  }
699  }
700 
701  return true;
702  }
703 
707  public function getStorageLocationOverride(): ?int
708  {
710  }
711 
715  public function setStorageLocationOverride(?int $storage_location_override): void
716  {
717  $this->storage_location_override = $storage_location_override;
718  }
719 
720  public function fillHeaderExcel(ilExcel $worksheet, int &$row, int &$col): void
721  {
722  $worksheet->setCell($row, $col, $this->getTitle());
723  $col++;
724  }
725 
726  public function checkTitlesForImport(array &$titles, array &$import_fields): void
727  {
728  foreach ($titles as $k => $title) {
729  if (!mb_detect_encoding($title, "UTF-8", true) == "UTF-8") {
730  $title = mb_convert_encoding($title, 'UTF-8', 'ISO-8859-1');
731  }
732  if ($title == $this->getTitle()) {
733  $import_fields[$k] = $this;
734  }
735  }
736  }
737 
741  public function storePropertiesFromForm(ilPropertyFormGUI $form): void
742  {
743  $field_props = $this->getValidFieldProperties();
744  $representation = ilDclFieldFactory::getFieldRepresentationInstance($this);
745 
746  foreach ($field_props as $property) {
747  $value = $form->getInput($representation->getPropertyInputFieldId($property));
748 
749  // save non empty values and set them to null, when they already exist. Do not override plugin-hook when already set.
750  if (!empty($value) || ($this->getPropertyInstance($property) != null && $property != self::PROP_PLUGIN_HOOK_NAME)) {
751  $this->setProperty($property, $value)?->store();
752  }
753  }
754  }
755 
759  public function fillPropertiesForm(ilPropertyFormGUI &$form): bool
760  {
761  $values = [
762  'table_id' => $this->getTableId(),
763  'field_id' => $this->getId(),
764  'title' => $this->getTitle(),
765  'datatype' => $this->getDatatypeId(),
766  'description' => $this->getDescription(),
767  ];
768 
769  $properties = $this->getValidFieldProperties();
770  foreach ($properties as $prop) {
771  $values['prop_' . $prop] = $this->getProperty($prop);
772  }
773 
774  $form->setValuesByArray($values);
775 
776  return true;
777  }
778 
783  public function isConfirmationRequired(ilPropertyFormGUI $form): bool
784  {
785  return false;
786  }
787 
792  {
793  $ilConfirmationGUI = new ilConfirmationGUI();
794  $ilConfirmationGUI->setFormAction($form->getFormAction());
795  $ilConfirmationGUI->addHiddenItem('confirmed', "1");
796  $ilConfirmationGUI->addHiddenItem('field_id', $form->getInput('field_id'));
797  $ilConfirmationGUI->addHiddenItem('title', $form->getInput('title'));
798  $ilConfirmationGUI->addHiddenItem('description', $form->getInput('description'));
799  $ilConfirmationGUI->addHiddenItem('datatype', $form->getInput('datatype'));
800  $ilConfirmationGUI->setConfirm($this->lng->txt('dcl_update_field'), 'update');
801  $ilConfirmationGUI->setCancel($this->lng->txt('cancel'), 'edit');
802 
803  return $ilConfirmationGUI;
804  }
805 }
hasProperty(string $key)
Checks if a certain property for a field is set.
checkTitlesForImport(array &$titles, array &$import_fields)
getConfirmationGUI(ilPropertyFormGUI $form)
called by ilDclFieldEditGUI if isConfirmationRequired returns true
getItemByPostVar(string $a_post_var)
checkFieldCreationInput(ilPropertyFormGUI $form)
Checks input of specific fields befor saving.
checkUniqueProp(ilPropertyFormGUI $form)
getRecordQueryFilterObject( $filter_value="", ?ilDclBaseFieldModel $sort_field=null)
Returns a query-object for building the record-loader-sql-query.
getValue()
Get the value that is displayed in the input client side.
Definition: Group.php:49
static getFieldRepresentationInstance(ilDclBaseFieldModel $field)
isConfirmationRequired(ilPropertyFormGUI $form)
called by ilDclFieldEditGUI when updating field properties if you overwrite this method, remember to also overwrite getConfirmationGUI
doDelete()
Remove field and properties.
storePropertiesFromForm(ilPropertyFormGUI $form)
called when saving the &#39;edit field&#39; form
buildFromDBRecord(array $rec)
Builds model from db record.
loadTableFieldSetting()
loadTableFieldSetting
static getFieldCache(int $field_id=0)
updateProperties()
Update properties of this field in Database.
getSortField()
Returns the sort-field id.
static _getDatatypeForId(string $id)
gives you the datatype id of a specified standard field.
checkValidityFromForm(ilPropertyFormGUI &$form, ?int $record_id)
fillPropertiesForm(ilPropertyFormGUI &$form)
called to fill the &#39;edit field&#39; form
static _getFieldIdByTitle(string $title, int $table_id)
getStorageLocation()
Get storage location for the model.
static where($where, $operator=null)
checkUnique($value, ?int $record_id)
getInput(string $a_post_var, bool $ensureValidation=true)
Returns the input of an item, if item provides getInput method and as fallback the value of the HTTP-...
addToTableViews()
create ilDclTableViewFieldSettings for this field in each tableview
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
loadExportability()
Load exportability.
static getDatatype(int $datatyp_id)
Get cached datatypes.
setCell(int $a_row, int $col, $value, ?string $datatype=null, bool $disable_strip_tags_for_strings=false)
Set cell value.
loadProperties()
Get all properties of a field.
setDatatypeId(int $a_id)
Set datatype id.
static getTableViewFieldSetting(string $id, int $tableview_id)
cloneProperties(ilDclBaseFieldModel $originalField)
const PROP_LENGTH
General properties.
global $DIC
Definition: shib_login.php:26
static getTableCache(?int $table_id=null)
getViewSetting(int $tableview_id)
setValuesByArray(array $a_values, bool $a_restrict_to_value_keys=false)
static setCloneOf(int $old, int $new, string $type)
static getInstance(int $table_id, string $field)
getDescription()
Get description.
loadDatatype()
Load datatype for model.
updateTableFieldSetting()
update exportable and fieldorder
int $storage_location_override
With this property the datatype-storage-location can be overwritten.
hasNumericSorting()
Set to true, when the sorting should be handled numerical.
fillHeaderExcel(ilExcel $worksheet, int &$row, int &$col)
getRecordQuerySortObject(string $direction="asc", bool $sort_by_status=false)
Returns a query-object for building the record-loader-sql-query.
setStorageLocationOverride(?int $storage_location_override)
setTitle(string $a_title)
Set title.
setDescription(string $a_desc)
Set description.
getValidFieldProperties()
Returns all valid properties for a field-type.
static getRecordFieldCache(object $record, object $field)
static getAllForTableId(int $table_id)
setTableId(int $a_id)
Set table id.
static _tableExists(int $table_id)
setProperty(string $key, $value)
static getFieldProperties(string $field_id)
Cache Field properties.
getDatatypeId()
Get datatype_id.
checkValidity($value, ?int $record_id)
Check if input is valid.
static _getTitleInvalidChars(bool $a_as_regex=true)
All valid chars for filed titles.