Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into

develop
This commit is contained in:
Laurent Destailleur 2026-08-17 04:49:53 +02:00
commit 2fdfd822b3
10 changed files with 221 additions and 45 deletions

View file

@ -1,6 +1,6 @@
<?php
/* Copyright (C) 2007-2026 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -503,8 +503,12 @@ function treeOutputForAbsoluteDir($sqltree, $selecteddir, $fullpathselecteddir,
// Edit link
print '<!-- edit link -->';
print '<td class="right" width="18"><a class="editfielda" href="';
print DOL_URL_ROOT.'/ecm/dir_card.php?module='.urlencode($modulepart).'&section='.urlencode($val['id']).'&relativedir='.urlencode($val['fullrelativename']);
print '&backtopage='.urlencode($_SERVER["PHP_SELF"].'?file_manager=1&website='.$websitekey.'&pageid='.$pageid);
print dolBuildUrl(DOL_URL_ROOT.'/ecm/dir_card.php', array(
'module' => $modulepart,
'section' => $val['id'],
'relativedir' => $val['fullrelativename'],
'backtopage' => $_SERVER["PHP_SELF"].'?file_manager=1&website='.$websitekey.'&pageid='.$pageid,
));
print '">'.img_edit($langs->trans("Edit").' - '.$langs->trans("View"), 0, 'class="valignmiddle opacitymedium"').'</a></td>';
// Add link

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -55,6 +55,7 @@ if (empty($conf) || !is_object($conf)) {
@phan-var-force EcmDirectory $ecmdir
@phan-var-force ?string $module
@phan-var-force int $section
@phan-var-force string $websitekey
';
?>
@ -95,7 +96,7 @@ if (!isset($section)) {
// Confirm remove file (for non javascript users)
if (($action == 'delete' || $action == 'file_manager_delete') && empty($conf->use_javascript_ajax)) {
// TODO Add website, pageid, filemanager if defined
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode($section).'&urlfile='.urlencode(GETPOST("urlfile")), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', '', 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section, 'urlfile' => GETPOST("urlfile"))), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', '', 1);
}
// Start container of all panels
@ -111,7 +112,15 @@ print '<div class="inline-block toolbarbutton centpercent">';
// Toolbar
if ($permtoadd) {
$websitekeyandpageid = (!empty($websitekey) ? '&website='.urlencode($websitekey) : '').(!empty($pageid) ? '&pageid='.urlencode((string) $pageid) : '');
print '<a id="acreatedir" href="'.DOL_URL_ROOT.'/ecm/dir_add_card.php?action=create&module='.urlencode($module).$websitekeyandpageid.'&backtopage='.urlencode($_SERVER["PHP_SELF"].'?file_manager=1'.$websitekeyandpageid).'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans('ECMAddSection')).'">';
$paramscreatedir = array('action' => 'create', 'module' => $module);
if (!empty($websitekey)) {
$paramscreatedir['website'] = $websitekey;
}
if (!empty($pageid)) {
$paramscreatedir['pageid'] = $pageid;
}
$paramscreatedir['backtopage'] = $_SERVER["PHP_SELF"].'?file_manager=1'.$websitekeyandpageid;
print '<a id="acreatedir" href="'.dolBuildUrl(DOL_URL_ROOT.'/ecm/dir_add_card.php', $paramscreatedir).'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans('ECMAddSection')).'">';
print img_picto('', 'folder-plus', '', 0, 0, 0, '', 'size15x marginrightonly');
print '</a>';
} else {
@ -120,19 +129,27 @@ if ($permtoadd) {
print '</a>';
}
if ($module == 'ecm') {
$tmpurl = ((!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_ECM_DISABLE_JS')) ? '#' : ($_SERVER["PHP_SELF"].'?action=refreshmanual'.($module ? '&module='.$module : '').($section ? '&section='.urlencode($section) : '')));
if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_ECM_DISABLE_JS')) {
$tmpurl = '#';
} else {
$paramsrefresh = array('action' => 'refreshmanual', 'module' => $module);
if ($section) {
$paramsrefresh['section'] = $section;
}
$tmpurl = dolBuildUrl($_SERVER["PHP_SELF"], $paramsrefresh);
}
print '<a id="arefreshbutton" href="'.$tmpurl.'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans('ReSyncListOfDir')).'">';
print img_picto('', 'refresh', 'id="refreshbutton"', 0, 0, 0, '', 'size15x marginrightonly');
print '</a>';
}
if ($permtoadd && GETPOSTISSET('website')) { // If on file manager to manage medias of a web site
// @phan-suppress-next-line PhanTypeExpectedObjectPropAccess
print '<a id="agenerateimgwebp" href="'.$_SERVER["PHP_SELF"].'?action=confirmconvertimgwebp&token='.newToken().'&website='.urlencode($website->ref).'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans("GenerateImgWebp")).'">';
print '<a id="agenerateimgwebp" href="'.dolBuildUrl($_SERVER["PHP_SELF"], array('action' => 'confirmconvertimgwebp', 'website' => $website->ref), true).'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans("GenerateImgWebp")).'">';
print img_picto('', 'images', '', 0, 0, 0, '', 'size15x flip marginrightonly');
print '</a>';
} elseif ($permtoadd && $module == 'ecm') { // If on file manager medias in ecm
if (getDolGlobalInt('ECM_SHOW_GENERATE_WEBP_BUTTON')) {
print '<a id="agenerateimgwebp" href="'.$_SERVER["PHP_SELF"].'?action=confirmconvertimgwebp&token='.newToken().'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans("GenerateImgWebp")).'">';
print '<a id="agenerateimgwebp" href="'.dolBuildUrl($_SERVER["PHP_SELF"], array('action' => 'confirmconvertimgwebp'), true).'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans("GenerateImgWebp")).'">';
print img_picto('', 'images', '', 0, 0, 0, '', 'size15x flip marginrightonly');
print '</a>';
}
@ -222,7 +239,7 @@ print '</div>';
// Ask confirmation of deletion of directory
if ($action == 'delete_section') {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode($section), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection', '', '', 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section)), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection', '', '', 1);
}
// End confirm
@ -241,14 +258,14 @@ if ($action == 'confirmconvertimgwebp') {
if ($module == 'medias') {
$formquestion['website'] = array('type' => 'hidden', 'value' => $website->ref, 'name' => 'website'); // @phan-suppress-current-line PhanTypeExpectedObjectPropAccess
}
$param = '';
$paramsconvertimgwebp = array();
if (!empty($sortfield)) {
$param .= '&sortfield='.urlencode($sortfield);
$paramsconvertimgwebp['sortfield'] = $sortfield;
}
if (!empty($sortorder)) {
$param .= '&sortorder='.urlencode($sortorder);
$paramsconvertimgwebp['sortorder'] = $sortorder;
}
print $form->formconfirm($_SERVER["PHP_SELF"].($param ? '?'.$param : ''), empty($file) ? $langs->trans('ConfirmImgWebpCreation') : $langs->trans('ConfirmChosenImgWebpCreation'), empty($file) ? $langs->trans('ConfirmGenerateImgWebp') : $langs->trans('ConfirmGenerateChosenImgWebp', basename($file)), 'convertimgwebp', $formquestion, "yes", 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], $paramsconvertimgwebp), empty($file) ? $langs->trans('ConfirmImgWebpCreation') : $langs->trans('ConfirmChosenImgWebpCreation'), empty($file) ? $langs->trans('ConfirmGenerateImgWebp') : $langs->trans('ConfirmGenerateChosenImgWebp', basename($file)), 'convertimgwebp', $formquestion, "yes", 1);
$action = 'file_manager';
}
@ -326,7 +343,12 @@ if (empty($action) || $action == 'editfile' || $action == 'file_manager' || preg
if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_ECM_DISABLE_JS')) {
// Show the link to "Root"
if ($showroot) {
print '<tr class="oddeven nohover"><td><div style="padding-left: 5px; padding-right: 5px;"><a href="'.$_SERVER["PHP_SELF"].'?file_manager=1'.(!empty($websitekey) ? '&website='.urlencode($websitekey) : '').'&pageid='.urlencode((string) $pageid).'">';
$paramsroot = array('file_manager' => 1);
if (!empty($websitekey)) {
$paramsroot['website'] = $websitekey;
}
$paramsroot['pageid'] = $pageid;
print '<tr class="oddeven nohover"><td><div style="padding-left: 5px; padding-right: 5px;"><a href="'.dolBuildUrl($_SERVER["PHP_SELF"], $paramsroot).'">';
if ($module == 'medias') {
print $langs->trans("RootOfMedias");
} else {

View file

@ -4,7 +4,7 @@
* Copyright (C) 2015 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
* Copyright (C) 2018 Francis Appels <francis.appels@yahoo.com>
* Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2019-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -1055,9 +1055,9 @@ class EcmFiles extends CommonObject
$tmppath = preg_replace('/^[^\/]+\//', '', $this->filepath);
}
}
$url = DOL_URL_ROOT.'/document.php?modulepart='.urlencode($option).'&file='.urlencode($tmppath.'/'.$this->filename).'&entity='.((int) $this->entity);
$url = dolBuildUrl(DOL_URL_ROOT.'/document.php', array('modulepart' => $option, 'file' => $tmppath.'/'.$this->filename, 'entity' => (int) $this->entity));
} else {
$url = DOL_URL_ROOT.'/ecm/file_card.php?id='.$this->id;
$url = dolBuildUrl(DOL_URL_ROOT.'/ecm/file_card.php', array('id' => $this->id));
}
$linkclose = '';

View file

@ -292,7 +292,7 @@ if (empty($action) || $action == 'delete_section') {
// Generate form to confirm deletion of a category line
if ($action == 'delete_section') {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode($section), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection');
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section)), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection');
}
@ -300,7 +300,7 @@ if (empty($action) || $action == 'delete_section') {
print '<div class="tabsAction">';
// Delete
print dolGetButtonAction($langs->trans('Delete'), '', 'delete', $_SERVER["PHP_SELF"].'?section='.urlencode($section).'&action=delete_section&token='.newToken(), '', $user->hasRight('ecm', 'setup'));
print dolGetButtonAction($langs->trans('Delete'), '', 'delete', dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section, 'action' => 'delete_section'), true), '', $user->hasRight('ecm', 'setup'));
print '</div>';
}

View file

@ -463,16 +463,25 @@ if ($action != 'edit' && $action != 'delete' && $action != 'deletefile') {
print '<div class="tabsAction">';
if ($permissiontoadd) {
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&token='.newToken().'&module='.urlencode($module).'&section='.urlencode($section).'">'.$langs->trans('Edit').'</a>';
print '<a class="butAction" href="'.dolBuildUrl($_SERVER['PHP_SELF'], array('action' => 'edit', 'module' => $module, 'section' => $section), true).'">'.$langs->trans('Edit').'</a>';
}
if ($permissiontoadd) {
print '<a class="butAction" href="'.DOL_URL_ROOT.'/ecm/dir_add_card.php?action=create&token='.newToken().'&module='.urlencode($module).'&catParent='.urlencode($section).'">'.$langs->trans('ECMAddSection').'</a>';
print '<a class="butAction" href="'.dolBuildUrl(DOL_URL_ROOT.'/ecm/dir_add_card.php', array('action' => 'create', 'module' => $module, 'catParent' => $section), true).'">'.$langs->trans('ECMAddSection').'</a>';
} else {
print '<a class="butActionRefused classfortooltip" href="#" title="'.$langs->trans("NotAllowed").'">'.$langs->trans('ECMAddSection').'</a>';
}
print dolGetButtonAction($langs->trans('Delete'), '', 'delete', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=delete&token='.newToken().'&module='.urlencode($module).'&section='.urlencode($section).($backtopage ? '&backtopage='.urlencode($backtopage) : ''), '', $permissiontoadd);
$paramsdelete = array(
'id' => $object->id,
'action' => 'delete',
'module' => $module,
'section' => $section,
);
if ($backtopage) {
$paramsdelete['backtopage'] = $backtopage;
}
print dolGetButtonAction($langs->trans('Delete'), '', 'delete', dolBuildUrl($_SERVER["PHP_SELF"], $paramsdelete, true), '', $permissiontoadd);
print '</div>';
}
@ -480,7 +489,14 @@ if ($action != 'edit' && $action != 'delete' && $action != 'deletefile') {
// Confirm remove file
if ($action == 'deletefile') {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode(GETPOST("section", 'alpha')).'&urlfile='.urlencode(GETPOST("urlfile")).($backtopage ? '&backtopage='.urlencode($backtopage) : ''), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile');
$paramsdeletefile = array(
'section' => GETPOST("section", 'alpha'),
'urlfile' => GETPOST("urlfile"),
);
if ($backtopage) {
$paramsdeletefile['backtopage'] = $backtopage;
}
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], $paramsdeletefile), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile');
}
// Confirm remove dir
@ -495,7 +511,14 @@ if ($action == 'delete' || $action == 'delete_dir') {
);
}
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode(GETPOST('section', 'alpha')).'&module='.$module.($backtopage ? '&backtopage='.urlencode($backtopage) : ''), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $relativepathwithoutslash), 'confirm_deletedir', $formquestion, 1, 1);
$paramsdeletedir = array(
'section' => GETPOST('section', 'alpha'),
'module' => $module,
);
if ($backtopage) {
$paramsdeletedir['backtopage'] = $backtopage;
}
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], $paramsdeletedir), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $relativepathwithoutslash), 'confirm_deletedir', $formquestion, 1, 1);
}

View file

@ -135,7 +135,11 @@ if ($cancel) {
header("Location: ".$backtopage);
exit;
} else {
header('Location: '.$_SERVER["PHP_SELF"].'?urlfile='.urlencode($urlfile).'&section='.urlencode($section).($module ? '&module='.urlencode($module) : ''));
$paramscancel = array('urlfile' => $urlfile, 'section' => $section);
if ($module) {
$paramscancel['module'] = $module;
}
header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], $paramscancel));
exit;
}
}
@ -236,7 +240,7 @@ if ($action == 'update' && $permissiontoadd) {
$urlfile .= '.noexe';
}
header('Location: '.$_SERVER["PHP_SELF"].'?urlfile='.urlencode($urlfile).'&section='.urlencode($section));
header('Location: '.dolBuildUrl($_SERVER["PHP_SELF"], array('urlfile' => $urlfile, 'section' => $section)));
exit;
} else {
$db->rollback();
@ -343,11 +347,12 @@ print '<tr><td>';
print $form->textwithpicto($langs->trans("DirectDownloadInternalLink"), $langs->trans("PrivateDownloadLinkDesc"));
print '</td><td>';
$modulepart = 'ecm';
$rellink = '/document.php?modulepart=' . $modulepart . '&attachment=1';
$paramsrellink = array('modulepart' => $modulepart, 'attachment' => 1);
if (!empty($object->entity)) {
$rellink .= '&entity='.$object->entity;
$paramsrellink['entity'] = $object->entity;
}
$rellink .= '&file='.urlencode($filepath);
$paramsrellink['file'] = $filepath;
$rellink = dolBuildUrl('/document.php', $paramsrellink);
$fulllink = $urlwithroot.$rellink;
print img_picto('', 'globe').' ';
if ($action != 'edit') {
@ -413,7 +418,7 @@ if ($action == 'edit') {
// Confirm deletion of a file
if ($action == 'deletefile') {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode($section), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile', $urlfile), 'confirm_deletefile', '', 1, 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section)), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile', $urlfile), 'confirm_deletefile', '', 1, 1);
}
if ($action != 'edit') {
@ -421,7 +426,7 @@ if ($action != 'edit') {
print '<div class="tabsAction">';
if ($user->hasRight('ecm', 'setup')) {
print '<a class="butAction" href="'.$_SERVER['PHP_SELF'].'?action=edit&section='.urlencode($section).'&urlfile='.urlencode($urlfile).'">'.$langs->trans('Edit').'</a>';
print '<a class="butAction" href="'.dolBuildUrl($_SERVER['PHP_SELF'], array('action' => 'edit', 'section' => $section, 'urlfile' => $urlfile)).'">'.$langs->trans('Edit').'</a>';
}
print '</div>';

View file

@ -2,7 +2,7 @@
/* Copyright (C) 2008-2014 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2008-2010 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2016 Alexandre Spangaro <aspangaro@open-dsi.fr>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Joachim Kueter <git-jk@bloxera.com>
*
@ -428,7 +428,7 @@ print dol_get_fiche_head($head, 'index_auto', '', -1, '');
// Confirm remove file (for non javascript users)
if ($action == 'deletefile' && empty($conf->use_javascript_ajax)) {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode((string) $section).'&urlfile='.urlencode(GETPOST("urlfile")), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', '', 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section, 'urlfile' => GETPOST("urlfile"))), $langs->trans('DeleteFile'), $langs->trans('ConfirmDeleteFile'), 'confirm_deletefile', '', '', 1);
}
// Start container of all panels
@ -442,7 +442,18 @@ if ($action == 'deletefile' && empty($conf->use_javascript_ajax)) {
print '<div class="inline-block toolbarbutton centpercent">';
// Toolbar
$url = ((!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_ECM_DISABLE_JS')) ? '#' : ($_SERVER["PHP_SELF"].'?action=refreshmanual'.($module ? '&module='.urlencode($module) : '').($section ? '&section='.urlencode((string) $section) : '')));
if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_ECM_DISABLE_JS')) {
$url = '#';
} else {
$paramsrefresh = array('action' => 'refreshmanual');
if ($module) {
$paramsrefresh['module'] = $module;
}
if ($section) {
$paramsrefresh['section'] = $section;
}
$url = dolBuildUrl($_SERVER["PHP_SELF"], $paramsrefresh);
}
print '<a href="'.$url.'" class="inline-block valignmiddle toolbarbutton paddingtop" title="'.dol_escape_htmltag($langs->trans('Refresh')).'">';
print img_picto('', 'refresh', 'id="refreshbutton"', 0, 0, 0, '', 'size15x marginrightonly');
print '</a>';
@ -459,7 +470,7 @@ print '</div>';
// Generate form to confirm the deletion of a category line
if ($action == 'delete_section') {
print $form->formconfirm($_SERVER["PHP_SELF"].'?section='.urlencode((string) $section), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection', '', '', 1);
print $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], array('section' => $section)), $langs->trans('DeleteSection'), $langs->trans('ConfirmDeleteSection', $ecmdir->label), 'confirm_deletesection', '', '', 1);
}
// End confirm
@ -506,7 +517,7 @@ if (empty($action) || $action == 'file_manager' || preg_match('/refresh/i', $act
}
print '<li class="directory collapsed">';
print '<a class="fmdirlia jqft ecmjqft" href="'.$_SERVER["PHP_SELF"].'?module='.urlencode($val['module']).'">';
print '<a class="fmdirlia jqft ecmjqft" href="'.dolBuildUrl($_SERVER["PHP_SELF"], array('module' => $val['module'])).'">';
print dolPrintLabel($val['label']);
print '</a>';

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2008-2017 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2008-2010 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -126,13 +126,22 @@ $websitekey = '';
*/
$savbacktopage = $backtopage;
$backtopage = $_SERVER["PHP_SELF"].'?file_manager=1&website='.urlencode((string) ($websitekey)).'&pageid='.urlencode((string) ($pageid)).(GETPOST('section_dir', 'alpha') ? '&section_dir='.urlencode((string) (GETPOST('section_dir', 'alpha'))) : ''); // used after a confirm_deletefile into actions_linkedfiles.inc.php
// used after a confirm_deletefile into actions_linkedfiles.inc.php
$paramsbacktopage = array(
'file_manager' => 1,
'website' => (string) $websitekey,
'pageid' => (string) $pageid,
);
if (GETPOST('section_dir', 'alpha')) {
$paramsbacktopage['section_dir'] = GETPOST('section_dir', 'alpha');
}
if ($sortfield) {
$backtopage .= '&sortfield='.urlencode($sortfield);
$paramsbacktopage['sortfield'] = $sortfield;
}
if ($sortorder) {
$backtopage .= '&sortorder='.urlencode($sortorder);
$paramsbacktopage['sortorder'] = $sortorder;
}
$backtopage = dolBuildUrl($_SERVER["PHP_SELF"], $paramsbacktopage);
include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php'; // This manage 'sendit', 'confirm_deletefile', 'renamefile' action when submitting new file.
$backtopage = $savbacktopage;

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2018 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2025-2026 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
@ -54,6 +54,20 @@ $paramwithoutsection = preg_replace('/&?section=(\d+)/', '', $param);
$openeddir = '/'; // The root directory shown
// $preopened // The dir to have preopened
// We must use token=currentToken() and not newToken() here because ajaxdirtree has NOTOKENRENEWAL define so there
// is no rollup of token so we must compare with the one valid on main page.
$paramsdirtree = array(
'token' => currentToken(),
'modulepart' => $module,
'openeddir' => $openeddir,
);
if (!empty($preopened)) {
$paramsdirtree['preopened'] = $preopened;
}
// $paramwithoutsection is a raw, already-built query string fragment (starting with '&' when not empty), so it is
// appended as-is after the encoded params above, not merged into $paramsdirtree.
$dirtreeurl = dolBuildUrl(DOL_URL_ROOT.'/core/ajax/ajaxdirtree.php', $paramsdirtree).(empty($paramwithoutsection) ? '' : $paramwithoutsection);
?>
$(document).ready(function() {
@ -61,8 +75,7 @@ $(document).ready(function() {
$('#filetree').fileTree({
root: '<?php print dol_escape_js($openeddir); ?>',
// Ajax called if we click to expand a dir (not a file). Parameter 'dir' is provided as a POST parameter by fileTree code to this following URL.
// We must use token=currentToken() and not newToken() here because ajaxdirtree has NOTOKENRENEWAL define so there is no rollup of token so we must compare with the one valid on main page
script: '<?php echo DOL_URL_ROOT.'/core/ajax/ajaxdirtree.php?token='.currentToken().'&modulepart='.urlencode($module).(empty($preopened) ? '' : '&preopened='.urlencode($preopened)).'&openeddir='.urlencode($openeddir).(empty($paramwithoutsection) ? '' : $paramwithoutsection); ?>',
script: '<?php echo $dirtreeurl; ?>',
folderEvent: 'click', // 'dblclick'
multiFolder: false },
// Called if we click on a file (not a dir)
@ -115,7 +128,7 @@ function loadandshowpreview(filedirname,section)
$('#ecmfileview').empty();
var url = '<?php echo dol_escape_js(dol_buildpath('/core/ajax/ajaxdirpreview.php', 1).'?action=preview&module='.urlencode($module)); ?>&section='+urlencode(section)+'&file='+urlencode(filedirname)<?php echo (empty($paramwithoutsection) ? '' : "+'".dol_escape_js($paramwithoutsection)."'"); ?>;
var url = '<?php echo dol_escape_js(dolBuildUrl(dol_buildpath('/core/ajax/ajaxdirpreview.php', 1), array('action' => 'preview', 'module' => $module))); ?>&section='+urlencode(section)+'&file='+urlencode(filedirname)<?php echo (empty($paramwithoutsection) ? '' : "+'".dol_escape_js($paramwithoutsection)."'"); ?>;
$.get(url, function(data) {
//alert('Load of url '+url+' was performed : '+data);
pos=data.indexOf("TYPE=directory",0);

View file

@ -2320,4 +2320,93 @@ class FunctionsLibTest extends CommonClassTest
$this->assertEquals('2020-07-01 00:00:01', dol_print_date($timestamp, 'standard', false));
date_default_timezone_set($savtz);
}
/**
* testGetElementProperties
*
* @return void
*/
public function testGetElementProperties()
{
// Generic path: no '@'/'_' parsing, no special case branch, so classfile/classname are completed
// from subelement (classfile=strtolower(subelement), classname=ucfirst(subelement))
$properties = getElementProperties('project');
$this->assertEquals('project', $properties['element']);
$this->assertEquals('projet', $properties['module']);
$this->assertEquals('project', $properties['subelement']);
$this->assertEquals('projet', $properties['table_element']);
$this->assertEquals('projet/class', $properties['classpath']);
$this->assertEquals('project', $properties['classfile']);
$this->assertEquals('Project', $properties['classname']);
// 'myobject@mymodule' syntax to ask a resource from an external module: note that table_element
// keeps the full original string (it is never cleaned of the '@mymodule' part)
$properties = getElementProperties('myobject@mymodule');
$this->assertEquals('myobject', $properties['element']);
$this->assertEquals('mymodule', $properties['module']);
$this->assertEquals('myobject', $properties['subelement']);
$this->assertEquals('myobject@mymodule', $properties['table_element']);
$this->assertEquals('mymodule/class', $properties['classpath']);
$this->assertEquals('myobject', $properties['classfile']);
$this->assertEquals('Myobject', $properties['classname']);
// 'myobject_mysubobject' syntax: element/module resolved from the string, but the specific
// 'project_task' case branch then overrides module (projet, not project) and table_element
$properties = getElementProperties('project_task');
$this->assertEquals('project', $properties['element']);
$this->assertEquals('projet', $properties['module']);
$this->assertEquals('task', $properties['subelement']);
$this->assertEquals('projet_task', $properties['table_element']);
$this->assertEquals('projet/class', $properties['classpath']);
$this->assertEquals('task', $properties['classfile']);
$this->assertEquals('Task', $properties['classname']);
// Generic '...det' fallback (no dedicated case branch for this fictional module): module and
// subelement are stripped of the 'det' suffix, but element is not, and classname keeps the raw
// (non-capitalized) result of the suffix replacement since it is not empty afterwards
$properties = getElementProperties('myobjectdet');
$this->assertEquals('myobjectdet', $properties['element']);
$this->assertEquals('myobject', $properties['module']);
$this->assertEquals('myobject', $properties['subelement']);
$this->assertEquals('myobjectdet', $properties['table_element']);
$this->assertEquals('myobject/class', $properties['classpath']);
$this->assertEquals('myobject', $properties['classfile']);
$this->assertEquals('myobjectLine', $properties['classname']);
// 'contratdet': the generic '...det' fallback runs first (module=contrat is in the list of
// modules using "Ligne" instead of "Line", so classname=contratLigne), then the dedicated
// 'contratdet' case branch only adds parent_element on top, it does not touch classname/classfile
$properties = getElementProperties('contratdet');
$this->assertEquals('contrat', $properties['module']);
$this->assertEquals('contrat', $properties['subelement']);
$this->assertEquals('contratdet', $properties['table_element']);
$this->assertEquals('contrat', $properties['parent_element']);
$this->assertEquals('contrat/class', $properties['classpath']);
$this->assertEquals('contrat', $properties['classfile']);
$this->assertEquals('contratLigne', $properties['classname']);
// 'facturedet': same generic fallback runs first, but this time the dedicated 'facturedet' case
// branch overrides classpath (fuller 'compta/facture/class' path) and classname (capitalized
// 'FactureLigne' instead of the generic fallback's lowercase 'factureLigne')
$properties = getElementProperties('facturedet');
$this->assertEquals('facture', $properties['module']);
$this->assertEquals('facturedet', $properties['table_element']);
$this->assertEquals('facture', $properties['parent_element']);
$this->assertEquals('compta/facture/class', $properties['classpath']);
$this->assertEquals('facture', $properties['classfile']);
$this->assertEquals('FactureLigne', $properties['classname']);
// facture is a core module, always configured with an output directory
$this->assertNotEmpty($properties['dir_output']);
// 'action'/'actioncomm' special case: note how table_element is corrected to 'actioncomm' even
// though the input 'action' is kept as-is in element, and subelement is capitalized 'Actioncomm'
$properties = getElementProperties('action');
$this->assertEquals('action', $properties['element']);
$this->assertEquals('agenda', $properties['module']);
$this->assertEquals('Actioncomm', $properties['subelement']);
$this->assertEquals('actioncomm', $properties['table_element']);
$this->assertEquals('comm/action/class', $properties['classpath']);
$this->assertEquals('actioncomm', $properties['classfile']);
$this->assertEquals('Actioncomm', $properties['classname']);
}
}