Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   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.
   8  //
   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.
  13  //
  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/>.
  16  
  17  /**
  18   * Defines a category in my profile page navigation.
  19   *
  20   * @package   core_user
  21   * @copyright 2015 onwards Ankit Agarwal
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_user\output\myprofile;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Defines a category in my profile page navigation.
  30   *
  31   * @since     Moodle 2.9
  32   * @package   core_user
  33   * @copyright 2015 onwards Ankit Agarwal
  34   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class category implements \renderable {
  37  
  38      /**
  39       * @var string Name of the category after which this category should appear.
  40       */
  41      private $after;
  42  
  43      /**
  44       * @var string Name of the category.
  45       */
  46      private $name;
  47  
  48      /**
  49       * @var string Title of the category.
  50       */
  51      private $title;
  52  
  53      /**
  54       * @var node[] Array of nodes associated with this category.
  55       */
  56      private $nodes = array();
  57  
  58      /**
  59       * @var string HTML class attribute for this category. Classes should be separated by a space, e.g. 'class1 class2'
  60       */
  61      private $classes;
  62  
  63      /**
  64       * @var array list of properties publicly accessible via __get.
  65       */
  66      private $properties = array('after', 'name', 'title', 'nodes', 'classes');
  67  
  68      /**
  69       * Constructor for category class.
  70       *
  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.
  75       */
  76      public function __construct($name, $title, $after = null, $classes = null) {
  77          $this->after = $after;
  78          $this->name = $name;
  79          $this->title = $title;
  80          $this->classes = $classes;
  81      }
  82  
  83      /**
  84       * Add a node to this category.
  85       *
  86       * @param node $node node object.
  87       * @see \core_user\output\myprofile\tree::add_node()
  88       *
  89       * @throws \coding_exception
  90       */
  91      public function add_node(node $node) {
  92          $name = $node->name;
  93          if (isset($this->nodes[$name])) {
  94              throw new \coding_exception("Node with name $name already exists");
  95          }
  96          if ($node->parentcat !== $this->name) {
  97              throw new \coding_exception("Node parent must match with the category it is added to");
  98          }
  99          $this->nodes[$node->name] = $node;
 100      }
 101  
 102      /**
 103       * Sort nodes of the category in the order in which they should be displayed.
 104       *
 105       * @see \core_user\output\myprofile\tree::sort_categories()
 106       * @throws \coding_exception
 107       */
 108      public function sort_nodes() {
 109          $tempnodes = array();
 110          $this->validate_after_order();
 111  
 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));
 119              }
 120          }
 121  
 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));
 129              }
 130          }
 131  
 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');
 135          }
 136          $this->nodes = $tempnodes;
 137      }
 138  
 139      /**
 140       * Verifies that node with content can come after node with content only . Also verifies the same thing for nodes without
 141       * content.
 142       * @throws \coding_exception
 143       */
 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');
 151                  } else {
 152                      // Valid node found.
 153                      $afternode = $nodearray[$after];
 154                      $beforecontent = $node->content;
 155                      $aftercontent = $afternode->content;
 156  
 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');
 160                      }
 161                  }
 162              }
 163          }
 164      }
 165  
 166      /**
 167       * Given a node object find all node objects that should appear after it.
 168       *
 169       * @param node $node node object
 170       *
 171       * @return array
 172       */
 173      protected function find_nodes_after($node) {
 174          $return = array();
 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));
 180              }
 181          }
 182          return $return;
 183      }
 184  
 185      /**
 186       * Magic get method.
 187       *
 188       * @param string $prop property to get.
 189       *
 190       * @return mixed
 191       * @throws \coding_exception
 192       */
 193      public function __get($prop) {
 194          if (in_array($prop, $this->properties)) {
 195              return $this->$prop;
 196          }
 197          throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
 198      }
 199  }