From e1a5a90f0a72bcdd5562d76978460043d16a6b00 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 13 Dec 2016 00:15:21 +0100 Subject: [PATCH] NEW Uploaded files are indexed into database --- htdocs/core/class/html.formfile.class.php | 79 +++++++++++---- htdocs/core/lib/files.lib.php | 98 +++++++++++++++++-- .../tpl/document_actions_post_headers.tpl.php | 5 +- htdocs/ecm/class/ecmfiles.class.php | 17 +++- .../install/mysql/migration/5.0.0-6.0.0.sql | 1 + .../mysql/tables/llx_ecm_files.key.sql | 4 +- htdocs/install/mysql/tables/llx_ecm_files.sql | 6 +- 7 files changed, 173 insertions(+), 37 deletions(-) diff --git a/htdocs/core/class/html.formfile.class.php b/htdocs/core/class/html.formfile.class.php index 60ed409fc7f..b33ea772aa8 100644 --- a/htdocs/core/class/html.formfile.class.php +++ b/htdocs/core/class/html.formfile.class.php @@ -863,7 +863,7 @@ class FormFile * @param string $modulepart Value for modulepart used by download or viewimage wrapper * @param string $param Parameters on sort links (param must start with &, example &aaa=bbb&ccc=ddd) * @param int $forcedownload Force to open dialog box "Save As" when clicking on file - * @param string $relativepath Relative path of docs (autodefined if not provided) + * @param string $relativepath Relative path of docs (autodefined if not provided), relative to module. * @param int $permonobject Permission on object (so permission to delete or crop document) * @param int $useinecm Change output for use in ecm module * @param string $textifempty Text to show if filearray is empty ('NoFileFound' if not defined) @@ -871,23 +871,40 @@ class FormFile * @param string $title Title before list * @param string $url Full url to use for click links ('' = autodetect) * @param int $showrelpart 0=Show only filename (default), 1=Show first level 1 dir - * @param int $permtoeditline Permission to edit document line (-1 is deprecated) + * @param int $permtoeditline Permission to edit document line (You msut provide a value, -1 is deprecated and must not be used any more) * @return int <0 if KO, nb of files shown if OK + * @return string $upload_dir Full path directory so we can know dir relative to MAIN_DATA_ROOT. */ - function list_of_documents($filearray,$object,$modulepart,$param='',$forcedownload=0,$relativepath='',$permonobject=1,$useinecm=0,$textifempty='',$maxlength=0,$title='',$url='', $showrelpart=0, $permtoeditline=-1) + function list_of_documents($filearray,$object,$modulepart,$param='',$forcedownload=0,$relativepath='',$permonobject=1,$useinecm=0,$textifempty='',$maxlength=0,$title='',$url='', $showrelpart=0, $permtoeditline=-1,$upload_dir='') { global $user, $conf, $langs, $hookmanager; global $bc; global $sortfield, $sortorder, $maxheightmini; + // Define relative path used to store the file + if (empty($relativepath)) + { + $relativepath=(! empty($object->ref)?dol_sanitizeFileName($object->ref):'').'/'; + if ($object->element == 'invoice_supplier') $relativepath=get_exdir($object->id,2,0,0,$object,'invoice_supplier').$relativepath; // TODO Call using a defined value for $relativepath + if ($object->element == 'project_task') $relativepath='Call_not_supported_._Call_function_using_a_defined_relative_path_.'; + } + // For backward compatiblity, we detect file is stored into an old path + if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO) && $file['level1name'] == 'photos') + { + $relativepath=preg_replace('/^.*\/produit\//','',$file['path']).'/'; + } + // Defined relative dir to DOL_DATA_ROOT + $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir); + $rel_dir = preg_replace('/^[\\/]/','',$rel_dir); + $hookmanager->initHooks(array('formfile')); - $parameters=array( 'filearray' => $filearray, 'modulepart'=> $modulepart, 'param' => $param, 'forcedownload' => $forcedownload, - 'relativepath' => $relativepath, + 'relativepath' => $relativepath, // relative filename to module dir + 'reldir' => $rel_dir, // relative dirname to DOL_DATA_ROOT 'permtodelete' => $permonobject, 'useinecm' => $useinecm, 'textifempty' => $textifempty, @@ -942,10 +959,42 @@ class FormFile print_liste_field_titre(''); print "\n"; + // Get list of files stored into database for same directory + $filearrayindatabase = dol_dir_list_in_database($rel_dir, '', null, 'name', SORT_ASC); + + //var_dump($filearray); + //var_dump($filearrayindatabase); + + // Complete filearray with properties found into $filearrayindatabase + foreach($filearray as $key => $val) + { + $found=0; + // Search if it exists into $filearrayindatabase + foreach($filearrayindatabase as $key2 => $val2) + { + if ($filearrayindatabase[$key2]['name'] == $filearray[$key]['name']) + { + $filearray[$key]['position']=$filearrayindatabase[$key2]['position']; + $filearray[$key]['cover']=$filearrayindatabase[$key2]['cover']; + $filearray[$key]['acl']=$filearrayindatabase[$key2]['acl']; + $found=1; + break; + } + } + if (! $found) + { + $filearray[$key]['position']=999999; // File not indexed are at end. So if we add a file, it will not replace existing in position + $filearray[$key]['cover']=0; + $filearray[$key]['acl']=''; + } + } + + $filearray=dol_sort_array($filearray, 'position'); + //var_dump($filearray); + $nboffiles=count($filearray); - if ($nboffiles > 0) include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; - + $var=true; foreach($filearray as $key => $file) // filearray must be only files here { @@ -953,23 +1002,13 @@ class FormFile && $file['name'] != '..' && ! preg_match('/\.meta$/i',$file['name'])) { - // Define relative path used to store the file - if (empty($relativepath)) - { - $relativepath=(! empty($object->ref)?dol_sanitizeFileName($object->ref):'').'/'; - if ($object->element == 'invoice_supplier') $relativepath=get_exdir($object->id,2,0,0,$object,'invoice_supplier').$relativepath; // TODO Call using a defined value for $relativepath - if ($object->element == 'project_task') $relativepath='Call_not_supported_._Call_function_using_a_defined_relative_path_.'; - } - // For backward compatiblity, we detect file is stored into an old path - if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO) && $file['level1name'] == 'photos') - { - $relativepath=preg_replace('/^.*\/produit\//','',$file['path']).'/'; - } $var=!$var; $editline=0; - print ''."\n"; + print ''."\n"; + // Do we have entry into database ? + print ''."\n"; print ''; print ''; diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index cc22f44ec48..059448b35dc 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -41,7 +41,7 @@ function dol_basename($pathfile) * Scan a directory and return a list of files/directories. * Content for string is UTF8 and dir separator is "/". * - * @param string $path Starting path from which to search + * @param string $path Starting path from which to search. This is a full path. * @param string $types Can be "directories", "files", or "all" * @param int $recursive Determines whether subdirectories are searched * @param string $filter Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function @@ -50,7 +50,8 @@ function dol_basename($pathfile) * @param string $sortorder Sort order (SORT_ASC, SORT_DESC) * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only * @param int $nohook Disable all hooks - * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','date'=>'yyy','size'=>99,'type'=>'dir|file') + * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','date'=>'yyy','size'=>99,'type'=>'dir|file',...) + * @see dol_dir_list_indatabase */ function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter="", $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=false) { @@ -205,6 +206,82 @@ function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefil } +/** + * Scan a directory and return a list of files/directories. + * Content for string is UTF8 and dir separator is "/". + * + * @param string $path Starting path from which to search + * @param string $filter Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function + * @param array|null $excludefilter Array of Regex for exclude filter (example: array('(\.meta|_preview\.png)$','^\.')) + * @param string $sortcriteria Sort criteria ("","fullname","name","date","size") + * @param string $sortorder Sort order (SORT_ASC, SORT_DESC) + * @param int $mode 0=Return array minimum keys loaded (faster), 1=Force all keys like description + * @return array Array of array('name'=>'xxx','fullname'=>'/abc/xxx','type'=>'dir|file',...) + * @see dol_dir_list + */ +function dol_dir_list_in_database($path, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0) +{ + global $conf, $db; + + $sql=" SELECT label, entity, filename, filepath, fullpath_orig, keywords, cover, gen_or_uploaded, extraparams, date_c, date_m, fk_user_c, fk_user_m, acl, position"; + if ($mode) $sql.=", description"; + $sql.=" FROM ".MAIN_DB_PREFIX."ecm_files"; + $sql.=" WHERE filepath = '".$db->escape($path)."'"; + $sql.=" AND entity = ".$conf->entity; + + $resql = $db->query($sql); + if ($resql) + { + $file_list=array(); + $num = $db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $db->fetch_object($resql); + if ($obj) + { + preg_match('/([^\/]+)\/[^\/]+$/',DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename,$reg); + $level1name=(isset($reg[1])?$reg[1]:''); + $file_list[] = array( + "name" => $obj->filename, + "path" => DOL_DATA_ROOT.'/'.$obj->filepath, + "level1name" => $level1name, + "fullname" => DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename, + "fullpath_orig" => $obj->fullpath_orig, + "date_c" => $db->jdate($obj->date_c), + "date_m" => $db->jdate($obj->date_m), + "type" => 'file', + "keywords" => $obj->keywords, + "cover" => $obj->cover, + "position" => (int) $obj->position, + "acl" => $obj->acl + ); + } + $i++; + } + + // Obtain a list of columns + if (! empty($sortcriteria)) + { + $myarray=array(); + foreach ($file_list as $key => $row) + { + $myarray[$key] = (isset($row[$sortcriteria])?$row[$sortcriteria]:''); + } + // Sort the data + if ($sortorder) array_multisort($myarray, $sortorder, $file_list); + } + + return $file_list; + } + else + { + dol_print_error($db); + return array(); + } +} + + /** * Fast compare of 2 files identified by their properties ->name, ->date and ->size * @@ -751,7 +828,7 @@ function dol_delete_file($file,$disableglob=0,$nophperrors=0,$nohook=0,$object=n $rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $filename); if (! preg_match('/\/temp\//', $rel_filetodelete)) // If not a tmp file { - $rel_filetodelete = preg_replace('/^\//', '', $rel_filetodelete); + $rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete); dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG); include DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; @@ -1083,10 +1160,10 @@ function dol_add_file_process($upload_dir, $allowoverwrite=0, $donotupdatesessio else // Update table of files { $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir); - if (! preg_match('/\/temp\//', $rel_dir)) // If not a tmp file + if (! preg_match('/[\\/]temp[\\/]/', $rel_dir)) // If not a tmp file { - $rel_dir = preg_replace('/\/$/', '', $rel_dir); - $rel_dir = preg_replace('/^\//', '', $rel_dir); + $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); + $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); include DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; $ecmfile=new EcmFiles($db); @@ -1384,11 +1461,11 @@ function dol_most_recent_file($dir,$regexfilter='',$excludefilter=array('(\.meta * Security check when accessing to a document (used by document.php, viewimage.php and webservices) * * @param string $modulepart Module of document ('module', 'module_user_temp', 'module_user' or 'module_temp') - * @param string $original_file Relative path with filename + * @param string $original_file Relative path with filename, relative to modulepart. * @param string $entity Restrict onto entity * @param User $fuser User object (forced) * @param string $refname Ref of object to check permission for external users (autodetect if not provided) - * @return mixed Array with access information : accessallowed & sqlprotectagainstexternals & original_file (as full path name) + * @return mixed Array with access information : 'accessallowed' & 'sqlprotectagainstexternals' & 'original_file' (as a full path name) */ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fuser='',$refname='') { @@ -1682,7 +1759,7 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu $accessallowed=1; } $original_file=$conf->projet->dir_output.'/'.$original_file; - $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; + $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project', 1).")"; } else if ($modulepart == 'project_task') { @@ -1691,7 +1768,7 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu $accessallowed=1; } $original_file=$conf->projet->dir_output.'/'.$original_file; - $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity; + $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project', 1).")"; } // Wrapping for interventions else if ($modulepart == 'fichinter') @@ -1806,6 +1883,7 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu $accessallowed=1; } $original_file=$conf->contrat->dir_output.'/'.$original_file; + $sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract', 1).")"; } // Wrapping pour les dons diff --git a/htdocs/core/tpl/document_actions_post_headers.tpl.php b/htdocs/core/tpl/document_actions_post_headers.tpl.php index 83d38793072..546cec91142 100644 --- a/htdocs/core/tpl/document_actions_post_headers.tpl.php +++ b/htdocs/core/tpl/document_actions_post_headers.tpl.php @@ -79,7 +79,7 @@ $formfile->list_of_documents( $modulepart, $param, 0, - $relativepathwithnofile, // relative path with no file. For example "moduledir/0/1" + $relativepathwithnofile, // relative path with no file. For example "0/1" $permission, 0, '', @@ -87,7 +87,8 @@ $formfile->list_of_documents( '', '', 0, - $permtoedit + $permtoedit, + $upload_dir ); print "
"; diff --git a/htdocs/ecm/class/ecmfiles.class.php b/htdocs/ecm/class/ecmfiles.class.php index 0133bff2580..0e1885fa62f 100644 --- a/htdocs/ecm/class/ecmfiles.class.php +++ b/htdocs/ecm/class/ecmfiles.class.php @@ -136,6 +136,21 @@ class EcmFiles //extends CommonObject } if (empty($this->date_c)) $this->date_c = dol_now(); + $maxposition=0; + if (empty($this->position)) // Get max used + { + $sql = "SELECT MAX(position) as maxposition FROM " . MAIN_DB_PREFIX . $this->table_element; + $sql.= " WHERE filepath ='".$this->filepath."'"; + + $resql = $this->db->query($sql); + if ($resql) + { + $obj = $this->db->fetch_object($resql); + $maxposition = (int) $obj->maxposition; + } + else dol_print_error($this->db); + } + $maxposition=$maxposition+1; // Check parameters // Put here code to add control on parameters values @@ -167,7 +182,7 @@ class EcmFiles //extends CommonObject $sql .= ' '.(! isset($this->description)?'NULL':"'".$this->db->escape($this->description)."'").','; $sql .= ' '.(! isset($this->keywords)?'NULL':"'".$this->db->escape($this->keywords)."'").','; $sql .= ' '.(! isset($this->cover)?'NULL':"'".$this->db->escape($this->cover)."'").','; - $sql .= ' '.(! isset($this->position)?'0':$this->db->escape($this->position)).','; + $sql .= ' '.$maxposition.','; $sql .= ' '.(! isset($this->gen_or_uploaded)?'NULL':"'".$this->db->escape($this->gen_or_uploaded)."'").','; $sql .= ' '.(! isset($this->extraparams)?'NULL':"'".$this->db->escape($this->extraparams)."'").','; $sql .= ' '."'".$this->db->idate($this->date_c)."'".','; diff --git a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql index 9c6aefdec6d..73d209b0cdc 100644 --- a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql +++ b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql @@ -27,3 +27,4 @@ ALTER TABLE llx_ecm_files CHANGE COLUMN fullpath filepath varchar(750); ALTER TABLE llx_ecm_files ADD COLUMN position integer; +ALTER TABLE llx_ecm_files CHANGE COLUMN keyword keyword varchar(750); diff --git a/htdocs/install/mysql/tables/llx_ecm_files.key.sql b/htdocs/install/mysql/tables/llx_ecm_files.key.sql index 9b1f4fcf164..2b8b1348ba3 100644 --- a/htdocs/install/mysql/tables/llx_ecm_files.key.sql +++ b/htdocs/install/mysql/tables/llx_ecm_files.key.sql @@ -17,5 +17,7 @@ -- ============================================================================ -ALTER TABLE llx_ecm_files ADD UNIQUE INDEX uk_ecm_files (label, entity); +ALTER TABLE llx_ecm_files ADD UNIQUE INDEX uk_ecm_files (label, entity); -- label is a md5 --ALTER TABLE llx_ecm_files ADD UNIQUE INDEX uk_ecm_files_fullpath(fullpath); Disabled, mysql limits size of index + + diff --git a/htdocs/install/mysql/tables/llx_ecm_files.sql b/htdocs/install/mysql/tables/llx_ecm_files.sql index 7a30066c2b2..45157cdf959 100644 --- a/htdocs/install/mysql/tables/llx_ecm_files.sql +++ b/htdocs/install/mysql/tables/llx_ecm_files.sql @@ -19,13 +19,13 @@ CREATE TABLE llx_ecm_files ( rowid integer AUTO_INCREMENT PRIMARY KEY, - label varchar(64) NOT NULL, + label varchar(64) NOT NULL, -- label contains a md5 entity integer DEFAULT 1 NOT NULL, -- multi company id - filepath varchar(750) NOT NULL, -- relative to dolibarr document dir. example module/def + filepath varchar(750) NOT NULL, -- relative to dolibarr document dir. Example module/def filename varchar(255) NOT NULL, -- file name only without any directory fullpath_orig varchar(750), -- full path of original filename, when file is uploaded from a local computer description text, - keywords text, -- list of keywords, separated with comma + keywords varchar(750), -- list of keywords, separated with comma. Must be limited to most important keywords. cover text, -- is this file a file to use for a cover position integer, -- position of file among others gen_or_uploaded varchar(12), -- 'generated' or 'uploaded'