From 0807601d2fd3e20474cd5eada633ba889056cd62 Mon Sep 17 00:00:00 2001 From: Ankit Agarwal Date: Wed, 25 Feb 2015 14:35:34 +0530 Subject: [PATCH] MDL-48932 myprofile: Add apis to make my profile pluggable --- user/classes/output/myprofile/category.php | 150 +++++++++++++++++++ user/classes/output/myprofile/manager.php | 87 ++++++++++++ user/classes/output/myprofile/node.php | 112 +++++++++++++++ user/classes/output/myprofile/tree.php | 158 +++++++++++++++++++++ 4 files changed, 507 insertions(+) create mode 100644 user/classes/output/myprofile/category.php create mode 100644 user/classes/output/myprofile/manager.php create mode 100644 user/classes/output/myprofile/node.php create mode 100644 user/classes/output/myprofile/tree.php diff --git a/user/classes/output/myprofile/category.php b/user/classes/output/myprofile/category.php new file mode 100644 index 00000000000..5c283026fd9 --- /dev/null +++ b/user/classes/output/myprofile/category.php @@ -0,0 +1,150 @@ +. + +/** + * Defines a category in my profile page navigation. + * + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_user\output\myprofile; +defined('MOODLE_INTERNAL') || die(); + +/** + * Defines a category in my profile page navigation. + * + * @since Moodle 2.9 + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class category implements \renderable { + + /** + * @var string Name of the category after which this category should appear. + */ + private $after; + + /** + * @var string Name of the category. + */ + private $name; + + /** + * @var string Title of the category. + */ + private $title; + + /** + * @var node[] Array of nodes associated with this category. + */ + private $nodes = array(); + + /** + * @var array list of properties publicly accessible via __get. + */ + private $properties = array('after', 'name', 'title', 'nodes'); + + /** + * Constructor for category class. + * + * @param string $name Category name. + * @param string $title category title. + * @param null|string $after Name of category after which this category should appear. + */ + public function __construct($name, $title, $after = null) { + $this->after = $after; + $this->name = $name; + $this->title = $title; + } + + /** + * Add a node to this category. + * + * @param node $node node object. + * @see \core_user\output\myprofile\tree::add_node() + * + * @throws \coding_exception + */ + public function add_node(\core_user\output\myprofile\node $node) { + $name = $node->name; + if (isset($this->nodes[$name])) { + throw new \coding_exception("Node with name $name already exists"); + } + if ($node->parentcat !== $this->name) { + throw new \coding_exception("Node parent must match with the category it is added to"); + } + $this->nodes[$node->name] = $node; + } + + /** + * Sort nodes of the category in the order in which they should be displayed. + * + * @see \core_user\output\myprofile\tree::sort_categories() + * @throws \coding_exception + */ + public function sort_nodes() { + $tempnodes = array(); + foreach ($this->nodes as $node) { + $after = $node->after; + if ($after == null) { + // Can go anywhere in the cat. + $tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node)); + } + } + if (count($tempnodes) !== count($this->nodes)) { + // Orphan nodes found. + throw new \coding_exception('Some of the nodes specified contains invalid \'after\' property'); + } + $this->nodes = $tempnodes; + } + + /** + * Given a node object find all node objects that should appear after it. + * + * @param node $node node object + * + * @return array + */ + protected function find_nodes_after($node) { + $return = array(); + $nodearray = $this->nodes; + foreach ($nodearray as $nodeelement) { + if ($nodeelement->after === $node->name) { + // Find all nodes that comes after this node as well. + $return = array_merge($return, array($nodeelement), $this->find_nodes_after($nodeelement)); + } + } + return $return; + } + + /** + * Magic get method. + * + * @param string $prop property to get. + * + * @return mixed + * @throws \coding_exception + */ + public function __get($prop) { + if (in_array($prop, $this->properties)) { + return $this->$prop; + } + throw new \coding_exception('Property "' . $prop . '" doesn\'t exist'); + } +} diff --git a/user/classes/output/myprofile/manager.php b/user/classes/output/myprofile/manager.php new file mode 100644 index 00000000000..17ae6ced494 --- /dev/null +++ b/user/classes/output/myprofile/manager.php @@ -0,0 +1,87 @@ +. + +/** + * Defines Manager class for my profile navigation tree. + * + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_user\output\myprofile; +defined('MOODLE_INTERNAL') || die(); + +/** + * Defines MAnager class for myprofile navigation tree. + * + * @since Moodle 2.9 + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manager { + /** + * Parse all callbacks and builds the tree. + * + * @param integer $user ID of the user for which the profile is displayed. + * @param bool $iscurrentuser true if the profile being viewed is of current user, else false. + * @param \stdClass $course Course object + * + * @return tree Fully build tree to be rendered on my profile page. + */ + public static function build_tree($user, $iscurrentuser, $course = null) { + global $CFG; + $tree = new tree(); + + // Add core nodes. + $file = $CFG->libdir . "/myprofilelib.php"; + if (is_readable($file)) { + require_once($file); + $function = "core_myprofile_navigation"; + if (function_exists($function)) { + $function($tree, $user, $iscurrentuser, $course); + } + } + + // Core components. + $components = \core_component::get_core_subsystems(); + foreach ($components as $component => $directory) { + if (empty($directory)) { + continue; + } + $file = $directory . "/lib.php"; + if (is_readable($file)) { + require_once($file); + $function = "core_" . $component . "_myprofile_navigation"; + if (function_exists($function)) { + $function($tree, $user, $iscurrentuser, $course); + } + } + } + + // Plugins. + $types = \core_component::get_plugin_types(); + foreach ($types as $type => $dir) { + $pluginlist = get_plugin_list_with_function($type, "myprofile_navigation", "lib.php"); + foreach ($pluginlist as $function) { + $function($tree, $user, $iscurrentuser, $course); + } + } + $tree->sort_categories(); + return $tree; + } +} diff --git a/user/classes/output/myprofile/node.php b/user/classes/output/myprofile/node.php new file mode 100644 index 00000000000..254d74399c6 --- /dev/null +++ b/user/classes/output/myprofile/node.php @@ -0,0 +1,112 @@ +. + +/** + * Defines a node in my profile page navigation. + * + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_user\output\myprofile; +defined('MOODLE_INTERNAL') || die(); + +/** + * Defines a node in my profile page navigation. + * + * @since Moodle 2.9 + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class node implements \renderable { + /** + * @var string Name of parent category. + */ + private $parentcat; + + /** + * @var string Name of this node. + */ + private $name; + + /** + * @var string Name of the node after which this node should appear. + */ + private $after; + + /** + * @var string Title of this node. + */ + private $title; + + /** + * @var string|\moodle_url Url that this node should link to. + */ + private $url; + + /** + * @var string Content to display under this node. + */ + private $content; + + /** + * @var string|\pix_icon Icon for this node. + */ + private $icon; + + /** + * @var array list of properties accessible via __get. + */ + private $properties = array('parentcat', 'after', 'name', 'title', 'url', 'content', 'icon'); + + /** + * Constructor for the node. + * + * @param string $parentcat Name of parent category. + * @param string $name Name of this node. + * @param string $title Title of this node. + * @param null|string $after Name of the node after which this node should appear. + * @param null|string|\moodle_url $url Url that this node should link to. + * @param null|string $content Content to display under this node. + * @param null|string|\pix_icon $icon Icon for this node. + */ + public function __construct($parentcat, $name, $title, $after = null, $url = null, $content = null, $icon = null) { + $this->parentcat = $parentcat; + $this->after = $after; + $this->name = $name; + $this->title = $title; + $this->url = new \moodle_url($url); + $this->content = $content; + $this->icon = $icon; + } + + /** + * Magic get method. + * + * @param string $prop property to get. + * + * @return mixed + * @throws \coding_exception + */ + public function __get($prop) { + if (in_array($prop, $this->properties)) { + return $this->$prop; + } + throw new \coding_exception('Property "' . $prop . '" doesn\'t exist'); + } +} diff --git a/user/classes/output/myprofile/tree.php b/user/classes/output/myprofile/tree.php new file mode 100644 index 00000000000..cb3d2ba1386 --- /dev/null +++ b/user/classes/output/myprofile/tree.php @@ -0,0 +1,158 @@ +. + +/** + * Defines profile page navigation tree. + * + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_user\output\myprofile; +defined('MOODLE_INTERNAL') || die(); + +/** + * Defines my profile page navigation tree. + * + * @since Moodle 2.9 + * @package core_user + * @copyright 2015 onwards Ankit Agarwal + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tree implements \renderable { + /** + * @var category[] Array of categories in the tree. + */ + private $categories = array(); + + /** + * @var node[] Array of nodes in the tree that were directly added to the tree. + */ + private $nodes = array(); + + /** + * @var array List of properties accessible via __get. + */ + private $properties = array('categories', 'nodes'); + + /** + * Add a node to the tree. + * + * @param node $node node object. + * + * @throws \coding_exception + */ + public function add_node(node $node) { + $name = $node->name; + if (isset($this->nodes[$name])) { + throw new \coding_exception("Node name $name already used"); + } + $this->nodes[$node->name] = $node; + } + + /** + * Add a category to the tree. + * + * @param category $cat category object. + * + * @throws \coding_exception + */ + public function add_category(category $cat) { + $name = $cat->name; + if (isset($this->categories[$name])) { + throw new \coding_exception("Category name $name already used"); + } + $this->categories[$cat->name] = $cat; + } + + /** + * Sort categories and nodes. Builds the tree structure that would be displayed to the user. + * + * @throws \coding_exception + */ + public function sort_categories() { + $this->attach_nodes_to_categories(); + $tempcategories = array(); + foreach ($this->categories as $category) { + $after = $category->after; + if ($after == null) { + // Can go anywhere in the tree. + $category->sort_nodes(); + $tempcategories = array_merge($tempcategories, array($category->name => $category), + $this->find_categories_after($category)); + } + } + if (count($tempcategories) !== count($this->categories)) { + // Orphan categories found. + throw new \coding_exception('Some of the categories specified contains invalid \'after\' property'); + } + $this->categories = $tempcategories; + } + + /** + * Attach various nodes to their respective categories. + * + * @throws \coding_exception + */ + protected function attach_nodes_to_categories() { + foreach ($this->nodes as $node) { + $parentcat = $node->parentcat; + if (!isset($this->categories[$parentcat])) { + throw new \coding_exception("Category $parentcat doesn't exist"); + } else { + $this->categories[$parentcat]->add_node($node); + } + } + } + + /** + * Find all category nodes that should be displayed after a given a category node. + * + * @param category $category category object + * + * @return category[] array of category objects + * @throws \coding_exception + */ + protected function find_categories_after($category) { + $return = array(); + $categoryarray = $this->categories; + foreach ($categoryarray as $categoryelement) { + if ($categoryelement->after == $category->name) { + // Find all categories that comes after this category as well. + $categoryelement->sort_nodes(); + $return = array_merge($return, array($categoryelement->name => $categoryelement), + $this->find_categories_after($categoryelement)); + } + } + return $return; + } + + /** + * Magic get method. + * + * @param string $prop property to get. + * + * @return mixed + * @throws \coding_exception + */ + public function __get($prop) { + if (in_array($prop, $this->properties)) { + return $this->$prop; + } + throw new \coding_exception('Property "' . $prop . '" doesn\'t exist'); + } +}