<?php
/**
 * Define a fileor directory element.
 * @author Valentin CARRUESCO
 * @category Plugin
 * @license copyright
 */
class Element extends Entity{
	public $id,$path,$label,$type,$extension,$size,$creatorSite,$icon,$thumbnail,$childNumber;
	protected $TABLE_NAME = 'document_element';
	public $fields =
	array(
		'id' => 'key',
		'path' => 'longstring',
		'label' => 'string',
		'type' => 'string',
		'extension' => 'string',
		'size' => 'string',
		'creatorSite' => 'int'
	);

	//Récupération de la racine de la GED
	public static function root(){
		return File::dir().'documents'.SLASH;
	}

	public  function save(){
		$this->path = str_replace('\\','/',$this->path);
		parent::save();
	}

	//Récuperation du chemin ou du flux de thumbnail associé à l'élement
	public function thumbnail(){
		
		if(!in_array($this->extension, array('jpg','png','jpeg','gif','bmp'))) return  $this->icon();

		if(!file_exists(self::root().'.thumbnails')) mkdir(self::root().'.thumbnails',755,true);

		
		
		$thumbname = str_replace(array('\\','./'),array('/',''),$this->path);
		$thumbnail = self::root().'.thumbnails'.SLASH.base64_encode($thumbname).'.'.$this->extension;
		
		if(!file_exists($thumbnail)){
			$osPath = File::convert_decoding($this->path);
			copy(self::root().$osPath,$thumbnail);
			Image::resize($thumbnail,200,200);
		}
		
		$path ='data:image/'.$this->extension.';base64,'.base64_encode(file_get_contents($thumbnail));
	
		return $path;
	}

	//Récuperation du chemin di'one à l'élement
	public function icon(){
		$path = 'plugin/document/img/file-types/';
		$icon = $this->type == 'directory' ? 'folder.svg' : 'document.svg';
		
		switch($this->extension){
			case 'docx':
			case 'doc':
			case 'dot':
			case 'dotx':
				$icon = 'word.svg';
			break;
			case 'xls':
			case 'xlsx':
			case 'csv':
				$icon = 'excel.svg';
			break;
			case 'ppt':
			case 'pptx':
				$icon = 'powerpoint.svg';
			break;
			case 'pdf':
				$icon = 'pdf.svg';
			break;
			case 'mp3':
			case 'wav':
			case 'mp4':
			case 'flac':
				$icon = 'music.svg';
			break;
			case 'html':
			case 'htm':
				$icon = 'url.svg';
			break;
			case 'msg':
				$icon = 'mail.svg';
			break;
			case 'psd':
				$icon = 'photoshop.svg';
			break;
			case 'rss':
				$icon = 'feed.svg';
			break;
			case 'mdb':
				$icon = 'access.svg';
			break;
			case 'bat':
			case 'sh':
			case 'cmd':
				$icon = 'gear.svg';
			break;
			case 'php':
			case 'js':
			case 'java':
			case 'class':
			case 'css':
			case 'c':
			case 'cpp':
			case 'py':
				$icon = 'code.svg';
			break;
			case 'jpg':
			case 'jpeg':
			case 'png':
			case 'bmp':
			case 'gif':
			case 'svg':
				$icon = 'image.svg';
			break;
		}

		$path .= $icon;
	
		return $path;
	}

	//Récuperation d'un element base depuis un chemin physique, ou d'un objet element rensiegné a partir du path si non existant en base (prend du utf8 en entré)
	public static function fromPath($path){
		global $conf;
		$element = new self();

		//convertion utf8 ISO-8859-1
		$osPath = File::convert_decoding($path);
		$infos = pathinfo($path);
		//Gestion label
		
		$element->label = mt_basename($path);
		
		$element->path = str_replace(self::root(),'',$path);
		$element->type = is_dir($osPath) ? 'directory' : 'file';
		$exists = file_exists($osPath);

		if($element->type == 'directory'){
			$path .= SLASH;
			$fi = new FilesystemIterator($osPath, FilesystemIterator::SKIP_DOTS);
			$element->childNumber = iterator_count($fi);
			$element->size =  0;
		} else { 
			if(isset($infos['extension'])) $element->extension = $infos['extension'];
			$element->size = !$exists ? 0 : filesize($osPath);
		}

		$element->updated =  !$exists ? 0 : filemtime($osPath);
		$element->created =  !$exists ? 0 : filectime($osPath);
		
		$relativePath = trim(str_replace(Element::root(),'',$path),SLASH);
		if ($baseElement = Element::load(array('path'=>str_replace('\\','/',$relativePath)   ))) {
			$element->creator = $baseElement->creator;
			$element->id = $baseElement->id;
		}
		$element->path = trim($element->path,SLASH);
		return $element;
	}
	
	//Récuperation de la liste des élements d'un repertoire (tiens compte des droits utilisateur) (attends de l'utf8)
	public static function browse($scanned,$folderOnly = false){
		global $myUser,$myFirm;
		require_once(__DIR__.SLASH.'ElementRight.class.php');
	
		$osScanned = File::convert_decoding($scanned);
		$elements = array();
		User::check_access('document','read');

		$toCheck = array();

		//Récuperation du tableau des rang utilisateur sous forme d'id.
		$ranks = array();
		if(!$myUser->superadmin){
			foreach($myUser->ranks[$myFirm->id] as $rank)	
				$ranks[] = $rank->id;
		}

		$existingPathes = array();
	
		//Pour chaque fichier physique existant
		foreach(glob($osScanned) as $osPath){
			$osPath = str_replace(SLASH.'.'.SLASH, SLASH, $osPath);
			$basePath = File::convert_encoding($osPath);
			if((substr(mt_basename($osPath), 0,1)=='.') || ($folderOnly && !is_dir($osPath))) continue;

			
			//Récuperation/création de l'element objet correspondant (frompath n'accepte que l'utf8, on lui donne le basepath)
			$line = Element::fromPath($basePath);

			$existingPathes[] = $line->path;

			//Si l'element correspondant n'existe pas en base, on le créé
			if($line->id == ''){
				$line->creator = 'anonymous';
				$line->save();
			}

			//Si l'utilisateur n'est pas le créateur, pas un super admin et n'a pas le droit configure sur les documents on check les partages de droits
			if($myUser->login != $line->creator && !$myUser->can('document','configure')  && !$myUser->superadmin){
				$can = false;
				$pathes = array();
				$path = '';

				//Récuperation des chemins dont on doit récuperer les droits :
				// ex : pour le fichier a/b/c les chemins sont a, a/b, et a/b/c
				foreach(explode(SLASH,$line->path) as $i=>$pathCrumb){
					if($i!=0) $path.= SLASH;
					$path.= $pathCrumb;
					$pathes[] = str_replace("\\","\\\\",$path);
				}
				//On récupere les droits pour chaques chemin
				foreach(ElementRight::staticQuery('SELECT er.*,el.path as '.Element::tableName().'_join_path  FROM {{table}} er LEFT JOIN '.Element::tableName().' el ON el.id=er.element WHERE 
					( er.entity="all" OR (er.entity="user" AND er.uid=?) OR (er.entity="rank" AND er.uid IN ('.implode(',',$ranks).')  ) ) 
					AND element IN(SELECT id from '.Element::tableName().' WHERE path IN ("'.implode('","',$pathes).'")) ',array($myUser->login),true,1) as $right){

					$element = $right->join('element');
					//Si les droits sont sur un dossier parent et son recursif OU si les droits sont sur le fichier concerné on continue la verification
					if($right->element == $line->id || ($right->recursive && strpos($line->path, $element->path)!==false ) )
						$can = true;
				}
				if(!$can) continue;
			}
			$elements[] = $line;
		}


		//Element::staticQuery('SELECT * FROM {{table}} WHERE `path` NOT IN ("'.implode('","',$existingPathes).'") ');



		return $elements;
	}

	//Téléchargement d'un fichier ou d'un repertoire (prends de l'utf 8 en entrée)
	public static function download($path){
		$osPath = File::convert_decoding($path);

		if(!file_exists($osPath)) return '';
		$element = Element::fromPath($path);

		if(!self::hasRight($element,'read')) throw new Exception("Permissions insuffisantes",403);

		if(is_dir($osPath)){
			$filename = rand().'-'.time().'.zip';
			$filepath = sys_get_temp_dir().SLASH.$filename;
			$zip = new ZipArchive;

			$res = $zip->open($filepath, ZipArchive::CREATE);
			if ($res === TRUE) {
				$ressources = glob_recursive($osPath.SLASH.'*') ;
				foreach($ressources as $i=>$resource){
					if(is_dir($resource)) continue;
					$filename = $resource;
					$filename = File::convert_encoding($filename);
					$filename =  str_replace($path,mt_basename($path),$filename);
					$filename=iconv("UTF-8", "IBM850", $filename);
					if(!$zip->addFromString($filename, file_get_contents($resource))) throw new Exception("Impossible de compresser le fichier ".$path);	
				}

				if(count($ressources)==0)
					$zip->addEmptyDir('.');

				$zip->close();
			}
			$stream = file_get_contents($filepath);
			unlink($filepath); 
		}else{
			$stream = file_get_contents($osPath);
		}
		return $stream;
	}

	//Supression d'un répértoire ou d'un fichier en base et en physique (prends de l'utf 8 en entrée)
	public static function remove($path){
		global $myUser,$myFirm;
		require_once(__DIR__.SLASH.'ElementRight.class.php');
		$osPath = File::convert_decoding($path);
		if(!file_exists($osPath)) return;
		$element = Element::fromPath($path);
		$dbPath = str_replace('\\','/',$element->path);
		if(!self::hasRight($element,'edit')) throw new Exception("Permissions insuffisantes",403);
	
		if(is_dir($osPath)) {
			self::remove_dir_recursive($osPath);
			Element::staticQuery('DELETE FROM {{table}} WHERE `path` = ? OR `path` LIKE ?',array($dbPath,$dbPath.'/%'));
		} else {
			unlink($osPath);
			Element::delete(array('path'=>$dbPath));
		}
		ElementRight::delete(array('element'=>$element->id));
	}

	//Copie d'un répértoire ou d'un fichier en base et en physique (prends de l'utf 8 en entrée)
	public static function copy($path,$to){
		global $conf, $myUser;

		$osPath = File::convert_decoding($path);
		$osTo = File::convert_decoding($to);

		if(!file_exists($osPath)) return;
		$element = Element::fromPath($path);
		if(file_exists($osTo)) return false;

		$parentPathTo = dirname($to);
		$parentOsPathTo = dirname($osTo);
		if(!file_exists($parentOsPathTo))  throw new Exception("Dossier de destination inexistant",404);
		$parentTo = Element::fromPath($parentPathTo);
		if(!self::hasRight($parentTo,'read')) throw new Exception("Permissions insuffisantes",403);
		if(!self::hasRight($element,'read')) throw new Exception("Permissions insuffisantes",403);

		$dbPath = str_replace('\\','/',$element->path);

		if($element->type == 'directory'){
			$element->path = $element->path;
			//Si c'est un dossier, on récupere tous les élements commendant par ce nom afin de gerer leurs chemins
			$baseElement = Element::staticQuery('SELECT * FROM {{table}} WHERE `path` = ? OR `path` LIKE ?',array($dbPath,$dbPath.'/%'), true);
		} else {
			$baseElement = Element::load(array('path'=>$dbPath));
			if(is_object($baseElement) && $baseElement->id != 0) $element->id = $baseElement->id;
		}
		
		if(!copy($osPath,$osTo)) throw new Exception('Erreur lors de la copie...');
		$newelement = Element::fromPath($to);

		if(is_array($baseElement)){
			foreach ($baseElement as $elem) {
				if($elem->path == $dbPath) $elem->label = $element->label;
				$elem->path = str_replace($dbPath, $newelement->path, $elem->path);
				$elem->id = 0;
				$elem->save();
			}
		} else {
			$baseElement->path = $newelement->path;
			$baseElement->label = $newelement->label;
			$baseElement->id = 0;
			$baseElement->save();
		}
	}

	//Déplacement d'un répertoire ou d'un fichier en base et en physique
	public static function move($path,$to, $label=''){
		global $conf, $myUser;

		$osPath = File::convert_decoding($path);
		$osTo = File::convert_decoding($to);

		if(!file_exists($osPath)) return;
		
		$element = Element::fromPath($path);
		
		
		//Si le dossier se termine par un ., on supprime ce . (non pris en charge par l'os)
		if($element->type=="directory" && substr($to, -1,1) =='.')
			$to = substr($to, 0,strlen($to)-1);
		
		
		if($to == $path) return $element;

		if(!self::hasRight($element,'edit')) throw new Exception("Permissions insuffisantes",403);

		$parentPathTo = dirname($to);
		$parentPathOsTo = dirname($osTo);
		if(!file_exists($parentPathOsTo))  throw new Exception("Dossier de destination inexistant",404);
		$parentTo = Element::fromPath($parentPathTo);
		if(!self::hasRight($parentTo,'read')) throw new Exception("Permissions insuffisantes",403);
	

		$dbPath = str_replace('\\','/',$element->path);
			
			if(file_exists($osTo)) return false;

			if($element->type == 'directory'){
				
				$element->path = $oldPath = $element->path;
				
				//Si c'est un dossier, on récupere tous les élements commencant par ce nom afin de modifier leurs chemins
				$baseElement = Element::staticQuery('SELECT * FROM {{table}} WHERE `path` = ? OR `path` LIKE ?',array($dbPath,$dbPath.'/%'), true);
			} else {
				$baseElement = Element::load(array('path'=>$dbPath));
				if(is_object($baseElement) && $baseElement->id != 0) $element->id = $baseElement->id;
			}
			try {
				if(!rename($osPath,$osTo))
					throw new Exception('Un élément du même nom existe déjà.');
			} catch (Exception $e) {
				return false;
			}
			$toElement = Element::fromPath($to);
			//Si type est image, on supprime l'ancienne miniature
			if(in_array($element->extension, array('jpg','png','jpeg','gif','bmp')) && file_exists(self::root().'.thumbnails'.SLASH.base64_encode($element->path).'.'.$element->extension))
				unlink(self::root().'.thumbnails'.SLASH.base64_encode($element->path).'.'.$element->extension);

			if(is_array($baseElement)){
				foreach ($baseElement as $elem) {
					if($elem->path == $oldPath)
						$elem->label = $toElement->label;

					$elem->path = str_replace($dbPath, $toElement->path, $elem->path);
					$elem->save();
				}
			} else {
				$baseElement->path = $toElement->path;
				$baseElement->label = $toElement->label;
				$baseElement->save();
			}
		

		return $toElement;
	}

	//Ajout d'un fichier en base et en physique (prend de l'utf8 en entré pour le path)
	public static function addFile($path,$stream){
		//on enregistre le fichier dans l'encodage du système
		$osPath = File::convert_decoding($path);
		$parentPath = dirname($path);
		$parentOsPath = File::convert_decoding($parentPath);
		if(!file_exists($parentOsPath))  throw new Exception("Dossier de destination inexistant",404);
		$parent = Element::fromPath($parentPath);
		if(!self::hasRight($parent,'read')) throw new Exception("Permissions insuffisantes",403);

		file_put_contents($osPath, $stream);

		$element = Element::fromPath($path, array('preview'=>false));
		
		$element->save();
		return $element;
	}

	//Ajout d'un dossier en base et en physique  (prend de l'utf8 en entré pour le path)
	public static function addFolder($path, $recursive = false, $checkRight=true, $creator = null){
		$osPath = File::convert_decoding($path);
		$parentPath = dirname($path);
		$osParentPath = dirname($osPath);
		if(!file_exists($osParentPath)) {
			if(!$recursive){
				throw new Exception("Dossier parent ".$parentPath."  inexistant",404);
			} else {
				self::addFolder($parentPath, $recursive, $checkRight, $creator);
			}
		}
		$parent = Element::fromPath($parentPath);

		if($checkRight && !self::hasRight($parent,'read')) throw new Exception("Permissions insuffisantes",403);

		if(file_exists($osPath)) return;
		mkdir($osPath,0755,$recursive);
		$element = Element::fromPath($path);
		if(isset($creator)) $element->creator = $creator;
		$element->save();
		return $element;
	}

	public static function hasRight($element,$type){
		global $myUser,$myFirm;
		require_once(__DIR__.SLASH.'ElementRight.class.php');
		$documentRight = '';
		switch($type){
			case 'read':
				$documentRight = 'read';
			break;
			case 'delete':
				$documentRight = 'edit';
			break;
			case 'edit':
				$documentRight = 'edit';
			break;
		}

		if(!$myUser->can('document',$type)) return false;
		if($myUser->login == $element->creator || $myUser->can('document','configure') || $myUser->superadmin == 1) return true;

		$allPathes = array();
		$rootPath = '';
		foreach (explode(SLASH,$element->path) as $i=>$crumb) {
			$rootPath .= ($i==0?'':SLASH).$crumb;
			$allPathes[] = str_replace("\\","\\\\",$rootPath);
		}

		$userRanks = array();
		foreach ($myUser->ranks[$myFirm->id] as $rank) 
			$userRanks[] = $rank->id;
		
		$query = 'SELECT COUNT(id) allowed FROM {{table}} WHERE (element IN(SELECT id from '.Element::tableName().' WHERE path IN("'.implode('","',$allPathes).'")) AND `'.$documentRight.'`=1) AND (entity="all" OR (entity="user" AND uid=?) ';
		
		if(count($userRanks)!=0) $query .= ' OR (entity="rank" AND uid IN('.implode(',',$userRanks).')) ';
		
		$query .= ')';
		
		$result = ElementRight::staticQuery($query,array($myUser->login));
		$result = $result->fetch();
		if(!$result || $result['allowed']<=0) return false;
		return true;
	}

	public static function remove_dir_recursive($path) {
		foreach(glob($path.SLASH.'*') as $element){
			if(mt_basename($element) == '.' || mt_basename($element) == '..') continue;
			if(is_dir($element)) {
				self::remove_dir_recursive($element);
			} else {
				unlink($element);
			}
		}
		rmdir($path);
	}

	

}
?>