1<?php
2/* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4include_once("./Modules/Cloud/exceptions/class.ilCloudException.php");
5include_once("class.ilCloudFileNode.php");
6include_once("class.ilCloudConnector.php");
7include_once("class.ilCloudUtil.php");
8
9/**
10 * ilCloudFileTree class
11 *
12 * Representation of the structure of all files and folders so far. Not really a tree but a list simulating a tree
13 * (for faster access on the nodes). This class also calls the functions of a service to update the tree (addToFileTree,
14 * deleteItem, etc.)
15 *
16 * @author Timon Amstutz <timon.amstutz@ilub.unibe.ch>
17 * @version $Id$
18 * @ingroup ModulesCloud
19 */
20class ilCloudFileTree
21{
22
23    /**
24     * id of the ilCloudFileTree, equals the object_id of the calling object or gui_class
25     * @var int id
26     */
27    protected $id = 0;
28
29    /**
30     * @var ilCloudFileNode
31     */
32    protected $root_node = null;
33
34    /**
35     * Path to $root_node ($root_node has always path "/", root_path is the path which can be changed in the settings)
36     * @var string
37     */
38    protected $root_path = "";
39    /**
40     * @var array
41     */
42    protected $item_list = array();
43
44    /**
45     * Only for better performance
46     */
47    protected $id_to_path_map = array();
48
49    /**
50     * @var string $service_name
51     */
52    protected $service_name = "";
53
54    /**
55     * @var bool
56     */
57    protected $case_sensitive = false;
58
59    /**
60     * @param string $root_path
61     * @param string $root_id
62     * @param int $id
63     * @param string $service_name
64     * @param bool $case_sensitive
65     */
66    public function __construct($root_path = "/", $root_id = "root", $id, $service_name)
67    {
68        $this->setId($id);
69        $this->root_node = $this->createNode($root_path, $root_id, true);
70        $this->setServiceName($service_name);
71        $service = ilCloudConnector::getServiceClass($service_name, $id);
72        $this->setCaseSensitive($service->isCaseSensitive());
73    }
74
75    /**
76     * @param int $id
77     */
78    protected function setId($id)
79    {
80        $this->id = $id;
81    }
82
83    /**
84     * @return int
85     */
86    public function getId()
87    {
88        return $this->id;
89    }
90
91
92    /**
93     * @param string $path
94     */
95    protected function setRootPath($path = "/")
96    {
97        $this->root_path = ilCloudUtil::normalizePath($path);
98    }
99
100    /**
101     * @return string
102     */
103    public function getRootPath()
104    {
105        return $this->root_path;
106    }
107
108    /**
109     * @param string $service_name
110     */
111    protected function setServiceName($service_name)
112    {
113        $this->service_name = $service_name;
114    }
115
116    /**
117     * @return string
118     */
119    public function getServiceName()
120    {
121        return $this->service_name;
122    }
123
124    /**
125     * @return boolean
126     */
127    public function isCaseSensitive()
128    {
129        return $this->case_sensitive;
130    }
131
132    /**
133     * @param boolean $case_sensitive
134     */
135    public function setCaseSensitive($case_sensitive)
136    {
137        $this->case_sensitive = $case_sensitive;
138    }
139
140    /**
141     * @return ilCloudFileNode|null
142     */
143    public function getRootNode()
144    {
145        return $this->root_node;
146    }
147
148    /**
149     * @param string $path
150     * @param bool $is_dir
151     * @return ilCloudFileNode
152     */
153    protected function createNode($path = "/", $id, $is_dir = false)
154    {
155        $node = new ilCloudFileNode(ilCloudUtil::normalizePath($path), $id);
156        $this->item_list[$node->getPath()] = $node;
157        $this->id_to_path_map[$node->getId()] = $node->getPath();
158        $node->setIsDir($is_dir);
159        return $node;
160    }
161
162    /**
163     * @param $path
164     * @param $is_Dir
165     * @param null $modified
166     * @param int $size
167     * @return ilCloudFileNode
168     */
169    public function addNode($path, $id, $is_Dir, $modified = null, $size = 0)
170    {
171        $path = ilCloudUtil::normalizePath($path);
172        $node = $this->getNodeFromPath($path);
173
174        //node does not yet exist
175        if (!$node) {
176            if ($this->getNodeFromId($id)) {
177                throw new ilCloudException(ilCloudException::ID_ALREADY_EXISTS_IN_FILE_TREE_IN_SESSION);
178            }
179            $path_of_parent = ilCloudUtil::normalizePath(dirname($path));
180            $node_parent = $this->getNodeFromPath($path_of_parent);
181            if (!$node_parent) {
182                throw new ilCloudException(ilCloudException::PATH_DOES_NOT_EXIST_IN_FILE_TREE_IN_SESSION, "Parent: " . $path_of_parent);
183            }
184            $node = $this->createNode($path, $id, $is_Dir);
185            $node->setParentId($node_parent->getId());
186            $node_parent->addChild($node->getPath());
187        }
188
189        $node->setSize($size);
190        $node->setModified($modified);
191
192        return $node;
193    }
194
195
196    /**
197     * Add node that relies on id's
198     *
199     * @param      $path
200     * @param      $id
201     * @param      $parent_id
202     * @param      $is_Dir
203     * @param null $modified
204     * @param int  $size
205     *
206     * @return ilCloudFileNode
207     * @throws ilCloudException
208     */
209    public function addIdBasedNode($path, $id, $parent_id, $is_Dir, $modified = null, $size = 0)
210    {
211        $path = ilCloudUtil::normalizePath($path);
212        $node = $this->getNodeFromPath($path);
213
214        //node does not yet exist
215        if (!$node) {
216            $nodeFromId = $this->getNodeFromId($id);
217            // If path isn't found but id is there -> Path got changed
218            if ($nodeFromId) {
219                // Adjust path
220                $nodeFromId->setPath($path);
221            }
222
223            $node_parent = $this->getNodeFromId($parent_id);
224            if (!$node_parent) {
225                throw new ilCloudException(ilCloudException::PATH_DOES_NOT_EXIST_IN_FILE_TREE_IN_SESSION, "Parent: " . $parent_id);
226            }
227            $node = $this->createNode($path, $id, $is_Dir);
228            $node->setParentId($node_parent->getId());
229            $node_parent->addChild($node->getPath());
230        }
231
232        $node->setSize($size);
233        $node->setModified($modified);
234
235        return $node;
236    }
237
238
239    /**
240     * @param $path
241     */
242    public function removeNode($path)
243    {
244        $node = $this->getNodeFromPath($path);
245        $parent = $this->getNodeFromId($node->getParentId());
246        $parent->removeChild($path);
247        unset($this->item_list[$node->getPath()]);
248        unset($this->id_to_path_map[$node->getId()]);
249    }
250
251    /**
252     * @return array
253     */
254    public function getItemList()
255    {
256        return $this->item_list;
257    }
258
259    /**
260     * @param   string $path
261     * @return  ilCloudFileNode  node;
262     */
263    public function getNodeFromPath($path = "/")
264    {
265        if (!$this->isCaseSensitive() || $this->item_list[$path]) {
266            return $this->item_list[$path];
267        }
268
269        foreach (array_keys($this->item_list) as $item) {
270            if (strtolower($item) == strtolower($path)) {
271                return $this->item_list[$item];
272            }
273        }
274
275        return null;
276    }
277
278    /**
279     * @param $id
280     * @return bool|ilCloudFileNode
281     */
282    public function getNodeFromId($id)
283    {
284        return $this->item_list[$this->id_to_path_map[$id]];
285    }
286
287    /**
288     * @param $path
289     * @throws ilCloudException
290     */
291    public function setLoadingOfFolderComplete($path)
292    {
293        $node = $this->getNodeFromPath($path);
294        if (!$node) {
295            throw new ilCloudException(ilCloudException::PATH_DOES_NOT_EXIST_IN_FILE_TREE_IN_SESSION, $path);
296        }
297        $node->setLoadingComplete(true);
298    }
299
300    /**
301     * @param $current_path
302     */
303    public function updateFileTree($current_path)
304    {
305        $node = $this->getNodeFromPath($current_path);
306
307        if (!$node) {
308            $this->updateFileTree(dirname($current_path));
309            $node = $this->getNodeFromPath($current_path);
310        }
311        if (!$node->getLoadingComplete()) {
312            $this->addItemsFromService($node->getId());
313        }
314        $this->storeFileTreeToSession();
315    }
316
317
318    /**
319     * @param $folder_id
320     *
321     * @throws ilCloudException
322     */
323    public function addItemsFromService($folder_id)
324    {
325        try {
326            $node = $this->getNodeFromId($folder_id);
327            if (!$node) {
328                throw new ilCloudException(ilCloudException::ID_DOES_NOT_EXIST_IN_FILE_TREE_IN_SESSION, $folder_id);
329            }
330            $service = ilCloudConnector::getServiceClass($this->getServiceName(), $this->getId());
331            if (!$service->addToFileTreeWithId($this, $node->getId())) {
332                $service->addToFileTree($this, $node->getPath());
333            }
334        } catch (Exception $e) {
335            if ($e instanceof ilCloudException) {
336                throw $e;
337            }
338            throw new ilCloudException(ilCloudException::ADD_ITEMS_FROM_SERVICE_FAILED, $e->getMessage());
339        }
340    }
341
342
343    /**
344     * @param $id
345     * @param $folder_name
346     *
347     * @return bool|ilCloudFileNode|null
348     * @throws ilCloudException
349     */
350    public function addFolderToService($id, $folder_name)
351    {
352        try {
353            if ($folder_name == null) {
354                throw new ilCloudException(ilCloudException::INVALID_INPUT, $folder_name);
355            }
356            $current_node = $this->getNodeFromId($id);
357            $path = ilCloudUtil::joinPaths($current_node->getPath(), ilCloudUtil::normalizePath($folder_name));
358
359            if ($this->getNodeFromPath($path) != null) {
360                throw new ilCloudException(ilCloudException::FOLDER_ALREADY_EXISTING_ON_SERVICE, $folder_name);
361            }
362
363
364            $current_node->setLoadingComplete(false);
365            $this->storeFileTreeToSession();
366
367            $service = ilCloudConnector::getServiceClass($this->getServiceName(), $this->getId());
368
369            $new_folder_id = $service->createFolderById($id, $folder_name);
370            $new_node = null;
371
372            if (is_null($new_folder_id) || !$new_folder_id) {
373                // Use path
374                $service->createFolder($path, $this);
375                $this->addItemsFromService($current_node->getId());
376                $new_path = ilCloudUtil::joinPaths($current_node->getPath(), $folder_name);
377                $new_node = $this->getNodeFromPath($new_path);
378            } else {
379                // Use id
380                $this->addItemsFromService($current_node->getId());
381                $new_node = $this->getNodeFromId($new_folder_id);
382            }
383
384            return $new_node;
385        } catch (Exception $e) {
386            if ($e instanceof ilCloudException) {
387                throw $e;
388            }
389            throw new ilCloudException(ilCloudException::FOLDER_CREATION_FAILED, $e->getMessage());
390        }
391    }
392
393
394    /**
395     * @param $current_id
396     * @param $tmp_name
397     * @param $file_name
398     *
399     * @throws ilCloudException
400     */
401    public function uploadFileToService($current_id, $tmp_name, $file_name)
402    {
403        $plugin = ilCloudConnector::getPluginClass($this->getServiceName(), $this->getId());
404        $max_file_size = $plugin->getMaxFileSize();
405
406        if ($max_file_size >= filesize($tmp_name) / (1024 * 1024)) {
407            $current_node = $this->getNodeFromId($current_id);
408
409            $current_node->setLoadingComplete(false);
410            $this->storeFileTreeToSession();
411
412            try {
413                $service = ilCloudConnector::getServiceClass($this->getServiceName(), $this->getId());
414                if (!$service->putFileById($tmp_name, $file_name, $current_node->getId(), $this)) {
415                    $service->putFile($tmp_name, $file_name, $current_node->getPath(), $this);
416                }
417            } catch (Exception $e) {
418                if ($e instanceof ilCloudException) {
419                    throw $e;
420                }
421                throw new ilCloudException(ilCloudException::UPLOAD_FAILED, $e->getMessage());
422            }
423        } else {
424            throw new ilCloudException(ilCloudException::UPLOAD_FAILED_MAX_FILESIZE, filesize($tmp_name) / (1024 * 1024) . " MB");
425        }
426    }
427
428    /**
429     * @param $id
430     * @throws ilCloudException
431     */
432    public function deleteFromService($id)
433    {
434        $item_node = $this->getNodeFromId($id);
435
436        try {
437            $service = ilCloudConnector::getServiceClass($this->getServiceName(), $this->getId());
438
439            if (!$service->deleteItemById($item_node->getId())) {
440                $service->deleteItem($item_node->getPath(), $this);
441            }
442
443            $this->removeNode($item_node->getPath());
444            $this->storeFileTreeToSession();
445        } catch (Exception $e) {
446            if ($e instanceof ilCloudException) {
447                throw $e;
448            }
449            throw new ilCloudException(ilCloudException::DELETE_FAILED, $e->getMessage());
450        }
451    }
452
453    /**
454     * @param $id
455     * @throws ilCloudException
456     */
457    public function downloadFromService($id)
458    {
459        try {
460            $service = ilCloudConnector::getServiceClass($this->getServiceName(), $this->getId());
461            $node = $this->getNodeFromId($id);
462
463            if (!$service->getFileById($node->getId())) {
464                $service->getFile($node->getPath(), $this);
465            }
466        } catch (Exception $e) {
467            if ($e instanceof ilCloudException) {
468                throw $e;
469            }
470            throw new ilCloudException(ilCloudException::DOWNLOAD_FAILED, $e->getMessage());
471        }
472    }
473
474    public function storeFileTreeToSession()
475    {
476        $_SESSION['ilCloudFileTree'] = null;
477        $_SESSION['ilCloudFileTree'] = serialize($this);
478    }
479
480    /**
481     * @return    ilCloudFileTree  fileTree;
482     */
483    public static function getFileTreeFromSession()
484    {
485        if (isset($_SESSION['ilCloudFileTree'])) {
486            return unserialize($_SESSION['ilCloudFileTree']);
487        } else {
488            return false;
489        }
490    }
491
492
493    public static function clearFileTreeSession()
494    {
495        $_SESSION['ilCloudFileTree'] = null;
496    }
497
498    /**
499     * @param $path1
500     * @param $path2
501     * @return int
502     */
503    public function orderListAlphabet($path1, $path2)
504    {
505        $node1 = $this->getNodeFromPath($path1);
506        $node2 = $this->getNodeFromPath($path2);
507        if ($node1->getIsDir() != $node2->getIsDir()) {
508            return $node2->getIsDir() ? +1 : -1;
509        }
510        $nameNode1 = strtolower(basename($node1->getPath()));
511        $nameNode2 = strtolower(basename($node2->getPath()));
512        return ($nameNode1 > $nameNode2) ? +1 : -1;
513    }
514
515    /**
516     * @param ilCloudFileNode $node
517     * @return array|null
518     */
519    public function getSortedListOfChildren(ilCloudFileNode $node)
520    {
521        $children = $node->getChildrenPathes();
522        usort($children, array("ilCloudFileTree", "orderListAlphabet"));
523        return $children;
524    }
525
526    /**
527     * @return array
528     */
529    public function getListForJSONEncode()
530    {
531        $list = array();
532        foreach ($this->getItemList() as $path => $node) {
533            $list[$node->getId()] = $node->getJSONEncode();
534        }
535        return $list;
536    }
537}
538