can('document','read')) return; $menuItems[] = array( 'sort'=>4, 'url'=>'index.php?module=document', 'label'=>'Documents', 'icon'=> 'far fa-copy', 'color'=> '#FFBA00' ); } //Cette fonction va generer une page quand on clique sur document dans menu function document_page(){ global $_; if(!isset($_['module']) || $_['module'] !='document') return; $page = !isset($_['page']) ? 'list' : $_['page']; $file = __DIR__.SLASH.'page.'.$page.'.php'; if(!file_exists($file)) throw new Exception("Page ".$page." inexistante"); require_once($file); } //Fonction executée lors de l'activation du plugin function document_install($id){ if($id != 'fr.core.document') return; Entity::install(__DIR__); if(!file_exists(Element::root())) mkdir(Element::root(),0755,true); if(!file_exists(__DIR__.SLASH.'logs')) mkdir(__DIR__.SLASH.'logs',0755,true); global $conf; $conf->put('document_allowed_extensions','csv,xls,xlsx,doc,docx,dotm,dotx,pdf,png,jpg,jpeg,gif,tif,svg,bmp,txt,zip,gzip,msg'); $conf->put('document_allowed_size',10485760); } //Fonction executée lors de la désactivation du plugin function document_uninstall($id){ if($id != 'fr.core.document') return; Entity::uninstall(__DIR__); } //Déclaration des sections de droits du plugin //Déclaration des sections de droits du plugin Right::register('document',array('label'=>'Gestion des droits sur le plugin document')); //Déclaration du menu de réglages function document_menu_setting(&$settingMenu){ global $myUser; if($myUser->can('document','configure')) { $settingMenu[]= array( 'sort' =>4, 'url' => 'setting.php?section=document', 'icon' => 'fas fa-angle-right', 'label' => 'Documents' ); } } //Déclaration des pages de réglages function document_content_setting(){ global $_; if(file_exists(__DIR__.SLASH.'setting.'.$_['section'].'.php')) require_once(__DIR__.SLASH.'setting.'.$_['section'].'.php'); } //Vérifie qu'un nom de fichier ou de dossier ne contient pas des caractères interdits par l'os (?:*|<>/\) function document_check_element_name($element){ preg_match('/[?:*|<>\/\\\\]/', $element, $match); return !empty($match) ? $match[0] : ''; } function document_dav_document($requested){ global $conf,$myUser; $requested = trim($requested, '/'); if(substr($requested, 0,13)!='dav/documents') return; if(empty($requested)) throw new Exception("Unspecified DAV path"); if($conf->get('document_enable_dav') != "1"){ header('HTTP/1.1 501 Method not implemented'); header('Content-Type: text/html; charset=utf-8'); throw new Exception('Mode Webdav désactivé, veuillez débloquer le Webdav dans les paramétrages pour accéder à cette fonctionnalité'); } require_once(__ROOT__.'common.php'); require_once(__DIR__.SLASH.'Element.class.php'); require_once(__DIR__.SLASH.'WebDav.class.php'); $projectPath = preg_replace('|https?\:\/\/'.$_SERVER['HTTP_HOST'].'|i', '', ROOT_URL); $server = new WebDav(); $server->logs = WebDav::logPath().SLASH.'dav-logs.txt'; $server->lockfile = WebDav::logPath().SLASH.'dav-lock.json'; $server->root = str_replace('//','/',$projectPath.'/dav/documents/'); $server->folder = Element::root(); //Windows cherche desktop.ini a chaque ouverture de dossier pour le custom $server->ignoreFiles[] = 'desktop.ini'; //Tortoise et autre client git d'explorer cherchent les .git a chaques ouverture $server->ignoreFiles[] = '.git'; if($conf->get('document_enable_dav_logs') == "1"){ $server->on['log'] = function($message){ Log::put(nl2br($message),'DAV'); }; } $server->do['login'] = function($login,$password){ global $myUser; if($myUser->login !=''){ return $myUser; } $myUser = User::check($login,$password); if(!$myUser) throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401); if(file_exists('enabled.maintenance') && $myUser->superadmin != 1) throw new Exception('Seul un compte Super Admin peut se connecter en mode maintenance',401); if(!$myUser->connected()) throw new Exception('Identifiant ou mot de passe incorrect',401); if(is_numeric($myUser->preference('default_firm')) && $myUser->haveFirm($myUser->preference('default_firm'))){ $_SESSION['firm'] = serialize(Firm::getById($myUser->preference('default_firm'))); if(!$_SESSION['firm']) throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401); }else if(count($myUser->firms)!=0){ $_SESSION['firm'] = serialize(reset($myUser->firms)); if(!$_SESSION['firm']) throw new Exception(" Problème lors de la connexion, veuillez contacter l'administrateur",401); }else{ throw new Exception('Ce compte n\'est actif sur aucune firm',401); } $myFirm = isset($_SESSION['firm']) ? unserialize($_SESSION['firm']) : new Firm(); if(!$myFirm) throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401); $_SESSION['currentUser'] = serialize($myUser); if(!$_SESSION['currentUser']) throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401); }; $server->do['delete'] = function($isoPath){ global $myUser,$conf; $utf8Path = utf8_encode($isoPath); Element::remove($utf8Path); if($conf->get('document_enable_logs')) Log::put('Suppression de '.$utf8Path,'document'); }; $server->do['download'] = function($isoPath){ global $myUser,$_,$conf; $utf8Path = utf8_encode($isoPath); //pour verfiier si le fichier existe, on récupere son chemin système avec le bon encodage $osPath = get_OS() === 'WIN' ? $isoPath: $utf8Path; if(!file_exists($osPath)) throw new Exception("Fichier inexistant",404); if(!is_file($osPath)) throw new Exception("Méthode non autorisée sur autre chose qu'un fichier",501); $stream = Element::download($utf8Path); if($conf->get('document_enable_logs_verbose')) Log::put('Téléchargement de '.$utf8Path,'document'); return $stream; }; $server->do['edit'] = function($isoPath,$body,$type){ global $myUser,$conf; User::check_access('document','edit'); $utf8Path = utf8_encode($isoPath); if($type=='file'){ $maxSize = $conf->get('document_allowed_size'); $extension = getExt($utf8Path); $extensions = explode(',',str_replace(' ', '', $conf->get('document_allowed_extensions'))); if(strlen($body) > $maxSize) throw new Exception("Taille du fichier trop grande, taille maximum :".readable_size($maxSize).' ('.$maxSize.' octets)',406); if(!in_array($extension , $extensions)) throw new Exception("Extension '".$extension."' du fichier ".$path." non permise, autorisé :".implode(', ',$extensions),415); $element = Element::addFile($utf8Path, $body); if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Enregistrement fichier '.$utf8Path,'document'); }else{ Element::addFolder($utf8Path); if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Enregistrement dossier '.$utf8Path,'document'); } $element = Element::fromPath($utf8Path); $baseElement = Element::load(array('path'=>$element->path)); if(is_object($baseElement) && $baseElement->id!=0) $element->id = $baseElement->id; $element->save(); }; $server->do['move'] = function($isoPath,$isoTo){ global $myUser,$conf; $utf8Path = utf8_encode($isoPath); $utf8To = utf8_encode($isoTo); User::check_access('document','edit'); $char = document_check_element_name(mt_basename($utf8To)); if(!empty($char)) throw new Exception("Caractères interdits : ".$char); Element::move($utf8Path,$utf8To); if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Déplacement de '.$utf8Path.' dans '.$utf8To,'document'); }; $server->do['copy'] = function($isoPath,$isoTo){ global $myUser,$conf; $utf8Path = utf8_encode($isoPath); $utf8To = utf8_encode($isoTo); User::check_access('document','edit'); $char = document_check_element_name(mt_basename($utf8To)); if(!empty($char)) throw new Exception("Caractères interdits : ".$char); Element::copy($utf8Path,$utf8To); if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Copie de '.$utf8Path.' dans '.$utf8To,'document'); }; $server->do['list'] = function($isoPath,$depth =0){ global $myUser,$conf; $utf8Path = utf8_encode($isoPath); User::check_access('document','read'); //pour verfier si le fichier existe, on récupere son chemin système avec le bon encodage $osPath = get_OS() === 'WIN' ? $isoPath: $utf8Path; if(!file_exists($osPath)) throw new Exception("Not found", 404); $files = array(); //Si la cible est la racine dav, on la retourne sous forme d'element fictif, sinon on utilise le systeme d'element standard if($utf8Path == Element::root()){ $file = new Element(); $file->label = ''; $file->type = 'directory'; $file->path = ''; $toScan = array($file); }else{ $toScan = array(Element::fromPath($utf8Path)); } if($conf->get('document_enable_logs_verbose')) Logs::put('visualisation de '.$utf8Path,'document'); //Si le dav demande un scan en profondeur du dossier, on scan les enfants du dossier ciblé if($depth>0) $toScan = array_merge($toScan,Element::browse($utf8Path.SLASH.'*')); foreach($toScan as $element){ //on convertis l'utf8 de l'element pour passer en iso webdav windows si le serveur est sous windows $path = Element::root().(get_OS() === 'WIN' ? utf8_decode( $element->path) : $element->path ); $file = array( 'type' => $element->type, 'label' => $element->label ); if($element->type == 'directory'){ $stat = stat($path); $file['create_time'] = $stat['ctime']; $file['update_time'] = $stat['mtime']; $file['length'] = $stat['size']; }else{ $file['create_time'] = filectime($path); $file['update_time'] = filemtime($path); $file['length'] = filesize($path); } $files[] = $file; } return $files; }; $server->start(); } //Déclaration des settings de base //Types possibles : text,select ( + "values"=> array('1'=>'Val 1'),password,checkbox. Un simple string définit une catégorie. Configuration::setting('document',array( "Général", 'document_allowed_extensions' => array("label"=>"Extensions autorisées","type"=>"text","legend"=>"(séparés par virgules)","placeholder"=>"csv,pdf,jpg,..."), 'document_allowed_size' => array("label"=>"Taille maximum autorisée","type"=>"text","legend"=>"(en octets)","placeholder"=>"28060000","type"=>"number"), 'document_enable_logs' => array("label"=>"Activer les logs standards","legend"=>"(ajout, supression, modifification d'un fichier ou dossier)","type"=>"boolean"), 'document_enable_logs_verbose' => array("label"=>"Activer les logs avancés","legend"=>"(lecture ou téléchargement d'un fichier ou dossier)","type"=>"boolean"), "Options DAV
Adresse WebDAV : ". ( substr(ROOT_URL,-1) == '/' ? substr(ROOT_URL,0,(strlen(ROOT_URL)-1)) : ROOT_URL ) ."/dav/documents
Les Identifiants sont identiques à ceux de l'erp
Si vous souhaitez vous connecter avec un lecteur réseau windows, vous devez installer un certificat https
", 'document_enable_dav' => array("label"=>"Activer le WebDav","type"=>"boolean"), 'document_enable_dav_logs' => array("label"=>"Activer les logs WebDav ","type"=>"boolean"), 'document_client_directory' => array("label"=>"Chemin vers le dossier document des clients","placeholder"=>"documents/Affaires/{{slug}}","type"=>"text"), )); function document_widget(&$widgets){ global $myUser; require_once(__DIR__.SLASH.'..'.SLASH.'dashboard'.SLASH.'DashboardWidget.class.php'); $modelWidget = new DashboardWidget(); $modelWidget->model = 'document'; $modelWidget->title = 'Documents'; $modelWidget->icon = 'fas fa-file'; $modelWidget->background = '#A3CB38'; $modelWidget->callback = 'init_components'; $modelWidget->load = 'action.php?action=document_widget_load'; if($myUser->can('document','configure')){ $modelWidget->configure = 'action.php?action=document_widget_configure'; $modelWidget->configure_callback = 'document_widget_configure_save'; } $modelWidget->js = [Plugin::url().'/js/widget.js?v='.time()]; $modelWidget->css = [Plugin::url().'/css/widget.css?v=1'.time()]; $modelWidget->description = "Affiche un espace document"; $widgets[] = $modelWidget; } function document_client_menu(&$clientMenu,$client){ global $myUser; if(!$myUser->can('document','read')) return; $menu = new MenuItem(); $menu->label = 'Documents'; $menu->url = '#tab=document'; $menu->slug = 'document'; $menu->sort = 10; $clientMenu[] = $menu; } function document_client_page($slug){ if($slug != 'document') return; require_once(__DIR__.SLASH.'tab.client.php'); } function document_client_merge($clientBase,$clientToMerge,&$logs){ global $_,$myUser,$conf; Plugin::need('client/Client'); require_once(__DIR__.SLASH.'Element.class.php'); $logs[] = ' Migration des documents attaches '; $clientDirectoryTemplate = empty($conf->get('document_client_directory')) ? 'Clients'.SLASH.'{{slug}}' : $conf->get('document_client_directory'); $toMergeRelativeDirectory = str_replace('{{slug}}',$clientToMerge->slug,$clientDirectoryTemplate); $toMergeDirectory = Element::root().$toMergeRelativeDirectory; if(!file_exists($toMergeDirectory)) return; $baseRelativeDirectory = str_replace('{{slug}}',$clientBase->slug,$clientDirectoryTemplate); $baseDirectory = Element::root().$baseRelativeDirectory; if(!file_exists($baseDirectory)) mkdir($baseDirectory,0755,true); File::merge($baseDirectory,$toMergeDirectory); } $api = new Api("document", "Api de gestion des documents"); $api->route('element/search','retourne une liste de documents','GET',function($request,&$response){ global $myUser,$_; $_ = $request['parameters']; if(!$myUser->connected()) throw new Exception("Credentials are missing",401); Action::run('document_element_search',$response); }); $api->route('element/upload','Upload un fichier dans la racine spécifiée','POST',function($request,&$response){ global $myUser,$_; if(isset($_SERVER['CONTENT_TYPE'])){ if(preg_match('/multipart\/(related|form); boundary=(.*)/is', $_SERVER['CONTENT_TYPE'],$match)){ $boundary = $match[2]; $parts = explode('--'.$boundary, $request['body']); $parts = array_filter($parts); $contents = array(); foreach ($parts as $part) { if(empty(trim($part))) continue; list($header,$body) = explode("\n\n",$part,2); $content = array( 'header' => array(), 'body'=>substr($body,0,-2) ); foreach(explode("\n",substr($header,1)) as $header){ $headerInfos = explode(':',$header); $content['header'][$headerInfos[0]] = $headerInfos[1]; } $contents[] = $content; } } } $i = 0; foreach($contents as $content){ //fichier if(isset($content['header']['Content-Disposition']) && strpos($content['header']['Content-Disposition'], 'filename="') !==-1){ preg_match('/filename="(.*)"/', $content['header']['Content-Disposition'],$matches); $_FILES['file']['name'][$i] = $matches[1]; $_FILES['file']['size'][$i] = strlen($content['body']); $tmpfname = tempnam(sys_get_temp_dir(), 'up_'); file_put_contents($tmpfname , $content['body']); $_FILES['file']['tmp_name'][$i] = $tmpfname; $i++; } if(strpos($content['header']['Content-Type'], 'application/json')!==false){ $_ = json_decode($content['body'],true); } } if(!isset($contents)){ $_ = json_decode($request['body'],true); } if(!$_) throw new Exception("Chaine json incorrecte ".$request['body']); if(!$myUser->connected()) throw new Exception("Credentials are missing",401); Action::run('document_element_upload',$response); }); $api->register(); //Cette fonction comprend toutes les actions du plugin qui ne nécessitent pas de vue html require_once(__DIR__.SLASH.'action.php'); function document_cron($time){ global $_; if(date('H:i', $time)!='03:00' && !isset($_['force-document'])) return; require_once(__DIR__.SLASH.'Element.class.php'); require_once(__DIR__.SLASH.'ElementRight.class.php'); $toDelete = array(); $i = 0; $ids = array(); foreach(Element::staticQuery('SELECT `id`,`path` FROM {{table}}')->fetchAll() as $row){ $osPath = Element::root().str_replace('/',SLASH, $row['path']); if(file_exists($osPath)) continue; echo $row['path'].PHP_EOL; $ids[] = $row['id']; if($i>500){ echo 'Delete pack'.PHP_EOL; Element::delete(array('id:IN'=>$ids)); ElementRight::delete(array('element:IN'=>$ids)); $ids = array(); $i = 0; } $i++; } } //Déclation des assets Plugin::addCss("/css/main.css"); Plugin::addCss("/css/document.api.css"); Plugin::addJs("/js/component.js",true); Plugin::addJs("/js/document.api.js"); Plugin::addJs("/js/main.js"); //Mapping hook / fonctions Plugin::addHook("widget", "document_widget"); Plugin::addHook("install", "document_install"); Plugin::addHook("uninstall", "document_uninstall"); Plugin::addHook("menu_main", "document_menu"); Plugin::addHook("page", "document_page"); Plugin::addHook("menu_setting", "document_menu_setting"); Plugin::addHook("content_setting", "document_content_setting"); Plugin::addHook("rewrite", "document_dav_document"); Plugin::addHook("cron", "document_cron"); Plugin::addHook("client_menu", "document_client_menu"); Plugin::addHook("client_page", "document_client_page"); Plugin::addHook("client_merge", "document_client_merge"); ?>