Skip to content
Snippets Groups Projects
Select Git revision
  • 541088347bf3c9ff69ec61466091e9997c6dcb85
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

StudipStudyArea.php

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    StudipStudyArea.php 15.02 KiB
    <?php
    /**
     * Studienbereich... TODO
     *
     * Copyright (C) 2008 - Marcus Lunzenauer <mlunzena@uos.de>
     *
     * 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.
     *
     * @package     studip
     *
     * @author    mlunzena
     * @author    André Noack <noack@data-quest.de>
     * @copyright (c) Authors
     *
     * @property string $id alias column for sem_tree_id
     * @property string $sem_tree_id database column
     * @property string $parent_id database column
     * @property int $priority database column
     * @property string $info database column
     * @property string $name database column
     * @property string|null $studip_object_id database column
     * @property int $type database column
     * @property int|null $mkdate database column
     * @property int|null $chdate database column
     * @property SimpleORMapCollection|StudipStudyArea[] $_children has_many StudipStudyArea
     * @property StudipStudyArea $_parent belongs_to StudipStudyArea
     * @property SimpleORMapCollection|Course[] $courses has_and_belongs_to_many Course
     */
    
    class StudipStudyArea extends SimpleORMap implements StudipTreeNode
    {
        use StudipTreeNodeCachableTrait;
        use StudipTreeNodeCourseTrait;
    
        /**
         * This constant represents the key of the root area.
         */
        const ROOT = 'root';
    
        protected static function configure($config = [])
        {
            $config['db_table'] = 'sem_tree';
            $config['has_many']['_children'] = [
                'class_name' => StudipStudyArea::class,
                'assoc_foreign_key' => 'parent_id',
                'assoc_func' => 'findByParent',
                'on_delete' => 'delete',
                'on_store' => 'store',
            ];
            $config['has_and_belongs_to_many']['courses'] = [
                'class_name' => Course::class,
                'thru_table' => 'seminar_sem_tree',
            ];
            $config['belongs_to']['_parent'] = [
                'class_name' => StudipStudyArea::class,
                'foreign_key' => 'parent_id',
            ];
    
            $config = self::registerCachableCallbacks($config);
    
            parent::configure($config);
        }
    
        /**
         * This is required, if the nodes are added backwards
         */
        public $required_children = [];
    
        /**
         * Returns the children of the study area with the specified ID.
         */
        public static function findByParent($parent_id)
        {
            return self::findByparent_id($parent_id, "ORDER BY priority,name");
        }
    
        /**
         * Returns the study area with the specified ID.
         */
        public static function find($id)
        {
    
            $result = NULL;
    
            if ($id === self::ROOT) {
                $result = self::getRootArea();
            }
    
            else {
                $result = parent::find($id);
            }
    
            return $result;
        }
    
        /**
         * Get a string representation of this study area.
         */
        public function __toString()
        {
            return $this->id;
        }
    
    
        /**
         * Get the comment of this study area.
         */
        public function getInfo()
        {
            return $this->content['info'];
        }
    
    
        /**
         * Set the comment of this study area.
         */
        public function setInfo($info)
        {
            $this->content['info'] = (string) $info;
            return $this;
        }
    
    
        /**
         * Get the display name of this study area.
         */
        public function getName(): string
        {
            return $this->content['name'];
        }
    
        /**
         * Set the display name of this study area.
         */
        public function setName($name)
        {
            $this->content['name'] = (string) $name;
            return $this;
        }
    
    
        /**
         * Get the parent ID of this study area.
         */
        public function getParentId()
        {
            return $this->content['parent_id'];
        }
    
    
        /**
         * Get the parent.
         */
        public function getParent()
        {
            $result = NULL;
            if ($this->getID() !== self::ROOT) {
                $result = $this->_parent;
            }
            return $result;
        }
    
    
        /**
         * Set the parent of this study area.
         */
        public function setParentId($parent_id)
        {
            $this->content['parent_id'] = (string) $parent_id;
            $this->resetRelation('parent');
            return $this;
        }
    
        /**
         * get the type of this study area.
         */
        public function getType()
        {
            return $this->content['type'];
        }
    
        /**
         * set the type of this study area.
         */
        public function setType($type)
        {
            $this->content['type'] = (int) $type;
            return $this;
        }
    
        /**
         * get the name of the type of this study area, see $SEM_TREE_TYPES in config.inc.php
         *
         * @return string
         */
        public function getTypeName()
        {
            if(isset($GLOBALS['SEM_TREE_TYPES'][$this->getType()]['name'])){
                return $GLOBALS['SEM_TREE_TYPES'][$this->getType()]['name'];
            } else {
                return '';
            }
        }
    
        /**
         * is this study area editable, see $SEM_TREE_TYPES in config.inc.php
         *
         * @return bool
         */
        public function isEditable()
        {
            if(isset($GLOBALS['SEM_TREE_TYPES'][$this->getType()]['editable'])){
                return (bool)$GLOBALS['SEM_TREE_TYPES'][$this->getType()]['editable'];
            } else {
                return false;
            }
        }
    
        /**
         * is this study area hidden, see $SEM_TREE_TYPES in config.inc.php
         *
         * @return bool
         */
        public function isHidden()
        {
            if (isset($GLOBALS['SEM_TREE_TYPES'][$this->getType()]['hidden'])) {
                return (bool) $GLOBALS['SEM_TREE_TYPES'][$this->getType()]['hidden'];
            } else {
                return false;
            }
        }
    
        /**
         * Get the path along the sem_tree to this study area.
         *
         * @param  string     optional; TODO
         *
         * @return mixed      TODO
         */
        public function getPath($separator = NULL)
        {
    
            $path = [];
    
            $area = $this;
            while ($area) {
                if ($area->getName() != '') {
                    $path[] = $area->getName();
                }
                if ($area->getParentId() == self::ROOT) {
                    break;
                }
                $area = $area->getParent();
            }
    
            $path = array_reverse($path);
    
            return isset($separator)
            ? join($separator, $path)
            : $path;
        }
    
    
        /**
         * Get the priority of this study area.
         */
        public function getPriority()
        {
            return $this->content['priority'];
        }
    
    
        /**
         * Set the priority of this study area.
         */
        public function setPriority($priority)
        {
            $this->content['priority'] = (int) $priority;
            return $this;
        }
    
    
        /**
         * Returns the children of this study area.
         */
        public function getChildren()
        {
            return $this->_children;
        }
    
        /**
         * Returns1 TRUE if the area has children.
         */
        public function hasChildren()
        {
            return sizeof($this->_children) > 0;
        }
    
    
        /**
         * Returns TRUE if this area is the root.
         */
        public function isRoot()
        {
            return $this->getId() === self::ROOT;
        }
    
    
        /**
         * Returns TRUE if this area can be select.
         */
        public function isAssignable()
        {
            $cfg = Config::GetInstance();
            $leaves_too = $cfg->getValue('SEM_TREE_ALLOW_BRANCH_ASSIGN');
            if ($leaves_too) {
                return !$this->isRoot() && !$this->isHidden();
            } else {
                return !$this->isRoot() && !$this->isHidden() && !$this->hasChildren();
            }
        }
    
        /**
         * is this study area considered a study modul?, see $SEM_TREE_TYPES in config.inc.php
         *
         * @return bool
         */
        public function isModule()
        {
            return isset($GLOBALS['SEM_TREE_TYPES'][$this->getType()]['is_module']);
        }
    
        /**
         * Get an associative array of all study areas of a course. The array
         * contains StudipStudyArea instances
         *
         * @param  string $id the course's ID
         *
         * @return SimpleCollection      a SimpleORMapCollection of that course's study areas
         */
        public static function getStudyAreasForCourse($id)
        {
            $course = Course::find($id);
            return $course ? $course->study_areas : new SimpleCollection();
        }
    
    
        /**
         * Returns the not really existing root study area.
         *
         * @return object     the root study area object
         */
        public static function getRootArea()
        {
            $root = new StudipStudyArea();
            $root->setID(self::ROOT);
            $root->setName(Config::get()->UNI_NAME_CLEAN);
            return $root;
        }
    
    
        /**
         * Search for study areas whose name matches the given search term.
         *
         * @param string $searchTerm the seach term
         *
         * @return StudipStudyArea[] nodes
         */
        public static function search($searchTerm)
        {
            return self::findBySql(
                "name LIKE :searchTerm ORDER BY priority",
                ['searchTerm' => "%$searchTerm%"]
            );
        }
    
        /**
         * Takes an array of StudyArea objects and produces the tree to the root node
         *
         * @param array $nodes All required nodes in the tree
         * @return StudipStudyArea the root node
         */
        public static function backwards($nodes)
        {
            // create the dummy root
            $root = static::getRootArea();
    
            $hashmap = [];
    
            $i = 0;
    
            // let the backwardssearch begin
            while ($nodes && $i < 99) {
    
                //clear cache
                $newNodes = [];
    
                //process nodes on this level
                foreach ($nodes as $node) {
    
                    // if we know the node already place there
                    if (isset($hashmap[$node->parent_id])) {
                        $cached = $hashmap[$node->parent_id];
                        $cached->required_children[$node->id] = $node;
                    } else {
                        // if we have a node that is directly under root
                        if ($node->parent_id == $root->id) {
                            $root->required_children[$node->id] = $node;
                        } else {
                            // else store in hashmap and continue
                            $hashmap[$node->parent_id] = $node->_parent;
                            $node->_parent->required_children[$node->id] = $node;
                            $newNodes[$node->id] = $node->_parent;
                        }
                    }
                }
                $nodes = $newNodes;
                $i++;
            }
    
            // plant the tree
            return $root;
        }
    
        public static function getNode($id): StudipTreeNode
        {
            if ($id === 'root') {
                return static::build([
                    'id'   => 'root',
                    'name' => Config::get()->UNI_NAME_CLEAN,
                ]);
            }
    
            return static::find($id);
        }
    
        public static function getCourseNodes(string $course_id): array
        {
            return Course::find($course_id)->study_areas->getArrayCopy();
        }
    
        public function getDescription(): string
        {
            return $this->getInfo();
        }
    
        /**
         * @see StudipTreeNode::getImage()
         */
        public function getImage()
        {
            return null;
        }
    
        public function hasChildNodes(): bool
        {
            return count($this->_children) > 0;
        }
    
        /**
         * @see StudipTreeNode::getChildNodes()
         */
        public function getChildNodes(bool $onlyVisible = false): array
        {
            if ($onlyVisible) {
                $visibleTypes = array_filter($GLOBALS['SEM_TREE_TYPES'], function ($t): bool {
                    return empty($t['hidden']);
                });
    
                return static::findBySQL(
                    "`parent_id` = :parent AND `type` IN (:types)",
                    ['parent' => $this->id, 'types' => $visibleTypes]
                );
            } else {
                return static::findByParent_id($this->id);
            }
        }
    
        /**
         * Retrieves all child nodes of this study area as a flat list.
         *
         * @param bool $only_visible Whether to include only visible nodes (true)
         *     or all nodes (false). Defaults to false.
         *
         * @return StudipStudyArea[] A list of all child nodes of this node.
         */
        public function getAllChildNodes(bool $only_visible = false) : array
        {
            $result = [];
            $children = $this->getChildNodes($only_visible);
            foreach ($children as $child) {
                $result[] = $child;
                $result = array_merge($result, $child->getAllChildNodes($only_visible));
            }
            return $result;
        }
    
        /**
         * @see StudipTreeNode::countCourses()
         */
        public function countCourses(
            $semester_id = 'all',
            $semclass = 0,
            $with_children = false
        ): int {
            if ($this->id === 'root' && !$with_children) {
                return 0;
            }
    
            [$condition, $parameters] = $this->getCoursesCondition(
                't',
                $semester_id,
                $semclass
            );
    
            $query = "SELECT COUNT(DISTINCT t.`seminar_id`) FROM `seminar_sem_tree` t {$condition}";
    
            if ($with_children) {
                $query .= " AND t.`sem_tree_id` IN (:ids)";
                $parameters['ids'] = array_merge([$this->id], $this->getDescendantIds());
            } else {
                $query .= " AND t.`sem_tree_id` = :id";
                $parameters['id'] = $this->id;
            }
    
            return DBManager::get()->fetchColumn($query, $parameters);
        }
    
        public function getCourses(
            $semester_id = 'all',
            $semclass = 0,
            $searchterm = '',
            $with_children = false,
            array $courses = []
        ): array
        {
            [$condition, $parameters, $order_by] = $this->getCoursesCondition(
                't',
                $semester_id,
                $semclass,
                $searchterm,
                $courses
            );
    
            $query = "SELECT DISTINCT s.* FROM `seminar_sem_tree` AS t {$condition}";
    
            if ($with_children) {
                $query .= " AND t.`sem_tree_id` IN (:ids)";
                $parameters['ids'] = array_merge([$this->id], $this->getDescendantIds());
            } else {
                $query .= " AND t.`sem_tree_id` = :id";
                $parameters['id'] = $this->id;
            }
    
            $query .= " ORDER BY " . implode(', ', $order_by);
    
            return DBManager::get()->fetchAll($query, $parameters, 'Course::buildExisting');
        }
    
        public function getAncestors(): array
        {
            $path = [
                [
                    'id' => $this->id,
                    'name' => $this->getName(),
                    'classname' => static::class
                ]
            ];
    
            if ($this->parent_id) {
                $path = array_merge($this->getNode($this->parent_id)->getAncestors(), $path);
            }
    
            return $path;
        }
    
        /**
         * Constructs an index from the level hierarchy, This index is a number,
         * containing the "depth" level and the priority on this level. For example,
         * a node on level 2 with priority 3 will get an index of 23.
         *
         * @return int
         */
        public function getIndex()
        {
            $level = 1;
            $index = (string) $level . (string) $this->priority;
            $current = $this;
    
            while ($current->getParent()) {
                $current = $current->getParent();
                $index .= $level . $current->priority;
                $level++;
            }
    
            return $index;
        }
    
    }