1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3require_once("./Services/UIComponent/Explorer2/classes/class.ilTreeExplorerGUI.php");
4require_once("./Services/UIComponent/AdvancedSelectionList/classes/class.ilAdvancedSelectionListGUI.php");
5require_once("./Services/UIComponent/Glyph/classes/class.ilGlyphGUI.php");
6require_once("./Services/UIComponent/Button/classes/class.ilLinkButton.php");
7require_once("./Modules/StudyProgramme/classes/class.ilObjStudyProgrammeSettingsGUI.php");
8/**
9 * Class ilStudyProgrammeTreeGUI
10 * ilObjStudyProgrammeTreeExplorerGUI generates the tree output for StudyProgrammes
11 * This class builds the tree with drag & drop functionality and some additional buttons which triggers bootstrap-modals
12 *
13 * @author  Michael Herren <mh@studer-raimann.ch>
14 * @version 1.0.0
15 */
16class ilObjStudyProgrammeTreeExplorerGUI extends ilExplorerBaseGUI
17{
18    protected $js_study_programme_path = "./Modules/StudyProgramme/templates/js/ilStudyProgramme.js";
19    protected $css_study_programme_path = "./Modules/StudyProgramme/templates/css/ilStudyProgrammeTree.css";
20
21    /**
22     * @var array
23     */
24    //protected $stay_with_command = array( "", "render", "view", "infoScreen", "performPaste", "cut", "tree_view");
25
26    /**
27     * @var int
28     */
29    protected $tree_root_id;
30
31    /**
32     * @var ilAccessHandler
33     */
34    protected $access;
35    /**
36     * @var ilLanguage
37     */
38    protected $lng;
39
40    /**
41     * @var ilTemplate
42     */
43    protected $tpl;
44
45    /**
46     * @var ilToolbarGUI
47     */
48    protected $toolbar;
49
50    /**
51     * @var ilCtrl
52     */
53    protected $ctrl;
54
55    /**
56     * @var string css-id of the bootstrap modal dialog
57     */
58    protected $modal_id;
59
60    /**
61     * @var array js configuration for the tree
62     */
63    protected $js_conf;
64
65    /**
66     * default classes of the tree [key=>class_name]
67     * @var array
68     */
69    protected $class_configuration = array(
70        'node' => array(
71            'node_title' => 'title',
72            'node_point' => 'points',
73            'node_current' => 'ilHighlighted current_node',
74            'node_buttons' => 'tree_button'
75        ),
76        'lp_object' => 'lp-object',
77    );
78
79    protected $node_template;
80
81    /**
82     * @param $a_expl_id
83     * @param $a_parent_obj
84     * @param $a_parent_cmd
85     * @param $a_tree
86     */
87    public function __construct($a_tree_root_id, $modal_id, $a_expl_id, $a_parent_obj, $a_parent_cmd)
88    {
89        global $DIC;
90        $ilAccess = $DIC['ilAccess'];
91        $lng = $DIC['lng'];
92        $tpl = $DIC['tpl'];
93        $ilToolbar = $DIC['ilToolbar'];
94        $ilCtrl = $DIC['ilCtrl'];
95
96        parent::__construct($a_expl_id, $a_parent_obj, $a_parent_cmd);
97
98        $this->tree_root_id = $a_tree_root_id;
99
100        $this->access = $ilAccess;
101        $this->lng = $lng;
102        $this->tpl = $tpl;
103        $this->toolbar = $ilToolbar;
104        $this->ctrl = $ilCtrl;
105        $this->modal_id = $modal_id;
106        $this->js_conf = array();
107
108        $lng->loadLanguageModule("prg");
109
110        $this->setAjax(true);
111
112        if ($this->checkAccess('write', $a_tree_root_id)) {
113            $this->setEnableDnd(true);
114        }
115    }
116
117
118    /**
119     * Return node element
120     *
121     * @param ilObjStudyProgramme|ilObject $node
122     *
123     * @return string
124     */
125    public function getNodeContent($node)
126    {
127        global $DIC;
128        $lng = $DIC['lng'];
129        $ilAccess = $DIC['ilAccess'];
130
131        $current_ref_id = (isset($_GET["ref_id"]))? $_GET["ref_id"] : -1;
132
133        $is_current_node = ($node->getRefId() == $current_ref_id);
134        $is_study_programme = ($node instanceof ilObjStudyProgramme);
135        $is_root_node = ($is_study_programme && $node->getRoot() == null);
136
137        // show delete only on not current elements and not root
138        $is_delete_enabled = ($is_study_programme && ($is_current_node || $is_root_node))? false : $this->checkAccess("delete", $current_ref_id);
139
140        $is_creation_enabled = ($this->checkAccess("create", $current_ref_id));
141
142        $node_config = array(
143            'current_ref_id' => $current_ref_id,
144            'is_current_node' => $is_current_node,
145            'is_delete_enabled' => $is_delete_enabled,
146            'is_creation_enabled' => $is_creation_enabled,
147            'is_study_programme' => $is_study_programme,
148            'is_root_node' => $is_root_node
149        );
150
151        // TODO: find way to remove a-tag around the content, to create valid html
152        $tpl = $this->getNodeTemplateInstance();
153
154        // add the tree buttons
155        if ($this->checkAccess('write', $node->getRefId())) {
156            if ($is_study_programme) {
157                $this->parseStudyProgrammeNodeButtons($node, $node_config, $tpl);
158            } else {
159                $this->parseLeafNodeButtons($node, $node_config, $tpl);
160            }
161        }
162
163        $tpl->setCurrentBlock('node-content-block');
164        $tpl->setVariable('NODE_TITLE_CLASSES', implode(' ', $this->getNodeTitleClasses($node_config)));
165        $tpl->setVariable('NODE_TITLE', $node->getTitle());
166
167        if ($is_study_programme) {
168            $tpl->setVariable('NODE_POINT_CLASSES', $this->class_configuration['node']['node_point']);
169            $tpl->setVariable('NODE_POINTS', $this->formatPointValue($node->getPoints()));
170        }
171
172        $tpl->parseCurrentBlock('node-content-block');
173
174        return $tpl->get();
175    }
176
177    /**
178     * Returns array with all css classes of the title node element
179     *
180     * @param array $node_config
181     *
182     * @return array
183     */
184    protected function getNodeTitleClasses($node_config)
185    {
186        $node_title_classes = array($this->class_configuration['node']['node_title']);
187        if ($node_config['is_study_programme']) {
188            if ($node_config['is_current_node']) {
189                array_push($node_title_classes, $this->class_configuration['node']['node_current']);
190            }
191        } else {
192            array_push($node_title_classes, $this->class_configuration['lp_object']);
193        }
194
195        return $node_title_classes;
196    }
197
198
199    /**
200     * Generates the buttons for a study-programme node
201     *
202     * @param ilObjStudyProgramme $node parsed node
203     * @param array $node_config configuration of current node
204     * @param ilTemplate $tpl current node template
205     */
206    protected function parseStudyProgrammeNodeButtons($node, $node_config, $tpl)
207    {
208        $tpl->setCurrentBlock('enable-tree-buttons');
209
210        // show info button only when it not the current node
211        $info_button = $this->getNodeButtonActionLink('ilObjStudyProgrammeSettingsGUI', 'view', array('ref_id' => $node->getRefId(), 'currentNode' => $node_config['is_current_node']), ilGlyphGUI::get(ilGlyphGUI::INFO));
212        $tpl->setVariable('NODE_INFO_BUTTON', $info_button);
213
214        // only show add button when create permission is set
215        if ($node_config['is_creation_enabled']) {
216            $create_button = $this->getNodeButtonActionLink('ilObjStudyProgrammeTreeGUI', 'create', array('ref_id' => $node->getRefId()), ilGlyphGUI::get(ilGlyphGUI::ADD));
217            $tpl->setVariable('NODE_CREATE_BUTTON', $create_button);
218        }
219
220        // only show delete button when its not the current node, not the root-node and delete permissions are set
221        if ($node_config['is_delete_enabled']) {
222            $delete_button = $this->getNodeButtonActionLink('ilObjStudyProgrammeTreeGUI', 'delete', array('ref_id' => $node->getRefId(), 'item_ref_id' => $node_config['current_ref_id']), ilGlyphGUI::get(ilGlyphGUI::REMOVE));
223            $tpl->setVariable('NODE_DELETE_BUTTON', $delete_button);
224        }
225
226        $tpl->parseCurrentBlock('enable-tree-buttons');
227    }
228
229    /**
230     * Generates the buttons for a study programme leaf
231     *
232     * @param ilObject $node parsed node
233     * @param array $node_config configuration of current node
234     * @param ilTemplate $tpl current node template
235     */
236    protected function parseLeafNodeButtons($node, $node_config, $tpl)
237    {
238        $tpl->setCurrentBlock('enable-tree-buttons');
239
240        // only show delete button when its not the current node
241        if ($node_config['is_delete_enabled']) {
242            $delete_button = $this->getNodeButtonActionLink('ilObjStudyProgrammeTreeGUI', 'delete', array('ref_id' => $node->getRefId(), 'item_ref_id' => $node_config['current_ref_id']), ilGlyphGUI::get(ilGlyphGUI::REMOVE));
243            $tpl->setVariable('NODE_DELETE_BUTTON', $delete_button);
244        }
245
246        $tpl->parseCurrentBlock('enable-tree-buttons');
247    }
248
249    /**
250     * Factory method for a new instance of a node template
251     *
252     * @return ilTemplate
253     */
254    protected function getNodeTemplateInstance()
255    {
256        return new ilTemplate("tpl.tree_node_content.html", true, true, "Modules/StudyProgramme");
257    }
258
259    /**
260     * Returns formatted point value
261     *
262     * @param $points
263     *
264     * @return string
265     */
266    protected function formatPointValue($points)
267    {
268        return '(' . $points . " " . $this->lng->txt('prg_points') . ')';
269    }
270
271    /**
272     * Generate link-element
273     *
274     * @param      $target_class
275     * @param      $cmd
276     * @param      $params  url-params send to the
277     * @param      $content
278     * @param bool $async
279     *
280     * @return string
281     */
282    protected function getNodeButtonActionLink($target_class, $cmd, $params, $content, $async = true)
283    {
284        foreach ($params as $param_name => $param_value) {
285            $this->ctrl->setParameterByClass($target_class, $param_name, $param_value);
286        }
287
288        $tpl = $this->getNodeTemplateInstance();
289        //$tpl->free();
290        $tpl->setCurrentBlock('tree-button-block');
291
292        $classes = array($this->class_configuration['node']['node_buttons']);
293        $classes[] = 'cmd_' . $cmd;
294
295        $tpl->setVariable('LINK_HREF', $this->ctrl->getLinkTargetByClass($target_class, $cmd, '', true, false));
296        $tpl->setVariable('LINK_CLASSES', implode(' ', $classes));
297
298        if ($async) {
299            $tpl->touchBlock('enable-async-link');
300            $tpl->setVariable('LINK_DATA_TARGET', '#' . $this->modal_id);
301        }
302
303        $tpl->setVariable('LINK_CONTENT', $content);
304
305        //$tpl->parseCurrentBlock('tree-button-block');
306
307        return $tpl->get();
308    }
309
310    /**
311     * Return root node of tree
312     *
313     * @return mixed
314     */
315    public function getRootNode()
316    {
317        $node = ilObjStudyProgramme::getInstanceByRefId($this->tree_root_id);
318        if ($node->getRoot() != null) {
319            return $node->getRoot();
320        }
321        return $node;
322    }
323
324    /**
325     * Get node icon
326     * Return custom icon of OrgUnit type if existing
327     *
328     * @param array $a_node
329     *
330     * @return string
331     */
332    public function getNodeIcon($a_node)
333    {
334        global $DIC;
335        $ilias = $DIC['ilias'];
336
337        $obj_id = ilObject::_lookupObjId($a_node->getRefId());
338        if ($ilias->getSetting('custom_icons')) {
339            //TODO: implement custom icon functionality
340        }
341
342        return ilObject::_getIcon($obj_id, "tiny");
343    }
344
345
346    /**
347     * Returns node link target
348     *
349     * @param mixed $node
350     *
351     * @return string
352     */
353    public function getNodeHref($node)
354    {
355        global $DIC;
356        $ilCtrl = $DIC['ilCtrl'];
357
358        if ($ilCtrl->getCmd() == "performPaste") {
359            $ilCtrl->setParameterByClass("ilObjStudyProgrammeGUI", "target_node", $node->getRefId());
360        }
361
362        $ilCtrl->setParameterByClass("ilObjStudyProgrammeGUI", "ref_id", $node->getRefId());
363
364        return '#';
365    }
366
367    /**
368     * Get childs of node
369     *
370     * @param                  $a_parent_node_id
371     *
372     * @global ilAccess
373     * @internal param int $a_parent_id parent id
374     * @return array childs
375     */
376    public function getChildsOfNode($a_parent_node_id)
377    {
378        global $DIC;
379        $ilAccess = $DIC['ilAccess'];
380
381        $parent_obj = ilObjectFactoryWrapper::singleton()->getInstanceByRefId($a_parent_node_id);
382
383        $children_with_permission = array();
384
385        // its currently only possible to have children on StudyProgrammes
386        if ($parent_obj instanceof ilObjStudyProgramme) {
387            $children = ($parent_obj->hasChildren())? $parent_obj->getChildren() : $parent_obj->getLPChildren();
388
389            if (is_array($children)) {
390                foreach ($children as $node) {
391                    if ($this->checkAccess('visible', $node->getRefId())) {
392                        $children_with_permission[] = $node;
393                    }
394                }
395            }
396        }
397
398        return $children_with_permission;
399    }
400
401    /**
402     * Is node clickable?
403     *
404     * @param mixed            $a_node node object/array
405     *
406     * @global ilAccessHandler $ilAccess
407     * @return boolean node clickable true/false
408     */
409    public function isNodeClickable($a_node)
410    {
411        return true;
412    }
413
414
415    /**
416     * Get id of a node
417     *
418     * @param mixed $a_node node array or object
419     *
420     * @return string id of node
421     */
422    public function getNodeId($a_node)
423    {
424        if (!is_null($a_node)) {
425            return $a_node->getRefId();
426        }
427        return null;
428    }
429
430    /**
431     * List item start
432     *
433     * @param
434     * @return
435     */
436    public function listItemStart($tpl, $a_node)
437    {
438        $tpl->setCurrentBlock("list_item_start");
439
440        if ($this->getAjax() && $this->nodeHasVisibleChilds($a_node) || ($a_node instanceof ilStudyProgramme && $a_node->getParent() === null)) {
441            $tpl->touchBlock("li_closed");
442        }
443        $tpl->setVariable(
444            "DOM_NODE_ID",
445            $this->getDomNodeIdForNodeId($this->getNodeId($a_node))
446        );
447        $tpl->parseCurrentBlock();
448        $tpl->touchBlock("tag");
449    }
450
451
452    /**
453     * Returns the output of the complete tree
454     * There are added some additional javascripts before output the parent::getHTML()
455     *
456     * @return string
457     */
458    public function getHTML()
459    {
460        $this->tpl->addJavascript($this->js_study_programme_path);
461        $this->tpl->addCss($this->css_study_programme_path);
462
463        $this->tpl->addOnLoadCode('$("#' . $this->getContainerId() . '").study_programme_tree(' . json_encode($this->js_conf) . ');');
464
465        return parent::getHTML();
466    }
467
468
469    /**
470     * Closes certain node in the tree session
471     * The open nodes of a tree are stored in a session. This function closes a certain node by its id.
472     *
473     * @param int $node_id
474     */
475    public function closeCertainNode($node_id)
476    {
477        if (in_array($node_id, $this->open_nodes)) {
478            $k = array_search($node_id, $this->open_nodes);
479            unset($this->open_nodes[$k]);
480        }
481        $this->store->set("on_" . $this->id, serialize($this->open_nodes));
482    }
483
484    /**
485     * Open certain node in the tree session
486     * The open nodes of a tree are stored in a session. This function opens a certain node by its id.
487     *
488     * @param int $node_id
489     */
490    public function openCertainNode($node_id)
491    {
492        $id = $this->getNodeIdForDomNodeId($node_id);
493        if (!in_array($id, $this->open_nodes)) {
494            $this->open_nodes[] = $id;
495        }
496        $this->store->set("on_" . $this->id, serialize($this->open_nodes));
497    }
498
499
500    /**
501     * Checks permission of current tree or certain child of it
502     *
503     * @param string $permission
504     * @param null $ref_id
505     *
506     * @return bool
507     */
508    protected function checkAccess($permission, $ref_id)
509    {
510        $checker = $this->access->checkAccess($permission, '', $ref_id);
511
512        return $checker;
513    }
514
515
516    /**
517     * Checks permission of a object and throws an exception if they are not granted
518     *
519     * @param string $permission
520     * @param null $ref_id
521     *
522     * @throws ilException
523     */
524    protected function checkAccessOrFail($permission, $ref_id)
525    {
526        if (!$this->checkAccess($permission, $ref_id)) {
527            throw new ilException("You have no permission for " . $permission . " Object with ref_id " . $ref_id . "!");
528        }
529    }
530
531    /**
532     * Adds configuration to the study-programme-tree jquery plugin
533     *
534     * @param array $js_conf
535     */
536    public function addJsConf($key, $value)
537    {
538        $this->js_conf[$key] = $value;
539    }
540
541    /**
542     * Returns setting of the study-programme-tree
543     *
544     * @param array $js_conf
545     */
546    public function getJsConf($key)
547    {
548        return $this->js_conf[$key];
549    }
550}
551