diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /lib/models/resources/ResourcePropertyDefinition.php | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/models/resources/ResourcePropertyDefinition.php')
| -rw-r--r-- | lib/models/resources/ResourcePropertyDefinition.php | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/lib/models/resources/ResourcePropertyDefinition.php b/lib/models/resources/ResourcePropertyDefinition.php new file mode 100644 index 0000000..b87e8e6 --- /dev/null +++ b/lib/models/resources/ResourcePropertyDefinition.php @@ -0,0 +1,409 @@ +<?php + +/** + * ResourcePropertyDefinition.php - model class for resource property definitions + * + * The ResourcePropertyDefinition class can be used as a Factory + * for ResourceProperty objects. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * @author Moritz Strohm <strohm@data-quest.de> + * @copyright 2017-2018 + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @category Stud.IP + * @package resources + * @since 4.1 + * + * @property string $id alias column for property_id + * @property string $property_id database column + * @property string $name database column + * @property I18NString|null $description database column + * @property string $type database column + * @property string $options database column + * @property int $system database column + * @property int $info_label database column + * @property I18NString $display_name database column + * @property int $searchable database column + * @property int $range_search database column + * @property string $write_permission_level database column + * @property int|null $property_group_id database column + * @property int|null $property_group_pos database column + * @property int $mkdate database column + * @property int $chdate database column + * @property ResourcePropertyGroup|null $group belongs_to ResourcePropertyGroup + */ + + +class ResourcePropertyDefinition extends SimpleORMap +{ + /** + * This regular expression is used to ensure that position properties + * are always in a format as specified by the ISO-6709 + * string representation. + */ + const CRSWGS84_REGEX = '/[+-]\d{1,3}\.\d{1,10}[+-]\d{1,3}\.\d{1,10}[+-]\d{1,5}\.\d{1,10}CRSWGS_84\/$/'; + + protected static function configure($config = []) + { + $config['db_table'] = 'resource_property_definitions'; + + $config['belongs_to']['group'] = [ + 'class_name' => ResourcePropertyGroup::class, + 'foreign_key' => 'property_group_id' + ]; + + $config['has_many']['properties'] = [ + 'class_name' => ResourceProperty::class, + 'assoc_foreign_key' => 'property_id', + 'on_delete' => 'delete', + ]; + + $config['i18n_fields']['display_name'] = true; + $config['i18n_fields']['description'] = true; + + parent::configure($config); + } + + public static function findByPropertyGroup($group_id) + { + return self::findBySql( + 'property_group_id = :group_id + ORDER BY property_group_pos ASC, name ASC', + [ + 'group_id' => $group_id + ] + ); + } + + /** + * Returns a list of all defined data types. + * + * @return string[] An array containing the names of all defined + * resource property types. + */ + public static function getDefinedTypes() + { + return [ + 'bool', + 'text', + 'num', + 'select', + 'user', + 'institute', + 'position', + 'fileref', + 'url' + ]; + } + + /** + * Returns all available options for this property + * as an array. + */ + public function getOptionsArray() + { + if ($this->options) { + $options = explode(';', $this->options); + return array_combine($options, $options); + } + return []; + } + + + public function setOptionsFromArray($array = []) + { + if (is_array($array)) { + $this->options = implode(';', $array); + } else { + $this->options = ''; + } + } + + /** + * Generates appropriate HTML input elements for this property. + * + * @param string $value The value of the HTML input element. + * + * @param string $special_name A special name for the HTML input(s). + * + * @param bool $with_label Whether a label shall be placed around the + * HTML input element(s) or not. + * + * @param bool $allow_boolean_false Wheter boolean attributes shall + * also include a hidden input field that sets the value to zero + * if the checkbox for the boolean attribute is not checked. + * Defaults to true. + * + * @return string A string containing HTML code. + */ + public function toHtmlInput( + $value = '', + $special_name = '', + $with_label = false, + $allow_boolean_false = true, + $disabled = false + ) + { + $label_html_classes = ''; + $type = $this->type; + $input_name = $special_name + ? $special_name + : 'properties[' . $this->id . ']'; + + if ($type === 'bool') { + $label_html_classes = 'col-3'; + //Booleans can have one or two input elements, + //whether a false state shall be selectable or not. + if ($allow_boolean_false) { + $input_html = sprintf( + '<input type="hidden" name="%1$s" value="0" %2$s %3$s>' + . '<input type="checkbox" name="%1$s" value="1" %2$s>', + htmlReady($input_name), + $value ? 'checked' : '', + $disabled ? 'disabled' : '' + ); + } else { + $input_html = sprintf( + '<input type="checkbox" name="%1$s" value="1" %2$s %3$s>', + htmlReady($input_name), + $value ? 'checked' : '', + $disabled ? 'disabled' : '' + ); + } + if ($with_label) { + return sprintf( + '<label %1$s>%2$s %3$s</label>', + ( + $label_html_classes + ? 'class="' . htmlReady($label_html_classes) . '"' + : '' + ), + $input_html, + htmlReady($this->__toString()) + ); + } else { + return $input_html; + } + } elseif ($type === 'select') { + $options_html = sprintf( + '<option value="" %2$s>%1$s</option>', + _('Bitte wählen'), + !$value ? 'selected="selected"' : '' + ); + foreach ($this->getOptionsArray() as $option) { + $options_html .= sprintf( + '<option value="%1$s" %2$s>%1$s</option>', + htmlReady($option), + $value == $option ? 'selected="selected"' : '' + ); + } + if ($with_label) { + return sprintf( + '<label %1$s>%4$s<select name="%2$s">%3$s</select></label>', + ( + $label_html_classes + ? 'class="' . htmlReady($label_html_classes) . '"' + : '' + ), + htmlReady($input_name), + $options_html, + htmlReady($this->__toString()) + ); + } else { + return sprintf( + '<select name="%1$s">%2$s</select>', + htmlReady($input_name), + $options_html + ); + } + } elseif ($type === 'position') { + $factory = new Flexi\Factory($GLOBALS['STUDIP_BASE_PATH']); + $template = $factory->open('templates/resources/position_attribute_form_part.php'); + $template->set_attribute( + 'input_name', + $input_name + ); + $template->set_attribute( + 'latitude', + $value[0] + ); + $template->set_attribute( + 'longitude', + $value[1] + ); + $template->set_attribute( + 'altitude', + $value[2] + ); + + return $template->render(); + } elseif ($type === 'user') { + $search = new QuickSearch($input_name, new StandardSearch('user_id')); + $search->defaultValue($value, ($value ? get_fullname($value, 'full_rev_username') : '')); + return sprintf( + '<label %1$s>%2$s<div class="assigned-user-search-wrapper flex-row">%3$s%4$s</div></label>', + ( + $label_html_classes + ? 'class="' . htmlReady($label_html_classes) . '"' + : '' + ), + $this->__toString(), + $search->render(), + Icon::create('refresh')->asImg( + [ + 'class' => 'delete-assigned-user-icon enter-accessible', + 'data-input-name' => $input_name, + 'title' => _('Zuorndung entfernen'), + 'tabindex' => '0', + 'aria-role' => 'button' + ] + ) + ); + } else { + $input_type = 'text'; + $min = ''; + if ($type === 'num') { + $input_type = 'number'; + $min = 'min="0"'; + } + if ($with_label) { + return sprintf( + '<label %1$s>%5$s<input type="%2$s" name="%3$s" value="%4$s" %6$s %7$s></label>', + ( + $label_html_classes + ? 'class="' . htmlReady($label_html_classes) . '"' + : '' + ), + $input_type, + htmlReady($input_name), + $value, + htmlReady($this->__toString()), + $disabled ? 'disabled' : '', + $min + ); + } else { + return sprintf( + '<input type="%1$s" name="%2$s" value="%3$s" %4$s %5$s>', + $input_type, + htmlReady($input_name), + $value, + $disabled ? 'disabled' : '', + $min + ); + } + } + } + + /** + * Verifies that a property value (state) is valid for the given + * resource property definition. + * + * @param string $state A state for this property which shall be checked. + * + * @throws ResourcePropertyStateException If the state has an invalid value + * a ResourcePropertyStateException is thrown. + * + * @return bool True, if the state value is valid. + */ + public function validateState($state = '') + { + $invalid_state = false; + + //The type 'text' does not need to be validated since it can have + //all sorts of data in it. + if ($this->type == 'bool') { + if (!in_array($state, ['0', '1'])) { + //invalid boolean state: neither true nor false + $invalid_state = true; + } + } elseif ($this->type == 'num') { + if (!preg_match('/[0-9.]+/', $state)) { + //not a number + $invalid_state = true; + } + } elseif ($this->type == 'user') { + if (!User::exists($state)) { + //User does not exist + throw new ResourcePropertyStateException( + sprintf( + _('Die Eigenschaft %1$s besitzt einen ungültigen Wert! Der/die Nutzer/-in mit der ID %2$s existiert nicht!'), + $this->name, + $state + ) + ); + } + } elseif ($this->type == 'institute') { + if (!Institute::exists($state)) { + //Institute does not exist + throw new ResourcePropertyStateException( + sprintf( + _('Die Eigenschaft %1$s besitzt einen ungültigen Wert! Die Einrichtung mit der ID %2$s existiert nicht!'), + $this->name, + $state + ) + ); + } + } elseif ($this->type == 'position') { + if (!preg_match(self::CRSWGS84_REGEX, $state)) { + //$state does not contain ISO-6709 coordinates + //in the CRSWGS84 format! + throw new ResourcePropertyStateException( + sprintf( + _('Die Positionsangabe für die Eigenschaft %1$s ist ungültig!'), + $state + ) + ); + } + } + + //A general exception message: + if ($invalid_state) { + throw new ResourcePropertyException( + sprintf( + _('Der Wert %1$s ist für die Eigenschaft %2$s (Typ %3$s) nicht zulässig!'), + $state, + $this->name, + $this->type + ) + ); + } + + return true; + } + + /** + * Creates a ResourceProperty object that is automatically linked + * to the property definition. The ResourceProperty object is only + * created but not stored in the database. + * + * @param Resource $resource The resource object which shall be extended + * by a property. + * @param string $state The value of the property that shall be created. + * + * @throws ResourcePropertyException If $state is invalid for this property. + * + * @return ResourceProperty A ResourceProperty object + * which can be modified. + */ + public function createResourceProperty(Resource $resource, $state = '') + { + if ($this->validateState($state)) { + $property = new ResourceProperty(); + $property->property_id = $this->id; + $property->resource_id = $resource->id; + $property->state = $state; + return $property; + } + + throw new Exception('Could not validate state'); + } + + public function __toString() + { + return trim($this->display_name) ?: $this->name; + } +} |
