1<?php
2/* Copyright (c) 1998-2016 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4/**
5 * This class handles all operations on files for the drafts of a forum object.
6 *
7 * @author	Nadia Matuschek <nmatuschek@databay.de>
8 *
9 * @ingroup ModulesForum
10 */
11class ilFileDataForumDrafts extends ilFileData
12{
13    protected $obj_id = 0;
14    protected $draft_id = 0;
15    protected $drafts_path = '';
16    private $lng;
17    private $error;
18
19    public function __construct($obj_id = 0, $draft_id)
20    {
21        global $DIC;
22        $this->lng = $DIC->language();
23        $this->error = $DIC['ilErr'];
24
25        $this->obj_id = $obj_id;
26        $this->draft_id = $draft_id;
27
28        define('FORUM_DRAFTS_PATH', 'forum/drafts');
29        parent::__construct();
30        $this->drafts_path = parent::getPath() . "/" . FORUM_DRAFTS_PATH;
31
32        // IF DIRECTORY ISN'T CREATED CREATE IT
33        if (!$this->__checkPath()) {
34            $this->__initDirectory();
35        }
36    }
37
38    /**
39     * @return int
40     */
41    public function getObjId()
42    {
43        return $this->obj_id;
44    }
45
46    /**
47     * @param int $obj_id
48     */
49    public function setObjId($obj_id)
50    {
51        $this->obj_id = $obj_id;
52    }
53
54    /**
55     * @return int
56     */
57    public function getDraftId()
58    {
59        return $this->draft_id;
60    }
61
62    /**
63     * @param int $draft_id
64     */
65    public function setDraftId($draft_id)
66    {
67        $this->draft_id = $draft_id;
68    }
69
70    /**
71     * @return string
72     */
73    public function getDraftsPath()
74    {
75        return $this->drafts_path;
76    }
77
78
79    /**
80     * @return array
81     */
82    public function getFiles()
83    {
84        $files = array();
85
86        foreach (new DirectoryIterator($this->getDraftsPath() . '/' . $this->getDraftId()) as $file) {
87            /**
88             * @var $file SplFileInfo
89             */
90
91            if ($file->isDir()) {
92                continue;
93            }
94
95            $files[] = array(
96                'path' => $file->getPathname(),
97                'md5' => md5($file->getFilename()),
98                'name' => $file->getFilename(),
99                'size' => $file->getSize(),
100                'ctime' => date('Y-m-d H:i:s', $file->getCTime())
101            );
102        }
103
104        return $files;
105    }
106
107    /**
108     * @return array
109     */
110    public function getFilesOfPost()
111    {
112        $files = array();
113
114        foreach (new DirectoryIterator($this->getDraftsPath() . '/' . $this->getDraftId()) as $file) {
115            /**
116             * @var $file SplFileInfo
117             */
118
119            if ($file->isDir()) {
120                continue;
121            }
122
123            $files[$file->getFilename()] = array(
124                'path' => $file->getPathname(),
125                'md5' => md5($file->getFilename()),
126                'name' => $file->getFilename(),
127                'size' => $file->getSize(),
128                'ctime' => date('Y-m-d H:i:s', $file->getCTime())
129            );
130        }
131
132        return $files;
133    }
134
135    public function moveFilesOfDraft($forum_path, $new_post_id)
136    {
137        foreach ($this->getFilesOfPost() as $file) {
138            @copy(
139                $file['path'],
140                $forum_path . '/' . $this->obj_id . '_' . $new_post_id . '_' . $file['name']
141            );
142        }
143        return true;
144    }
145
146    /**
147     * @return bool
148     */
149    public function delete()
150    {
151        ilUtil::delDir($this->getDraftsPath() . '/' . $this->getDraftId());
152        return true;
153    }
154
155    /**
156     *
157     * Store uploaded files in filesystem
158     *
159     * @param	array	$files	Copy of $_FILES array,
160     * @access	public
161     * @return	bool
162     *
163     */
164    public function storeUploadedFile($files)
165    {
166        if (isset($files['name']) && is_array($files['name'])) {
167            foreach ($files['name'] as $index => $name) {
168                // remove trailing '/'
169                while (substr($name, -1) == '/') {
170                    $name = substr($name, 0, -1);
171                }
172                $filename = ilUtil::_sanitizeFilemame($name);
173                $temp_name = $files['tmp_name'][$index];
174                $error = $files['error'][$index];
175
176                if (strlen($filename) && strlen($temp_name) && $error == 0) {
177                    $path = $this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $filename;
178
179                    $this->__rotateFiles($path);
180                    ilUtil::moveUploadedFile($temp_name, $filename, $path);
181                }
182            }
183
184            return true;
185        } elseif (isset($files['name']) && is_string($files['name'])) {
186            // remove trailing '/'
187            while (substr($files['name'], -1) == '/') {
188                $files['name'] = substr($files['name'], 0, -1);
189            }
190            $filename = ilUtil::_sanitizeFilemame($files['name']);
191            $temp_name = $files['tmp_name'];
192
193            $path = $this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $filename;
194
195            $this->__rotateFiles($path);
196            ilUtil::moveUploadedFile($temp_name, $filename, $path);
197
198            return true;
199        }
200
201        return false;
202    }
203    /**
204     * unlink files: expects an array of filenames e.g. array('foo','bar')
205     * @param array filenames to delete
206     * @access	public
207     * @return string error message with filename that couldn't be deleted
208     */
209    public function unlinkFiles($a_filenames)
210    {
211        if (is_array($a_filenames)) {
212            foreach ($a_filenames as $file) {
213                if (!$this->unlinkFile($file)) {
214                    return $file;
215                }
216            }
217        }
218        return '';
219    }
220    /**
221     * unlink one uploaded file expects a filename e.g 'foo'
222     * @param string filename to delete
223     * @access	public
224     * @return bool
225     */
226    public function unlinkFile($a_filename)
227    {
228        if (file_exists($this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $a_filename)) {
229            return unlink($this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $a_filename);
230        }
231    }
232    /**
233     * get absolute path of filename
234     * @param string relative path
235     * @access	public
236     * @return string absolute path
237     */
238    public function getAbsolutePath($a_path)
239    {
240        return $this->getDraftsPath() . '/' . $this->getDraftId();
241    }
242
243    /**
244     * get file data of a specific attachment
245     * @param string md5 encrypted filename
246     * @access public
247     * @return array filedata
248     */
249    public function getFileDataByMD5Filename($a_md5_filename)
250    {
251        $files = ilUtil::getDir($this->getDraftsPath() . '/' . $this->getDraftId());
252        foreach ((array) $files as $file) {
253            if ($file['type'] == 'file' && md5($file['entry']) == $a_md5_filename) {
254                return array(
255                    'path' => $this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $file['entry'],
256                    'filename' => $file['entry'],
257                    'clean_filename' => $file['entry']
258                );
259            }
260        }
261
262        return false;
263    }
264
265    /**
266     * get file data of a specific attachment
267     * @param string|array md5 encrypted filename or array of multiple md5 encrypted files
268     * @access public
269     * @return boolean status
270     */
271    public function unlinkFilesByMD5Filenames($a_md5_filename)
272    {
273        $files = ilUtil::getDir($this->getDraftsPath() . '/' . $this->getDraftId());
274        if (is_array($a_md5_filename)) {
275            foreach ((array) $files as $file) {
276                if ($file['type'] == 'file' && in_array(md5($file['entry']), $a_md5_filename)) {
277                    unlink($this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $file['entry']);
278                }
279            }
280
281            return true;
282        } else {
283            foreach ((array) $files as $file) {
284                if ($file['type'] == 'file' && md5($file['entry']) == $a_md5_filename) {
285                    return unlink($this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $file['entry']);
286                }
287            }
288        }
289
290        return false;
291    }
292
293    /**
294     * check if files exist
295     * @param array filenames to check
296     * @access	public
297     * @return bool
298     */
299    public function checkFilesExist($a_files)
300    {
301        if ($a_files) {
302            foreach ($a_files as $file) {
303                if (!file_exists($this->getDraftsPath() . '/' . $this->getDraftId() . '/' . $file)) {
304                    return false;
305                }
306            }
307            return true;
308        }
309        return true;
310    }
311
312    // PRIVATE METHODS
313    public function __checkPath()
314    {
315        if (!@file_exists($this->getDraftsPath() . '/' . $this->getDraftId())) {
316            return false;
317        }
318        $this->__checkReadWrite();
319
320        return true;
321    }
322    /**
323     * check if directory is writable
324     * overwritten method from base class
325     * @access	private
326     * @return bool
327     */
328    public function __checkReadWrite()
329    {
330        if (is_writable($this->getDraftsPath() . '/' . $this->getDraftId()) && is_readable($this->getDraftsPath() . '/' . $this->getDraftId())) {
331            return true;
332        } else {
333            $this->error->raiseError("Forum directory is not readable/writable by webserver", $this->error->FATAL);
334        }
335    }
336    /**
337     * init directory
338     * overwritten method
339     * @access	public
340     * @return string path
341     */
342    public function __initDirectory()
343    {
344        if (is_writable($this->getPath())) {
345            if (ilUtil::makeDirParents($this->getDraftsPath() . "/" . $this->getDraftId())) {
346                if (chmod($this->getDraftsPath() . "/" . $this->getDraftId(), 0755)) {
347                    return true;
348                }
349            }
350        }
351        return false;
352    }
353    /**
354     * rotate files with same name
355     * recursive method
356     * @param string filename
357     * @access	private
358     * @return bool
359     */
360    public function __rotateFiles($a_path)
361    {
362        if (file_exists($a_path)) {
363            $this->__rotateFiles($a_path . ".old");
364            return \ilFileUtils::rename($a_path, $a_path . '.old');
365        }
366        return true;
367    }
368
369    /**
370     * @param $file  $_GET['file']
371     * @return bool|void
372     */
373    public function deliverFile($file)
374    {
375        if (!$path = $this->getFileDataByMD5Filename($file)) {
376            return ilUtil::sendFailure($this->lng->txt('error_reading_file'), true);
377        } else {
378            return ilUtil::deliverFile($path['path'], $path['clean_filename']);
379        }
380    }
381
382    public function deliverZipFile()
383    {
384        $zip_file = $this->createZipFile();
385        if (!$zip_file) {
386            ilUtil::sendFailure($this->lng->txt('error_reading_file'), true);
387            return false;
388        } else {
389            $post = ilForumPostDraft::newInstanceByDraftId($this->getDraftId());
390            ilUtil::deliverFile($zip_file, $post->getPostSubject() . '.zip', '', false, true, false);
391            ilUtil::delDir($this->getDraftsPath() . '/drafts_zip/' . $this->getDraftId());
392            exit();
393        }
394    }
395
396    /**
397     * @return null|string
398     */
399    public function createZipFile()
400    {
401        $filesOfDraft = $this->getFilesOfPost();
402        if (count($filesOfDraft)) {
403            ksort($filesOfDraft);
404
405            ilUtil::makeDirParents($this->getDraftsPath() . '/drafts_zip/' . $this->getDraftId());
406            $tmp_dir = $this->getDraftsPath() . '/drafts_zip/' . $this->getDraftId();
407            foreach ($filesOfDraft as $file) {
408                @copy($file['path'], $tmp_dir . '/' . $file['name']);
409            }
410        }
411
412        $zip_file = null;
413        if (ilUtil::zip($tmp_dir, $this->getDraftsPath() . '/drafts_zip/' . $this->getDraftId() . '.zip')) {
414            $zip_file = $this->getDraftsPath() . '/drafts_zip/' . $this->getDraftId() . '.zip';
415        }
416
417        return $zip_file;
418    }
419}
420