2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Defines a category in my profile page navigation.
21 * @copyright 2015 onwards Ankit Agarwal
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace core_user\output\myprofile;
26 defined('MOODLE_INTERNAL') || die();
29 * Defines a category in my profile page navigation.
33 * @copyright 2015 onwards Ankit Agarwal
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class category implements \renderable {
39 * @var string Name of the category after which this category should appear.
44 * @var string Name of the category.
49 * @var string Title of the category.
54 * @var node[] Array of nodes associated with this category.
56 private $nodes = array();
59 * @var string HTML class attribute for this category. Classes should be separated by a space, e.g. 'class1 class2'
64 * @var array list of properties publicly accessible via __get.
66 private $properties = array('after', 'name', 'title', 'nodes', 'classes');
69 * Constructor for category class.
71 * @param string $name Category name.
72 * @param string $title category title.
73 * @param null|string $after Name of category after which this category should appear.
74 * @param null|string $classes a list of css classes.
76 public function __construct($name, $title, $after = null, $classes = null) {
77 $this->after = $after;
79 $this->title = $title;
80 $this->classes = $classes;
84 * Add a node to this category.
86 * @param node $node node object.
87 * @see \core_user\output\myprofile\tree::add_node()
89 * @throws \coding_exception
91 public function add_node(node $node) {
93 if (isset($this->nodes[$name])) {
94 throw new \coding_exception("Node with name $name already exists");
96 if ($node->parentcat !== $this->name) {
97 throw new \coding_exception("Node parent must match with the category it is added to");
99 $this->nodes[$node->name] = $node;
103 * Sort nodes of the category in the order in which they should be displayed.
105 * @see \core_user\output\myprofile\tree::sort_categories()
106 * @throws \coding_exception
108 public function sort_nodes() {
109 $tempnodes = array();
110 $this->validate_after_order();
112 // First content noes.
113 foreach ($this->nodes as $node) {
114 $after = $node->after;
115 $content = $node->content;
116 if (($after == null && !empty($content)) || $node->name === 'editprofile') {
117 // Can go anywhere in the cat. Also show content nodes first.
118 $tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
122 // Now nodes with no content.
123 foreach ($this->nodes as $node) {
124 $after = $node->after;
125 $content = $node->content;
126 if ($after == null && empty($content)) {
127 // Can go anywhere in the cat. Also show content nodes first.
128 $tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
132 if (count($tempnodes) !== count($this->nodes)) {
133 // Orphan nodes found.
134 throw new \coding_exception('Some of the nodes specified contains invalid \'after\' property');
136 $this->nodes = $tempnodes;
140 * Verifies that node with content can come after node with content only . Also verifies the same thing for nodes without
142 * @throws \coding_exception
144 protected function validate_after_order() {
145 $nodearray = $this->nodes;
146 foreach ($this->nodes as $node) {
147 $after = $node->after;
148 if (!empty($after)) {
149 if (empty($nodearray[$after])) {
150 throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
153 $afternode = $nodearray[$after];
154 $beforecontent = $node->content;
155 $aftercontent = $afternode->content;
157 if ((empty($beforecontent) && !empty($aftercontent)) || (!empty($beforecontent) && empty($aftercontent))) {
158 // Only node with content are allowed after content nodes. Same goes for no content nodes.
159 throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
167 * Given a node object find all node objects that should appear after it.
169 * @param node $node node object
173 protected function find_nodes_after($node) {
175 $nodearray = $this->nodes;
176 foreach ($nodearray as $nodeelement) {
177 if ($nodeelement->after === $node->name) {
178 // Find all nodes that comes after this node as well.
179 $return = array_merge($return, array($nodeelement->name => $nodeelement), $this->find_nodes_after($nodeelement));
188 * @param string $prop property to get.
191 * @throws \coding_exception
193 public function __get($prop) {
194 if (in_array($prop, $this->properties)) {
197 throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');