1<?php
2
3/**
4 * Copyright Intermesh
5 *
6 * This file is part of Group-Office. You should have received a copy of the
7 * Group-Office license along with Group-Office. See the file /LICENSE.TXT
8 *
9 * If you have questions write an e-mail to info@intermesh.nl
10 *
11 * @version $Id: File.php 7607 2011-09-01 15:40:20Z <<USERNAME>> $
12 * @copyright Copyright Intermesh
13 * @author <<FIRST_NAME>> <<LAST_NAME>> <<EMAIL>>@intermesh.nl
14 */
15
16namespace GO\Files\Model;
17
18use GO;
19use go\core\exception\NotFound;
20
21/**
22 * The File model
23 *
24 * @property int $id
25 * @property int $folder_id
26 * @property String $name
27
28 * @property int $locked_user_id
29 * @property int $status_id
30 * @property int $ctime
31 * @property int $mtime
32 * @property int $muser_id
33 * @property int $size
34 * @property int $user_id
35 * @property String $comments
36 * @property String $extension
37 * @property int $expire_time
38 * @property String $random_code
39 * @property String $content_expire_date
40 *
41 * @property String $thumbURL
42 *
43 * @property String $downloadUrl
44 *
45 * @property String $path
46 * @property \GO\Base\Fs\File $fsFile
47 * @property Folder $folder
48 * @property \GO\Base\Model\User $lockedByUser
49 *
50 * @property boolean $delete_when_expired
51 */
52class File extends \GO\Base\Db\ActiveRecord implements \GO\Base\Mail\SwiftAttachableInterface {
53
54	use \go\core\orm\CustomFieldsTrait;
55
56	public static $deleteInDatabaseOnly=false;
57
58	private $_permissionLevel;
59
60	public static $trimOnSave = false;
61
62	/**
63	 * Returns a static model of itself
64	 *
65	 * @param String $className
66	 * @return File
67	 */
68	public static function model($className=__CLASS__) {
69		return parent::model($className);
70	}
71
72	/**
73	 * Enable this function if you want this model to check the acl's automatically.
74	 */
75	public function aclField() {
76		return 'folder.acl_id';
77	}
78
79	/**
80	 * Returns the table name
81	 */
82	public function tableName() {
83		return 'fs_files';
84	}
85
86	protected function getLocalizedName() {
87		return \GO::t("File", "files");
88	}
89
90	public function customfieldsModel() {
91		return "GO\Files\Customfields\Model\File";
92	}
93
94	public function hasLinks() {
95		return true;
96	}
97
98	protected function getCacheAttributes() {
99
100		$path = $this->path;
101
102		//Don't cache tickets files because there are permissions issues. Everyone has read access to the types but may not see other peoples files.
103		if(strpos($path, 'tickets/')===0){
104			return false;
105		}
106
107		return array('name'=>$this->name, 'description'=>$path);
108	}
109
110	public function getLogMessage($action){
111		return $this->path;
112	}
113	/**
114	 * Here you can define the relations of this model with other models.
115	 * See the parent class for a more detailed description of the relations.
116	 */
117	public function relations() {
118		return array(
119				'lockedByUser' => array('type' => self::BELONGS_TO, 'model' => 'GO\Base\Model\User', 'field' => 'locked_user_id'),
120				'folder' => array('type' => self::BELONGS_TO, 'model' => 'GO\Files\Model\Folder', 'field' => 'folder_id'),
121				'versions' => array('type'=>self::HAS_MANY, 'model'=>'GO\Files\Model\Version', 'field'=>'file_id', 'delete'=>true),
122		);
123	}
124
125	public function getPermissionLevel(){
126
127		if(\GO::$ignoreAclPermissions)
128			return \GO\Base\Model\Acl::MANAGE_PERMISSION;
129
130		if(!$this->aclField())
131			return -1;
132
133		if(!\GO::user())
134			return false;
135
136		//if($this->isNew && !$this->joinAclField){
137		if(empty($this->{$this->aclField()}) && !$this->getIsJoinedAclField()){
138			//the new model has it's own ACL but it's not created yet.
139			//In this case we will check the module permissions.
140			$module = $this->getModule();
141			if($module=='base'){
142				return \GO::user()->isAdmin() ? \GO\Base\Model\Acl::MANAGE_PERMISSION : false;
143			}else
144				return \GO::modules()->$module->permissionLevel;
145
146		}else
147		{
148			if(!isset($this->_permissionLevel)){
149
150				$acl_id = $this->findAclId();
151				if(!$acl_id){
152					throw new \Exception("Could not find ACL for ".$this->className()." with pk: ".$this->pk);
153				}
154
155				$this->_permissionLevel=\GO\Base\Model\Acl::getUserPermissionLevel($acl_id);// model()->findByPk($acl_id)->getUserPermissionLevel();
156			}
157			return $this->_permissionLevel;
158		}
159
160	}
161
162	protected function init() {
163		$this->columns['expire_time']['gotype'] = 'unixdate';
164		$this->columns['content_expire_date']['gotype'] = 'unixdate';
165		$this->columns['name']['required']=true;
166		parent::init();
167	}
168
169	/**
170	 * Check if a file is locked by another user.
171	 *
172	 * @return boolean
173	 */
174	public function isLocked(){
175		return !empty($this->locked_user_id) && (!\GO::user() || $this->locked_user_id!=\GO::user()->id);
176	}
177
178	public function unlockAllowed(){
179		return ($this->locked_user_id==\GO::user()->id || \GO::user()->isAdmin()) && $this->checkPermissionLevel(\GO\Base\Model\Acl::WRITE_PERMISSION);
180	}
181
182	public function getJsonData() {
183			$data =  array(
184					'id' => $this->id,
185					'name' => $this->path,
186					'ctime' => \GO\Base\Util\Date::get_timestamp($this->ctime),
187					'mtime' => \GO\Base\Util\Date::get_timestamp($this->mtime),
188					'extension' => $this->extension,
189					'size' => $this->size,
190					'user_id' => $this->user_id,
191//					'type' => $this->type,
192					'folder_id' => $this->folder_id,
193					'type_id' => 'f:'.$this->id,
194					'path' => $this->path,
195					'locked' => $this->isLocked(),
196					'locked_user_id' => $this->locked_user_id,
197					'unlock_allowed' => $this->unlockAllowed(),
198					'expire_time' => $this->expire_time > 0 ? \GO\Base\Util\Date::get_timestamp($this->expire_time,false) : '',
199					'thumbs' => 0,
200					'thumb_url' => $this->getThumbURL()
201				);
202
203			if(method_exists($this, "getCustomFields")) {
204				$data['customFields'] = $this->getCustomFields()->toArray();
205			}
206			return $data;
207	}
208
209	/**
210	 *
211	 * @return \GO\Base\Fs\File
212	 */
213	private function _getOldFsFile(){
214
215		if($this->isNew)
216			return $this->fsFile;
217
218		$filename = $this->isModified('name') ? $this->getOldAttributeValue('name') : $this->name;
219		if($this->isModified('folder_id')){
220			//file will be moved so we need the old folder path.
221			$oldFolderId = $this->getOldAttributeValue('folder_id');
222			$oldFolder = Folder::model()->findByPk($oldFolderId);
223			$oldRelPath = $oldFolder->path;
224			$oldPath = \GO::config()->file_storage_path . $oldRelPath . '/' . $filename;
225
226		}else{
227			$oldPath = \GO::config()->file_storage_path . $this->folder->path.'/'.$filename;
228		}
229		return new \GO\Base\Fs\File($oldPath);
230	}
231
232	protected function beforeDelete() {
233
234		//blocked database check. We check this in the controller now.
235		if($this->isLocked() && !\GO::user()->isAdmin())
236			throw new \Exception(\GO::t("File is locked", "files").': '.$this->path);
237
238		return parent::beforeDelete();
239	}
240
241	/**
242	 * Check the disk and user quota
243	 * @param integer $newBytes amount of bytes that are added when check succeeds
244	 * @return boolean true if the check passed and the file may be added
245	 */
246	public static function checkQuota($newBytes) {
247		$enoughQuota = true;
248		$userQuota = \GO::user()->getDiskQuota();
249
250		if ($userQuota) {
251			$enoughQuota = \GO::user()->disk_usage + $newBytes <= $userQuota;
252		}
253		if ($enoughQuota && \GO::config()->quota > 0) {
254			$currentQuota = \GO::config()->get_setting('file_storage_usage');
255			$enoughQuota = $currentQuota + $newBytes <= (\GO::config()->quota * 1024);
256		}
257
258		return $enoughQuota;
259	}
260
261	public function checkNormalization() {
262		if(!\go\core\util\StringUtil::isNormalized($this->name)) {
263			\GO::debug("Normalizing file $this->id to Unicode Form C");
264
265			$name = \go\core\util\StringUtil::normalize($this->name);
266
267			if($this->getFsFile()->exists()) {
268				$this->getFsFile()->rename($name);
269			}
270
271			if(!$this->getIsNew()) {
272				 go()->getDbConnection()->update('fs_files',['name' => $name], ['id' => $this->id])->execute();
273			}
274			$this->name = $name;
275		}
276	}
277
278	protected function beforeSave() {
279
280		//Normalize UTF-8. ONly form D works on MacOS webdav!
281		$this->checkNormalization();
282
283		//check permissions on the filesystem
284		if($this->isNew){
285
286			if(is_null($this->folder->fsFolder)){
287				throw new \Exception("Folder ".$this->folder->path." cannot be found on disk, please check this path manually.");
288			}
289
290			if(!$this->folder->fsFolder->isWritable()){
291				throw new \Exception("Folder ".$this->folder->path." is read only on the filesystem. Please check the file system permissions (hint: chown -R www-data:www-data /home/groupoffice)");
292			}
293		}else
294		{
295			if($this->isModified('name') || $this->isModified('folder_id')){
296				if($this->_getOldFsFile()->exists() && !$this->_getOldFsFile()->isWritable())
297					throw new \Exception("File ".$this->_getOldFsFile()->path ()." is read only on the filesystem. Please check the file system permissions (hint: chown -R www-data:www-data /home/groupoffice)");
298			}
299		}
300
301		if($this->isNew || $this->isModified('name')){
302			$existingFile = $this->folder->hasFile($this->name);
303			if($existingFile && $existingFile->id!=$this->id)
304				throw new \Exception(sprintf(\GO::t("Filename %s already exists", "files"), $this->path));
305		}
306
307		if(!$this->isNew){
308
309			if($this->isModified('name')){
310				//rename filesystem file.
311				//throw new \Exception($this->getOldAttributeValue('name'));
312				$oldFsFile = $this->_getOldFsFile();
313				if($oldFsFile->exists())
314					$oldFsFile->rename($this->name);
315
316				$this->notifyUsers(
317					$this->folder_id,
318					FolderNotificationMessage::RENAME_FILE,
319					$this->folder->path . '/' . $this->getOldAttributeValue('name'),
320					$this->folder->path . '/' . $this->name
321				);
322			}
323
324			if($this->isModified('folder_id')){
325				if(!isset($oldFsFile))
326					$oldFsFile = $this->_getOldFsFile();
327
328				if (!$oldFsFile->move(new \GO\Base\Fs\Folder(\GO::config()->file_storage_path . dirname($this->path))))
329					throw new \Exception("Could not rename folder on the filesystem");
330
331				//get old folder objekt
332                                $oldFolderId = $this->getOldAttributeValue('folder_id');
333				$oldFolder = Folder::model()->findByPk($oldFolderId);
334
335				$this->notifyUsers(
336					array(
337					    $this->getOldAttributeValue('folder_id'),
338					    $this->folder_id
339					),
340					FolderNotificationMessage::MOVE_FILE,
341					$oldFolder->path . '/' . $this->name,
342					$this->path
343				);
344			}
345		}
346
347		if($this->isModified('locked_user_id')){
348			$old_locked_user_id = $this->getOldAttributeValue('locked_user_id');
349			if(!empty($old_locked_user_id) && $old_locked_user_id != \GO::user()->id && !\GO::user()->isAdmin())
350				throw new \GO\Files\Exception\FileLocked();
351		}
352
353
354		$this->extension = $this->fsFile->extension();
355		//make sure extension is not too long
356		$this->cutAttributeLength("extension");
357
358		$this->size = $this->fsFile->size();
359		//$this->ctime = $this->fsFile->ctime();
360		$this->mtime = $this->fsFile->mtime();
361
362
363
364		return parent::beforeSave();
365	}
366
367	protected function getPath() {
368		return $this->folder ? $this->folder->path . '/' . $this->name : $this->name;
369	}
370
371	public function getVirtualPath() {
372		return $this->folder->getVirtualPath() . '/' . $this->name;
373	}
374
375	protected function getFsFile() {
376		return new \GO\Base\Fs\File(\GO::config()->file_storage_path . $this->path);
377	}
378
379	private function _addQuota(){
380
381		if($this->isModified('size') || $this->isNew) {
382			$sizeDiff = (int) $this->fsFile->size() - (int) $this->getOldAttributeValue('size');
383
384//			GO::debug("Adding quota: $sizeDiff for ".$this->folder->quotaUser->getName());
385			if($this->folder->quotaUser){
386				$this->folder->quotaUser->calculatedDiskUsage($sizeDiff)->save(true); //user quota
387			}
388			if(GO::config()->quota>0) {
389				GO::config()->save_setting("file_storage_usage", GO::config()->get_setting('file_storage_usage')+$sizeDiff); //system quota
390			}
391		}
392
393	}
394
395	private function _removeQuota(){
396		if(\GO::config()->quota>0){
397			\GO::debug("Removing quota: $this->size");
398			\GO::config()->save_setting("file_storage_usage", \GO::config()->get_setting('file_storage_usage')-$this->size);
399		}
400
401		if($this->folder->quotaUser){
402			$this->folder->quotaUser->calculatedDiskUsage (0-$this->size)->save(true);
403		}
404
405	}
406
407	public function checkPermissionLevel($level) {
408
409//			var_dump($this->acl->description);
410
411		//If this folder belongs to a contact or project etc. then we only need write permission to delete it.
412		if($level == \GO\Base\Model\Acl::DELETE_PERMISSION && $this->folder->acl->usedIn != 'fs_folders.acl_id') {
413			$level = \GO\Base\Model\Acl::WRITE_PERMISSION;
414		}
415
416		return parent::checkPermissionLevel($level);
417	}
418
419	private function getOldFolder() {
420		return Folder::model()->findByPk($this->getOldAttributeValue('folder_id'));
421	}
422
423
424	public function checkOldPermissionLevel($level) {
425		//If this folder belongs to a contact or project etc. then we only need write permission to delete it.
426		if($level == \GO\Base\Model\Acl::DELETE_PERMISSION && $this->getOldFolder()->acl->usedIn != 'fs_folders.acl_id') {
427			$level = \GO\Base\Model\Acl::WRITE_PERMISSION;
428		}
429		return parent::checkOldPermissionLevel($level);
430	}
431
432	protected function afterSave($wasNew) {
433		$this->_addQuota();
434
435		if ($wasNew) {
436			$this->notifyUsers(
437				$this->folder_id,
438				FolderNotificationMessage::ADD_FILE,
439                $this->name,
440				$this->folder->path
441			);
442		} else {
443			if ($this->isModified() && !$this->isModified('name') && !$this->isModified('folder_id')) {
444				$this->notifyUsers(
445					$this->folder_id,
446					FolderNotificationMessage::UPDATE_FILE,
447					$this->path
448				);
449			}
450		}
451
452
453		//touch the timestamp so it won't sync with the filesystem
454		if($this->isModified('folder_id')){
455
456			GO::debug("touching parent");
457			$this->folder->touch();
458
459			$oldParent = \GO\Files\Model\Folder::model()->findByPk($this->getOldAttributeValue('folder_id'));
460
461			if($oldParent){
462				GO::debug("touching old parent");
463				$oldParent->touch();
464			}
465		}
466
467
468		return parent::afterSave($wasNew);
469	}
470
471	public $customVersionPath = null;
472	public function getVersionStoragePath() {
473		if($this->customVersionPath!==null) {
474			return $this->customVersionPath;
475		}
476		return 'versioning/'.$this->id;
477	}
478
479	protected function afterDelete() {
480
481		$this->_removeQuota();
482
483		if(!File::$deleteInDatabaseOnly)
484			$this->fsFile->delete();
485
486		$versioningFolder = new \GO\Base\Fs\Folder(\GO::config()->file_storage_path.$this->getVersionStoragePath());
487		$versioningFolder->delete();
488
489		$this->notifyUsers(
490            $this->folder_id,
491			FolderNotificationMessage::DELETE_FILE,
492			$this->path
493		);
494
495		return parent::afterDelete();
496	}
497
498	/**
499	 * The link that can be send in an e-mail as download link.
500	 *
501	 * @return StringHelper
502	 */
503	public function getEmailDownloadURL($html=true, $newExpireTime=false, $deleteWhenExpired=false) {
504
505		if($newExpireTime && $this->expire_time < $newExpireTime){
506			if($this->expire_time <= time() || empty($this->random_code)) {
507				$this->random_code=\GO\Base\Util\StringHelper::randomPassword(11,'a-z,A-Z,0-9');
508			}
509			$this->expire_time = $newExpireTime;
510			$this->delete_when_expired = $deleteWhenExpired;
511			$this->save();
512		}
513
514		if (!empty($this->expire_time) && !empty($this->random_code)) {
515			return \GO::url('files/file/download', array('id'=>$this->id,'inline'=>'false','random_code'=>$this->random_code), false, $html, false);
516		}
517	}
518
519
520	/**
521	 * The link to download the file.
522	 * This function does not check the file download expire time and the random code
523	 *
524	 * @return StringHelper
525	 */
526	public function getDownloadURL($downloadAttachment=true, $relative=false) {
527		return \GO::url('files/file/download', array('id'=>$this->id, 'inline'=>$downloadAttachment?'false':'true'), $relative);
528	}
529
530
531	public function getThumbURL($urlParams=array("lw"=>480, "ph"=>270, "zc"=>0)) {
532
533		$urlParams['filemtime']=$this->mtime;
534		$urlParams['src']=$this->path;
535
536		if($this->extension=='svg'){
537			return $this->getDownloadURL(false, true);
538		}else
539		{
540			return \GO::url('core/thumb', $urlParams);
541		}
542	}
543
544	/**
545	 * Move a file to another folder
546	 *
547	 * @param Folder $destinationFolder
548	 * @return boolean
549	 */
550	public function move($destinationFolder,$appendNumberToNameIfExists=false){
551
552		$this->folder_id=$destinationFolder->id;
553		if($appendNumberToNameIfExists)
554			$this->appendNumberToNameIfExists();
555		return $this->save();
556	}
557
558	/**
559	 * Just the let someone kow the file was opened
560	 */
561	public function open() {
562		$this->log('open');
563	}
564
565	/**
566	 * Adds some extra info to the loggin of files
567	 * @param StringHelper $action the action to log
568	 * @param boolean $save unused
569	 * @return boolean if save was successful
570	 */
571	protected function log($action, $save=true, $modifiedCustomfieldAttrs = false) {
572		$log = parent::log($action, false, $modifiedCustomfieldAttrs);
573		if(empty($log))
574			return false;
575
576		if($log === true) {
577			return true;
578		}
579
580		if($log->action=='update') {
581			$log->action = 'propedit';
582			if($log->object->isModified('folder_id'))
583				$log->action='moved';
584			if($log->object->isModified('name')) {
585				$log->action='renamed';
586				$log->message = $log->object->getOldAttributeValue('name') . ' > ' . $log->message;
587			}
588		}
589		return $save ? $log->save() : $log;
590	}
591
592	/**
593	 * Copy a file to another folder.
594	 *
595	 * @param Folder $destinationFolder
596	 * @param StringHelper $newFileName. Leave blank to use the same name.
597	 * @return File
598	 */
599	public function copy($destinationFolder, $newFileName=false, $appendNumberToNameIfExists=false){
600
601		$copy = $this->duplicate(array('folder_id'=>$destinationFolder->id), false, true);
602
603		if($newFileName)
604			$copy->name=$newFileName;
605
606		if($appendNumberToNameIfExists)
607			$copy->appendNumberToNameIfExists();
608
609		$this->fsFile->copy($copy->fsFile->parent(), $copy->name);
610
611		$copy->save(true);
612
613		return $copy;
614	}
615
616	/**
617	 * Import a filesystem file into the database.
618	 *
619	 * @param \GO\Base\Fs\File $fsFile
620	 * @return File
621	 */
622	public static function importFromFilesystem(\GO\Base\Fs\File $fsFile){
623
624		$folderPath = str_replace(\GO::config()->file_storage_path,"",$fsFile->parent()->path());
625
626		$folder = Folder::model()->findByPath($folderPath, true);
627		if(($file = $folder->hasFile($fsFile->name()))) {
628			return $file;
629		}
630		return $folder->addFile($fsFile->name());
631	}
632
633	/**
634	 * Replace filesystem file with given file.
635	 *
636	 * @param \GO\Base\Fs\File $fsFile
637	 */
638	public function replace(\GO\Base\Fs\File $fsFile, $isUploadedFile=false){
639
640		if($this->isLocked())
641			throw new \GO\Files\Exception\FileLocked();
642//		for safety allow replace action
643//		if(!File::checkQuota($fsFile->size()-$this->size))
644//			throw new \GO\Base\Exception\InsufficientDiskSpace();
645		if(!$this->isNew)
646			$this->log('edit');
647		$this->saveVersion();
648
649		if(!$fsFile->move($this->folder->fsFolder,$this->name, $isUploadedFile)){
650			return false;
651		}
652		$fsFile->setDefaultPermissions();
653
654		$old = $this->mtime;
655		$this->mtime = $this->fsFile->mtime();
656		$this->save();
657
658		$this->fireEvent('replace', array($this, $isUploadedFile));
659
660		return true;
661	}
662
663	public function putContents($data){
664//		for safety allow replace actions
665//		if(!File::checkQuota(strlen($data)))
666//			throw new \GO\Base\Exception\InsufficientDiskSpace();
667
668		$this->fsFile->putContents($data);
669		$old = $this->mtime;
670		$this->mtime = $this->fsFile->mtime();
671		$this->save();
672	}
673
674	/**
675	 * Copy current file to the versioning system.
676	 */
677	public function saveVersion(){
678
679		$this->version++;
680
681		$this->fireEvent('saveversion', array($this));
682
683		if(\GO::config()->max_file_versions > -1){
684			$version = new Version();
685			$version->file_id = $this->id;
686			$version->size_bytes = $this->size;
687			$version->save();
688		}
689	}
690
691	/**
692	 * Find the file model by relative path.
693	 *
694	 * @param StringHelper $relpath Relative path from \GO::config()->file_storage_path
695	 * @return File
696	 */
697	public function findByPath($relpath){
698		$folder = Folder::model()->findByPath(dirname($relpath),false,array());
699		if(!$folder)
700			return false;
701		else
702		{
703			return $folder->hasFile(\GO\Base\Fs\File::utf8Basename($relpath));
704		}
705	}
706
707	/**
708	 * Check if the file is an image.
709	 *
710	 * @return boolean
711	 */
712	public function isImage(){
713		switch(strtolower($this->extension)){
714			case 'ico':
715			case 'jpg':
716			case 'jpeg':
717			case 'png':
718			case 'gif':
719			case 'bmp':
720			case 'xmind':
721			case 'svg':
722
723				return true;
724			default:
725				return false;
726		}
727	}
728
729
730
731	/**
732	 * Checks if a filename exists and renames it.
733	 *
734	 * @param	StringHelper $filepath The complete path to the file
735	 * @access public
736	 * @return StringHelper  New filename
737	 */
738	public function appendNumberToNameIfExists()
739	{
740		$dir = $this->folder->path;
741		$origName = $this->fsFile->nameWithoutExtension();
742		$extension = $this->fsFile->extension();
743		$x=1;
744		$newName=$this->name;
745		while($this->folder->hasFile($newName))
746		{
747			$newName=$origName.' ('.$x.').'.$extension;
748			$x++;
749		}
750		$this->name=$newName;
751		return $this->name;
752	}
753
754	/**
755	 *
756	 * @param type $folder_id
757	 * @param type $type
758	 * @param type $arg1
759	 * @param type $arg2
760	 */
761	public function notifyUsers($folder_id, $type, $arg1, $arg2 = '') {
762		FolderNotification::model()->storeNotification($folder_id, $type, $arg1, $arg2);
763	}
764
765
766
767	public function findRecent($start=false,$limit=false){
768		$storeParams = \GO\Base\Db\FindParams::newInstance()->ignoreAcl();
769
770		$joinSearchCacheCriteria = \GO\Base\Db\FindCriteria::newInstance()
771					->addRawCondition('`t`.`id`', '`sc`.`entityId`')
772					->addCondition('entityTypeId', $this->modelTypeId(),'=','sc');
773
774		$storeParams->join(\GO\Base\Model\SearchCacheRecord::model()->tableName(), $joinSearchCacheCriteria, 'sc', 'INNER');
775
776
777		$aclJoinCriteria = \GO\Base\Db\FindCriteria::newInstance()
778							->addRawCondition('a.aclId', 'sc.aclId','=', false);
779
780		$aclWhereCriteria = \GO\Base\Db\FindCriteria::newInstance()
781						->addInCondition("groupId", \GO\Base\Model\User::getGroupIds(\GO::user()->id),"a", false);
782
783		$storeParams->join(\GO\Base\Model\AclUsersGroups::model()->tableName(), $aclJoinCriteria, 'a', 'INNER');
784
785		$storeParams->criteria(\GO\Base\Db\FindCriteria::newInstance()
786								->addModel(Folder::model())
787								->mergeWith($aclWhereCriteria));
788
789		$storeParams->group(array('t.id'))->order('mtime','DESC');
790
791		$storeParams->getCriteria()->addCondition('mtime', \GO\Base\Util\Date::date_add(\GO\Base\Util\Date::clear_time(time()),-7),'>');
792
793		if ($start!==false)
794			$storeParams->start($start);
795		if ($limit!==false)
796			$storeParams->limit($limit);
797
798		return $this->find($storeParams);
799	}
800
801	public function getHandlers(){
802		$handlers=array();
803		$classes = \GO\Files\FilesModule::getAllFileHandlers();
804		foreach($classes as $class){
805
806			$fileHandler = new $class;
807			if($fileHandler->fileIsSupported($this)){
808				$handlers[]= $fileHandler;
809			}
810		}
811
812		return $handlers;
813	}
814
815
816	public static $defaultHandlers;
817	/**
818	 *
819	 * @return \GO\Files\Filehandler\FilehandlerInterface
820	 */
821	public function getDefaultHandler(){
822
823		$ex = strtolower($this->extension);
824
825		if(!isset(self::$defaultHandlers[$ex])){
826			$fh = FileHandler::model()->findByPk(
827						array('extension'=>$ex, 'user_id'=>\GO::user()->id));
828
829			if($fh && class_exists($fh->cls)){
830				self::$defaultHandlers[$ex]=new $fh->cls;
831			}else{
832				$classes = \GO\Files\FilesModule::getAllFileHandlers();
833				foreach($classes as $class){
834
835//					$fileHandler = new $class->name;
836					$fileHandler = new $class();
837					if($fileHandler->isDefault($this)){
838						self::$defaultHandlers[$ex]= $fileHandler;
839						break;
840					}
841				}
842
843				if(!isset(self::$defaultHandlers[$ex]))
844					self::$defaultHandlers[$ex]=new \GO\Files\Filehandler\Download();
845			}
846		}
847
848		return self::$defaultHandlers[$ex];
849
850
851	}
852
853	/**
854	 * Returns this file as swift attachment
855	 *
856	 * @param string $altName
857	 * @return Swift_Attachment
858	 */
859	public function getAttachment($altName = null) {
860
861		$fullPath = $this->getFsFile()->path();
862
863		$attachment = \Swift_Attachment::fromPath($fullPath);
864
865		if($altName !== null){
866			$attachment->setFilename($altName);
867		}
868
869		return $attachment;
870	}
871
872	/**
873	 * @param GO\Base\Fs\File $outputFile
874	 * @param string $format
875	 * @throws NotFound
876	 */
877	public function convertTo(\GO\Base\Fs\File $outputFile, $format = 'pdf')
878	{
879		$converterModule = go()->getModule('business', 'fileconverter');
880		if (!$converterModule) {
881			throw new NotFound('Converter module is not available');
882		}
883
884		$service = \go\modules\business\fileconverter\Module::getAvailableService();
885		$service->convert($this->fsFile, $outputFile, $format);
886	}
887}
888