From cd260d9e2a711338c6917858b80191fcdec540d7 Mon Sep 17 00:00:00 2001 From: VESSILLER Date: Tue, 12 Sep 2023 15:12:30 +0200 Subject: [PATCH 001/397] NEW WebPortal module --- htdocs/webportal/ChangeLog.md | 5 + htdocs/webportal/README.md | 1 + htdocs/webportal/admin/configcss.php | 177 + htdocs/webportal/admin/setup.php | 625 + .../v16/core/class/html.formsetup.class.php | 1398 ++ .../webportal/build/makepack-webportal.conf | 11 + .../class/webportalinvoice.class.php | 340 + .../webportal/class/webportalmember.class.php | 533 + .../webportal/class/webportalorder.class.php | 348 + .../class/webportalpartnership.class.php | 365 + .../webportal/class/webportalpropal.class.php | 329 + .../core/modules/modWebPortal.class.php | 392 + htdocs/webportal/langs/en_US/webportal.lang | 82 + htdocs/webportal/lib/webportal.lib.php | 76 + htdocs/webportal/modulebuilder.txt | 3 + .../webportal/public/class/context.class.php | 684 + .../public/class/controller.class.php | 196 + .../class/html.formcardwebportal.class.php | 845 + .../class/html.formlistwebportal.class.php | 736 + .../public/class/html.formwebportal.class.php | 1318 ++ .../public/controllers/default.controller.php | 64 + .../controllers/document.controller.php | 304 + .../controllers/invoicelist.controller.php | 112 + .../public/controllers/login.controller.php | 63 + .../controllers/membercard.controller.php | 117 + .../controllers/orderlist.controller.php | 114 + .../partnershipcard.controller.php | 116 + .../controllers/propallist.controller.php | 115 + htdocs/webportal/public/css/global.css | 38 + htdocs/webportal/public/css/login.css | 202 + htdocs/webportal/public/css/mixin.css | 4 + htdocs/webportal/public/css/pico.css | 2821 +++ .../public/css/themes/custom.css.php | 44 + .../webportal/public/css/themes/default.css | 526 + .../public/includes/jquery/README.txt | 5 + .../base/images/ui-icons_444444_256x240.png | Bin 0 -> 7090 bytes .../base/images/ui-icons_555555_256x240.png | Bin 0 -> 7074 bytes .../base/images/ui-icons_777620_256x240.png | Bin 0 -> 4676 bytes .../base/images/ui-icons_777777_256x240.png | Bin 0 -> 7111 bytes .../base/images/ui-icons_cc0000_256x240.png | Bin 0 -> 4676 bytes .../base/images/ui-icons_ffffff_256x240.png | Bin 0 -> 6487 bytes .../includes/jquery/css/base/jquery-ui.css | 1311 ++ .../jquery/css/base/jquery-ui.min.css | 7 + .../public/includes/jquery/css/base/theme.css | 443 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 212 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 208 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 335 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 207 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 280 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 6922 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 6992 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 6999 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4549 bytes .../jquery/css/smoothness/jquery-ui.css | 2645 +++ .../jquery/css/smoothness/jquery-ui.min.css | 7 + .../includes/jquery/css/smoothness/theme.css | 410 + .../public/includes/jquery/js/jquery-ui.js | 19062 ++++++++++++++++ .../includes/jquery/js/jquery-ui.min.js | 6 + .../public/includes/jquery/js/jquery.js | 10965 +++++++++ .../public/includes/jquery/js/jquery.min.js | 2 + .../jnotify/jquery.jnotify-alt.min.css | 88 + .../jquery/plugins/jnotify/jquery.jnotify.css | 75 + .../jquery/plugins/jnotify/jquery.jnotify.js | 209 + .../plugins/jnotify/jquery.jnotify.min.css | 1 + .../plugins/jnotify/jquery.jnotify.min.js | 21 + htdocs/webportal/public/index.php | 23 + htdocs/webportal/public/js/modal.js | 95 + htdocs/webportal/public/js/theme.js | 21 + htdocs/webportal/public/lib/webportal.lib.php | 117 + .../lib/webportal_webportalmember.lib.php | 58 + htdocs/webportal/public/logout.php | 55 + htdocs/webportal/public/tpl/404.tpl.php | 22 + htdocs/webportal/public/tpl/dolibarr_logo.svg | 209 + htdocs/webportal/public/tpl/errors.tpl.php | 36 + htdocs/webportal/public/tpl/footer.tpl.php | 100 + htdocs/webportal/public/tpl/header.tpl.php | 42 + .../webportal/public/tpl/header_login.tpl.php | 46 + htdocs/webportal/public/tpl/home.tpl.php | 40 + htdocs/webportal/public/tpl/login.tpl.php | 38 + htdocs/webportal/public/tpl/menu.tpl.php | 189 + htdocs/webportal/webportal.main.inc.php | 215 + 85 files changed, 49667 insertions(+) create mode 100644 htdocs/webportal/ChangeLog.md create mode 100644 htdocs/webportal/README.md create mode 100644 htdocs/webportal/admin/configcss.php create mode 100644 htdocs/webportal/admin/setup.php create mode 100644 htdocs/webportal/backport/v16/core/class/html.formsetup.class.php create mode 100644 htdocs/webportal/build/makepack-webportal.conf create mode 100644 htdocs/webportal/class/webportalinvoice.class.php create mode 100644 htdocs/webportal/class/webportalmember.class.php create mode 100644 htdocs/webportal/class/webportalorder.class.php create mode 100644 htdocs/webportal/class/webportalpartnership.class.php create mode 100644 htdocs/webportal/class/webportalpropal.class.php create mode 100644 htdocs/webportal/core/modules/modWebPortal.class.php create mode 100644 htdocs/webportal/langs/en_US/webportal.lang create mode 100644 htdocs/webportal/lib/webportal.lib.php create mode 100644 htdocs/webportal/modulebuilder.txt create mode 100644 htdocs/webportal/public/class/context.class.php create mode 100644 htdocs/webportal/public/class/controller.class.php create mode 100644 htdocs/webportal/public/class/html.formcardwebportal.class.php create mode 100644 htdocs/webportal/public/class/html.formlistwebportal.class.php create mode 100644 htdocs/webportal/public/class/html.formwebportal.class.php create mode 100644 htdocs/webportal/public/controllers/default.controller.php create mode 100644 htdocs/webportal/public/controllers/document.controller.php create mode 100644 htdocs/webportal/public/controllers/invoicelist.controller.php create mode 100644 htdocs/webportal/public/controllers/login.controller.php create mode 100644 htdocs/webportal/public/controllers/membercard.controller.php create mode 100644 htdocs/webportal/public/controllers/orderlist.controller.php create mode 100644 htdocs/webportal/public/controllers/partnershipcard.controller.php create mode 100644 htdocs/webportal/public/controllers/propallist.controller.php create mode 100644 htdocs/webportal/public/css/global.css create mode 100644 htdocs/webportal/public/css/login.css create mode 100644 htdocs/webportal/public/css/mixin.css create mode 100644 htdocs/webportal/public/css/pico.css create mode 100644 htdocs/webportal/public/css/themes/custom.css.php create mode 100644 htdocs/webportal/public/css/themes/default.css create mode 100644 htdocs/webportal/public/includes/jquery/README.txt create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_444444_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_555555_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_777620_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_777777_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_cc0000_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/images/ui-icons_ffffff_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/base/jquery-ui.css create mode 100644 htdocs/webportal/public/includes/jquery/css/base/jquery-ui.min.css create mode 100644 htdocs/webportal/public/includes/jquery/css/base/theme.css create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-icons_222222_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-icons_454545_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-icons_888888_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/jquery-ui.css create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/jquery-ui.min.css create mode 100644 htdocs/webportal/public/includes/jquery/css/smoothness/theme.css create mode 100644 htdocs/webportal/public/includes/jquery/js/jquery-ui.js create mode 100644 htdocs/webportal/public/includes/jquery/js/jquery-ui.min.js create mode 100644 htdocs/webportal/public/includes/jquery/js/jquery.js create mode 100644 htdocs/webportal/public/includes/jquery/js/jquery.min.js create mode 100644 htdocs/webportal/public/includes/jquery/plugins/jnotify/jquery.jnotify-alt.min.css create mode 100644 htdocs/webportal/public/includes/jquery/plugins/jnotify/jquery.jnotify.css create mode 100644 htdocs/webportal/public/includes/jquery/plugins/jnotify/jquery.jnotify.js create mode 100644 htdocs/webportal/public/includes/jquery/plugins/jnotify/jquery.jnotify.min.css create mode 100644 htdocs/webportal/public/includes/jquery/plugins/jnotify/jquery.jnotify.min.js create mode 100644 htdocs/webportal/public/index.php create mode 100644 htdocs/webportal/public/js/modal.js create mode 100644 htdocs/webportal/public/js/theme.js create mode 100644 htdocs/webportal/public/lib/webportal.lib.php create mode 100644 htdocs/webportal/public/lib/webportal_webportalmember.lib.php create mode 100644 htdocs/webportal/public/logout.php create mode 100644 htdocs/webportal/public/tpl/404.tpl.php create mode 100644 htdocs/webportal/public/tpl/dolibarr_logo.svg create mode 100644 htdocs/webportal/public/tpl/errors.tpl.php create mode 100644 htdocs/webportal/public/tpl/footer.tpl.php create mode 100644 htdocs/webportal/public/tpl/header.tpl.php create mode 100644 htdocs/webportal/public/tpl/header_login.tpl.php create mode 100644 htdocs/webportal/public/tpl/home.tpl.php create mode 100644 htdocs/webportal/public/tpl/login.tpl.php create mode 100644 htdocs/webportal/public/tpl/menu.tpl.php create mode 100644 htdocs/webportal/webportal.main.inc.php diff --git a/htdocs/webportal/ChangeLog.md b/htdocs/webportal/ChangeLog.md new file mode 100644 index 0000000000000..e4bd582350ab5 --- /dev/null +++ b/htdocs/webportal/ChangeLog.md @@ -0,0 +1,5 @@ +# CHANGELOG WEBPORTAL FOR [DOLIBARR ERP CRM](https://www.dolibarr.org) + +## 1.0 + +Initial version diff --git a/htdocs/webportal/README.md b/htdocs/webportal/README.md new file mode 100644 index 0000000000000..e1e4064009e40 --- /dev/null +++ b/htdocs/webportal/README.md @@ -0,0 +1 @@ +Public web portal module for membership and partnership \ No newline at end of file diff --git a/htdocs/webportal/admin/configcss.php b/htdocs/webportal/admin/configcss.php new file mode 100644 index 0000000000000..895a6f4928f27 --- /dev/null +++ b/htdocs/webportal/admin/configcss.php @@ -0,0 +1,177 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file admin/setup.php + * \ingroup webportal + * \brief WebPortal setup page. + */ + +// Load Dolibarr environment +$res = 0; +// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined) +if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { + $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"] . "/main.inc.php"; +} +// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME +$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; +$tmp2 = realpath(__FILE__); +$i = strlen($tmp) - 1; +$j = strlen($tmp2) - 1; +while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { + $i--; + $j--; +} +if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1)) . "/main.inc.php")) { + $res = @include substr($tmp, 0, ($i + 1)) . "/main.inc.php"; +} +if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php")) { + $res = @include dirname(substr($tmp, 0, ($i + 1))) . "/main.inc.php"; +} +// Try main.inc.php using relative path +if (!$res && file_exists("../../main.inc.php")) { + $res = @include "../../main.inc.php"; +} +if (!$res && file_exists("../../../main.inc.php")) { + $res = @include "../../../main.inc.php"; +} +if (!$res) { + die("Include of main fails"); +} + +global $conf, $db, $hookmanager, $langs, $user; + +// Libraries +require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; +dol_include_once('/webportal/lib/webportal.lib.php'); + +// Translations +$langs->loadLangs(array("admin", "webportal", "hrm", "other")); + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('webportalsetup', 'globalsetup')); + +// Access control +if (!$user->admin) { + accessforbidden(); +} + +// Parameters +$action = GETPOST('action', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); + +/* + * Actions + */ + +$parameters = array(); +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} + +// Convert action set_XXX and del_XXX to set var (this is used when no javascript on for ajax_constantonoff) +$regs = array(); +if (preg_match('/^(set|del)_([A-Z_]+)$/', $action, $regs)) { + if ($regs[1] == 'set') { + dolibarr_set_const($db, $regs[2], 1, 'chaine', 0, '', $conf->entity); + } else { + dolibarr_del_const($db, $regs[2], $conf->entity); + } +} + +if ($action == 'updatecss') { + dolibarr_set_const($db, "WEBPORTAL_CUSTOM_CSS", GETPOST('WEBPORTAL_CUSTOM_CSS', 'restricthtml'), 'chaine', 0, '', $conf->entity); + dolibarr_set_const($db, "WEBPORTAL_PARAMS_REV", ((int) $conf->global->WEBPORTAL_PARAMS_REV) + 1, 'chaine', 0, '', $conf->entity); +} + + +/* + * View + */ + +$page_name = "WebPortalCSS"; + +$wikihelp = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones'; + +llxHeader( + '', + $langs->trans($page_name), + $wikihelp, + '', + 0, + 0, + array( + '/includes/ace/src/ace.js', + '/includes/ace/src/ext-statusbar.js', + '/includes/ace/src/ext-language_tools.js', + ), + array() +); + +// Subheader +$linkback = '' . $langs->trans("BackToModuleList") . ''; + +print load_fiche_titre($langs->trans($page_name), $linkback, 'title_setup'); + +// Configuration header +$head = webportalAdminPrepareHead(); +print dol_get_fiche_head($head, 'css', $langs->trans($page_name), -1, "webportal@webportal"); + +// Setup page goes here +echo '' . $langs->trans("WebPortalCSS") . '

'; + +//WYSIWYG Editor +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + +print '
'; + +print ''; +print ''; + +clearstatcache(); + +// editeur CSS +print '
'; +print ''; + +print ''; +print '' . "\n"; + +print '
'; + +$customcssValue = getDolGlobalString('WEBPORTAL_CUSTOM_CSS'); + +$doleditor = new DolEditor('WEBPORTAL_CUSTOM_CSS', $customcssValue, '80%', 400, 'Basic', 'In', true, false, 'ace', 10, '90%'); +$doleditor->Create(0, '', true, 'css', 'css'); +print '
' . "\n"; +print '
'; + +print '
'; +print ''; +print ''; +print '
'; + +print '
'; + +// Page end +print dol_get_fiche_end(); + +// End of page +llxFooter(); +$db->close(); diff --git a/htdocs/webportal/admin/setup.php b/htdocs/webportal/admin/setup.php new file mode 100644 index 0000000000000..2600796fff6ce --- /dev/null +++ b/htdocs/webportal/admin/setup.php @@ -0,0 +1,625 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file admin/setup.php + * \ingroup webportal + * \brief WebPortal setup page. + */ + +// Load Dolibarr environment +$res = 0; +// Try main.inc.php into web root known defined into CONTEXT_DOCUMENT_ROOT (not always defined) +if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { + $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; +} +// Try main.inc.php into web root detected using web root calculated from SCRIPT_FILENAME +$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1; +while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { + $i--; $j--; +} +if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { + $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; +} +if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { + $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; +} +// Try main.inc.php using relative path +if (!$res && file_exists("../../main.inc.php")) { + $res = @include "../../main.inc.php"; +} +if (!$res && file_exists("../../../main.inc.php")) { + $res = @include "../../../main.inc.php"; +} +if (!$res) { + die("Include of main fails"); +} + +global $conf, $db, $hookmanager, $langs, $user; + +// Libraries +require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php"; +dol_include_once('/webportal/lib/webportal.lib.php'); + +// Translations +$langs->loadLangs(array("admin", "webportal")); + +// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context +$hookmanager->initHooks(array('webportalsetup', 'globalsetup')); + +// Access control +if (!$user->admin) { + accessforbidden(); +} + +// Parameters +$action = GETPOST('action', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); +$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php + +$value = GETPOST('value', 'alpha'); +$label = GETPOST('label', 'alpha'); +$scandir = GETPOST('scan_dir', 'alpha'); +$type = 'myobject'; + + +$error = 0; +$setupnotempty = 0; + +// Set this to 1 to use the factory to manage constants. Warning, the generated module will be compatible with version v15+ only +$useFormSetup = 1; + +if (!class_exists('FormSetup')) { + // For retrocompatibility Dolibarr < 16.0 + if (floatval(DOL_VERSION) < 16.0 && !class_exists('FormSetup')) { + require_once __DIR__.'/../backport/v16/core/class/html.formsetup.class.php'; + } else { + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formsetup.class.php'; + } +} + +$formSetup = new FormSetup($db); + +// root url +$item = $formSetup->newItem('WEBPORTAL_ROOT_URL')->setAsString(); +$item->fieldAttr = array('placeholder' => 'https://'); +$item->helpText = $langs->transnoentities('WebPortalRootUrlHelp'); + +// Enable access for the membership record +$access_list = array( + 'hidden' => $langs->trans('WebPortalAccessHidden'), + 'visible' => $langs->trans('WebPortalAccessVisible'), + 'edit' => $langs->trans('WebPortalAccessEdit'), +); +$item = $formSetup->newItem('WEBPORTAL_MEMBER_CARD_ACCESS'); +$item->setAsSelect($access_list); +$item->helpText = $langs->transnoentities('WebPortalMemberCardAccessHelp'); + +// Enable access for the partnership record +$access_list = array( + 'hidden' => $langs->trans('WebPortalAccessHidden'), + 'visible' => $langs->trans('WebPortalAccessVisible'), +); +$item = $formSetup->newItem('WEBPORTAL_PARTNERSHIP_CARD_ACCESS'); +$item->setAsSelect($access_list); +$item->helpText = $langs->transnoentities('WebPortalPartnerShipCardAccessHelp'); + +// Enable access for the proposals +$formSetup->newItem('WEBPORTAL_PROPAL_LIST_ACCESS')->setAsYesNo(); + +// Enable access for the orders +$formSetup->newItem('WEBPORTAL_ORDER_LIST_ACCESS')->setAsYesNo(); + +// Enable access for the invoices +$formSetup->newItem('WEBPORTAL_INVOICE_LIST_ACCESS')->setAsYesNo(); + +// Add logged user +//$formSetup->newItem('WEBPORTAL_USER_LOGGED2')->setAsSelectUser(); +// only enabled users +$userList = $formSetup->form->select_dolusers(getDolGlobalInt('WEBPORTAL_USER_LOGGED'),'WEBPORTAL_USER_LOGGED', 0, null, 0, '', '', '0', 0, 0, '', 0, '', '', 1, 1); +$item = $formSetup->newItem('WEBPORTAL_USER_LOGGED'); +$item->setAsSelect($userList); +$item->helpText = $langs->transnoentities('WebPortalUserLoggedHelp'); + + +// HTTP HOST +//$item = $formSetup->newItem('NO_PARAM_JUST_TEXT'); +//$item->fieldOverride = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST']; +//$item->cssClass = 'minwidth500'; + +// Setup conf WEBPORTAL_MYPARAM1 as a simple string input +//$item = $formSetup->newItem('WEBPORTAL_MYPARAM1'); +//$item->defaultFieldValue = 'default value'; + +// Setup conf WEBPORTAL_MYPARAM2 as a simple textarea input but we replace the text of field title +//$item = $formSetup->newItem('WEBPORTAL_MYPARAM2'); +//$item->nameText = $item->getNameText().' more html text '; + +// Setup conf WEBPORTAL_MYPARAM3 +//$item = $formSetup->newItem('WEBPORTAL_MYPARAM3'); +//$item->setAsThirdpartyType(); + +// Setup conf WEBPORTAL_MYPARAM4 : exemple of quick define write style +//$formSetup->newItem('WEBPORTAL_MYPARAM4')->setAsYesNo(); + +// Setup conf WEBPORTAL_MYPARAM5 +//$formSetup->newItem('WEBPORTAL_MYPARAM5')->setAsEmailTemplate('thirdparty'); + +// Setup conf WEBPORTAL_MYPARAM6 +//$formSetup->newItem('WEBPORTAL_MYPARAM6')->setAsSecureKey()->enabled = 0; // disabled + +// Setup conf WEBPORTAL_MYPARAM7 +//$formSetup->newItem('WEBPORTAL_MYPARAM7')->setAsProduct(); + +//$formSetup->newItem('Title')->setAsTitle(); + +// Setup conf WEBPORTAL_MYPARAM8 +//$item = $formSetup->newItem('WEBPORTAL_MYPARAM8'); +//$TField = array( +// 'test01' => $langs->trans('test01'), +// 'test02' => $langs->trans('test02'), +// 'test03' => $langs->trans('test03'), +// 'test04' => $langs->trans('test04'), +// 'test05' => $langs->trans('test05'), +// 'test06' => $langs->trans('test06'), +//); +//$item->setAsMultiSelect($TField); +//$item->helpText = $langs->transnoentities('WEBPORTAL_MYPARAM8'); + + +// Setup conf WEBPORTAL_MYPARAM9 +//$formSetup->newItem('WEBPORTAL_MYPARAM9')->setAsSelect($TField); + + +// Setup conf WEBPORTAL_MYPARAM10 +//$item = $formSetup->newItem('WEBPORTAL_MYPARAM10'); +//$item->setAsColor(); +//$item->defaultFieldValue = '#FF0000'; +//$item->nameText = $item->getNameText().' more html text '; +//$item->fieldInputOverride = ''; +//$item->helpText = $langs->transnoentities('AnHelpMessage'); +//$item->fieldValue = ''; +//$item->fieldAttr = array() ; // fields attribute only for compatible fields like input text +//$item->fieldOverride = false; // set this var to override field output will override $fieldInputOverride and $fieldOutputOverride too +//$item->fieldInputOverride = false; // set this var to override field input +//$item->fieldOutputOverride = false; // set this var to override field output + + +$setupnotempty += count($formSetup->items); + + +$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + + +/* + * Actions + */ + +// For retrocompatibility Dolibarr < 15.0 +if ( versioncompare(explode('.', DOL_VERSION), array(15)) < 0 && $action == 'update' && !empty($user->admin)) { + $formSetup->saveConfFromPost(); +} + +include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; + +if ($action == 'updateMask') { + $maskconst = GETPOST('maskconst', 'aZ09'); + $maskvalue = GETPOST('maskvalue', 'alpha'); + + if ($maskconst && preg_match('/_MASK$/', $maskconst)) { + $res = dolibarr_set_const($db, $maskconst, $maskvalue, 'chaine', 0, '', $conf->entity); + if (!($res > 0)) { + $error++; + } + } + + if (!$error) { + setEventMessages($langs->trans("SetupSaved"), null, 'mesgs'); + } else { + setEventMessages($langs->trans("Error"), null, 'errors'); + } +} elseif ($action == 'specimen') { + $modele = GETPOST('module', 'alpha'); + $tmpobjectkey = GETPOST('object'); + + $tmpobject = new $tmpobjectkey($db); + $tmpobject->initAsSpecimen(); + + // Search template files + $file = ''; $classname = ''; $filefound = 0; + $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); + foreach ($dirmodels as $reldir) { + $file = dol_buildpath($reldir."core/modules/webportal/doc/pdf_".$modele."_".strtolower($tmpobjectkey).".modules.php", 0); + if (file_exists($file)) { + $filefound = 1; + $classname = "pdf_".$modele."_".strtolower($tmpobjectkey); + break; + } + } + + if ($filefound) { + require_once $file; + + $module = new $classname($db); + + if ($module->write_file($tmpobject, $langs) > 0) { + header("Location: ".DOL_URL_ROOT."/document.php?modulepart=webportal-".strtolower($tmpobjectkey)."&file=SPECIMEN.pdf"); + return; + } else { + setEventMessages($module->error, null, 'errors'); + dol_syslog($module->error, LOG_ERR); + } + } else { + setEventMessages($langs->trans("ErrorModuleNotFound"), null, 'errors'); + dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR); + } +} elseif ($action == 'setmod') { + // TODO Check if numbering module chosen can be activated by calling method canBeActivated + $tmpobjectkey = GETPOST('object'); + if (!empty($tmpobjectkey)) { + $constforval = 'WEBPORTAL_'.strtoupper($tmpobjectkey)."_ADDON"; + dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity); + } +} elseif ($action == 'set') { + // Activate a model + $ret = addDocumentModel($value, $type, $label, $scandir); +} elseif ($action == 'del') { + $ret = delDocumentModel($value, $type); + if ($ret > 0) { + $tmpobjectkey = GETPOST('object'); + if (!empty($tmpobjectkey)) { + $constforval = 'WEBPORTAL_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; + if (getDolGlobalString($constforval) == "$value") { + dolibarr_del_const($db, $constforval, $conf->entity); + } + } + } +} elseif ($action == 'setdoc') { + // Set or unset default model + $tmpobjectkey = GETPOST('object'); + if (!empty($tmpobjectkey)) { + $constforval = 'WEBPORTAL_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; + if (dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity)) { + // The constant that was read before the new set + // We therefore requires a variable to have a coherent view + $conf->global->$constforval = $value; + } + + // We disable/enable the document template (into llx_document_model table) + $ret = delDocumentModel($value, $type); + if ($ret > 0) { + $ret = addDocumentModel($value, $type, $label, $scandir); + } + } +} elseif ($action == 'unsetdoc') { + $tmpobjectkey = GETPOST('object'); + if (!empty($tmpobjectkey)) { + $constforval = 'WEBPORTAL_'.strtoupper($tmpobjectkey).'_ADDON_PDF'; + dolibarr_del_const($db, $constforval, $conf->entity); + } +} + + + +/* + * View + */ + +$form = new Form($db); + +$help_url = ''; +$page_name = "WebPortalSetup"; + +llxHeader('', $langs->trans($page_name), $help_url); + +// Subheader +$linkback = ''.$langs->trans("BackToModuleList").''; + +print load_fiche_titre($langs->trans($page_name), $linkback, 'title_setup'); + +// Configuration header +$head = webportalAdminPrepareHead(); +print dol_get_fiche_head($head, 'settings', $langs->trans($page_name), -1, "webportal@webportal"); + +// Setup page goes here +echo ''.$langs->trans("WebPortalSetupPage").'

'; + + +if ($action == 'edit') { + print $formSetup->generateOutput(true); + print '
'; +} elseif (!empty($formSetup->items)) { + print $formSetup->generateOutput(); + print '
'; + print ''.$langs->trans("Modify").''; + print '
'; +} else { + print '
'.$langs->trans("NothingToSetup"); +} + + +$moduledir = 'webportal'; +$myTmpObjects = array(); +// TODO Scan list of objects +$myTmpObjects['myobject'] = array('label'=>'MyObject', 'includerefgeneration'=>0, 'includedocgeneration'=>0); + + +foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) { + if ($myTmpObjectArray['includerefgeneration']) { + /* + * Orders Numbering model + */ + $setupnotempty++; + + print load_fiche_titre($langs->trans("NumberingModules", $myTmpObjectArray['label']), '', ''); + + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''."\n"; + + clearstatcache(); + + foreach ($dirmodels as $reldir) { + $dir = dol_buildpath($reldir."core/modules/".$moduledir); + + if (is_dir($dir)) { + $handle = opendir($dir); + if (is_resource($handle)) { + while (($file = readdir($handle)) !== false) { + if (strpos($file, 'mod_'.strtolower($myTmpObjectKey).'_') === 0 && substr($file, dol_strlen($file) - 3, 3) == 'php') { + $file = substr($file, 0, dol_strlen($file) - 4); + + require_once $dir.'/'.$file.'.php'; + + $module = new $file($db); + + // Show modules according to features level + if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) { + continue; + } + if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) { + continue; + } + + if ($module->isEnabled()) { + dol_include_once('/'.$moduledir.'/class/'.strtolower($myTmpObjectKey).'.class.php'); + + print ''; + + // Show example of numbering model + print ''."\n"; + + print ''; + + $mytmpinstance = new $myTmpObjectKey($db); + $mytmpinstance->initAsSpecimen(); + + // Info + $htmltooltip = ''; + $htmltooltip .= ''.$langs->trans("Version").': '.$module->getVersion().'
'; + + $nextval = $module->getNextValue($mytmpinstance); + if ("$nextval" != $langs->trans("NotAvailable")) { // Keep " on nextval + $htmltooltip .= ''.$langs->trans("NextValue").': '; + if ($nextval) { + if (preg_match('/^Error/', $nextval) || $nextval == 'NotConfigured') { + $nextval = $langs->trans($nextval); + } + $htmltooltip .= $nextval.'
'; + } else { + $htmltooltip .= $langs->trans($module->error).'
'; + } + } + + print ''; + + print "\n"; + } + } + } + closedir($handle); + } + } + } + print "
'.$langs->trans("Name").''.$langs->trans("Description").''.$langs->trans("Example").''.$langs->trans("Status").''.$langs->trans("ShortInfo").'
'.$module->name."\n"; + print $module->info(); + print ''; + $tmp = $module->getExample(); + if (preg_match('/^Error/', $tmp)) { + $langs->load("errors"); + print '
'.$langs->trans($tmp).'
'; + } elseif ($tmp == 'NotConfigured') { + print $langs->trans($tmp); + } else { + print $tmp; + } + print '
'; + $constforvar = 'WEBPORTAL_'.strtoupper($myTmpObjectKey).'_ADDON'; + if (getDolGlobalString($constforvar) == $file) { + print img_picto($langs->trans("Activated"), 'switch_on'); + } else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; + } + print ''; + print $form->textwithpicto('', $htmltooltip, 1, 0); + print '

\n"; + } + + if ($myTmpObjectArray['includedocgeneration']) { + /* + * Document templates generators + */ + $setupnotempty++; + $type = strtolower($myTmpObjectKey); + + print load_fiche_titre($langs->trans("DocumentModules", $myTmpObjectKey), '', ''); + + // Load array def with activated templates + $def = array(); + $sql = "SELECT nom"; + $sql .= " FROM ".MAIN_DB_PREFIX."document_model"; + $sql .= " WHERE type = '".$db->escape($type)."'"; + $sql .= " AND entity = ".$conf->entity; + $resql = $db->query($sql); + if ($resql) { + $i = 0; + $num_rows = $db->num_rows($resql); + while ($i < $num_rows) { + $array = $db->fetch_array($resql); + array_push($def, $array[0]); + $i++; + } + } else { + dol_print_error($db); + } + + print "\n"; + print "\n"; + print ''; + print ''; + print '\n"; + print '\n"; + print ''; + print ''; + print "\n"; + + clearstatcache(); + + foreach ($dirmodels as $reldir) { + foreach (array('', '/doc') as $valdir) { + $realpath = $reldir."core/modules/".$moduledir.$valdir; + $dir = dol_buildpath($realpath); + + if (is_dir($dir)) { + $handle = opendir($dir); + if (is_resource($handle)) { + while (($file = readdir($handle)) !== false) { + $filelist[] = $file; + } + closedir($handle); + arsort($filelist); + + foreach ($filelist as $file) { + if (preg_match('/\.modules\.php$/i', $file) && preg_match('/^(pdf_|doc_)/', $file)) { + if (file_exists($dir.'/'.$file)) { + $name = substr($file, 4, dol_strlen($file) - 16); + $classname = substr($file, 0, dol_strlen($file) - 12); + + require_once $dir.'/'.$file; + $module = new $classname($db); + + $modulequalified = 1; + if ($module->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) { + $modulequalified = 0; + } + if ($module->version == 'experimental' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1) { + $modulequalified = 0; + } + + if ($modulequalified) { + print ''; + + // Active + if (in_array($name, $def)) { + print ''; + } else { + print '"; + } + + // Default + print ''; + + // Info + $htmltooltip = ''.$langs->trans("Name").': '.$module->name; + $htmltooltip .= '
'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown")); + if ($module->type == 'pdf') { + $htmltooltip .= '
'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; + } + $htmltooltip .= '
'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file; + + $htmltooltip .= '

'.$langs->trans("FeaturesSupported").':'; + $htmltooltip .= '
'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1); + $htmltooltip .= '
'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang, 1, 1); + + print ''; + + // Preview + print ''; + + print "\n"; + } + } + } + } + } + } + } + } + + print '
'.$langs->trans("Name").''.$langs->trans("Description").''.$langs->trans("Status")."'.$langs->trans("Default")."'.$langs->trans("ShortInfo").''.$langs->trans("Preview").'
'; + print (empty($module->name) ? $name : $module->name); + print "\n"; + if (method_exists($module, 'info')) { + print $module->info($langs); + } else { + print $module->description; + } + print ''."\n"; + print ''; + print img_picto($langs->trans("Enabled"), 'switch_on'); + print ''; + print ''."\n"; + print 'scandir).'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"), 'switch_off').''; + print "'; + $constforvar = 'WEBPORTAL_'.strtoupper($myTmpObjectKey).'_ADDON_PDF'; + if (getDolGlobalString($constforvar) == $name) { + //print img_picto($langs->trans("Default"), 'on'); + // Even if choice is the default value, we allow to disable it. Replace this with previous line if you need to disable unset + print 'scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').''; + } else { + print 'scandir).'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').''; + } + print ''; + print $form->textwithpicto('', $htmltooltip, 1, 0); + print ''; + if ($module->type == 'pdf') { + $newname = preg_replace('/_'.preg_quote(strtolower($myTmpObjectKey), '/').'/', '', $name); + print ''.img_object($langs->trans("Preview"), 'pdf').''; + } else { + print img_object($langs->trans("PreviewNotAvailable"), 'generic'); + } + print '
'; + } +} + +if (empty($setupnotempty)) { + print '
'.$langs->trans("NothingToSetup"); +} + +// Page end +print dol_get_fiche_end(); + +llxFooter(); +$db->close(); diff --git a/htdocs/webportal/backport/v16/core/class/html.formsetup.class.php b/htdocs/webportal/backport/v16/core/class/html.formsetup.class.php new file mode 100644 index 0000000000000..fd1944e91bd78 --- /dev/null +++ b/htdocs/webportal/backport/v16/core/class/html.formsetup.class.php @@ -0,0 +1,1398 @@ + + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/** + * This class help you create setup render + */ +class FormSetup +{ + /** + * @var DoliDB Database handler. + */ + public $db; + + /** @var FormSetupItem[] */ + public $items = array(); + + /** + * @var int + */ + public $setupNotEmpty = 0; + + /** @var Translate */ + public $langs; + + /** @var Form */ + public $form; + + /** @var int */ + protected $maxItemRank; + + /** + * this is an html string display before output form + * @var string + */ + public $htmlBeforeOutputForm = ''; + + /** + * this is an html string display after output form + * @var string + */ + public $htmlAfterOutputForm = ''; + + /** + * this is an html string display on buttons zone + * @var string + */ + public $htmlOutputMoreButton = ''; + + + /** + * + * @var array + */ + public $formAttributes = array( + 'action' => '', // set in __construct + 'method' => 'POST' + ); + + /** + * an list of hidden inputs used only in edit mode + * @var array + */ + public $formHiddenInputs = array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + * @param Translate $outputLangs if needed can use another lang + */ + public function __construct($db, $outputLangs = false) + { + global $langs; + $this->db = $db; + $this->form = new Form($this->db); + $this->formAttributes['action'] = $_SERVER["PHP_SELF"]; + + $this->formHiddenInputs['token'] = newToken(); + $this->formHiddenInputs['action'] = 'update'; + + + if ($outputLangs) { + $this->langs = $outputLangs; + } else { + $this->langs = $langs; + } + } + + /** + * Generate an attributes string form an input array + * + * @param array $attributes an array of attributes keys and values, + * @return string attribute string + */ + static public function generateAttributesStringFromArray($attributes) + { + $Aattr = array(); + if (is_array($attributes)) { + foreach ($attributes as $attribute => $value) { + if (is_array($value) || is_object($value)) { + continue; + } + $Aattr[] = $attribute.'="'.dol_escape_htmltag($value).'"'; + } + } + + return !empty($Aattr)?implode(' ', $Aattr):''; + } + + + /** + * generateOutput + * + * @param bool $editMode true will display output on edit mod + * @return string html output + */ + public function generateOutput($editMode = false) + { + global $hookmanager, $action, $langs; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; + + $parameters = array( + 'editMode' => $editMode + ); + $reshook = $hookmanager->executeHooks('formSetupBeforeGenerateOutput', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + + if ($reshook > 0) { + return $hookmanager->resPrint; + } else { + $out = ''; + $out.= $this->htmlBeforeOutputForm; + + if ($editMode) { + $out.= '
formAttributes) . ' >'; + + // generate hidden values from $this->formHiddenInputs + if (!empty($this->formHiddenInputs) && is_array($this->formHiddenInputs)) { + foreach ($this->formHiddenInputs as $hiddenKey => $hiddenValue) { + $out.= ''; + } + } + } + + // generate output table + $out .= $this->generateTableOutput($editMode); + + + $reshook = $hookmanager->executeHooks('formSetupBeforeGenerateOutputButton', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + + if ($reshook > 0) { + return $hookmanager->resPrint; + } elseif ($editMode) { + $out .= '
'; // Todo : remove this
by adding style to form-setup-button-container css class in all themes + $out .= '
'; // Todo : remove .center by adding style to form-setup-button-container css class in all themes + $out.= $this->htmlOutputMoreButton; + $out .= ''; // Todo fix dolibarr style for
'; + } + + if ($editMode) { + $out .= '
'; + } + + $out.= $this->htmlAfterOutputForm; + + return $out; + } + } + + /** + * generateTableOutput + * + * @param bool $editMode true will display output on edit mod + * @return string html output + */ + public function generateTableOutput($editMode = false) + { + global $hookmanager, $action; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; + + $parameters = array( + 'editMode' => $editMode + ); + $reshook = $hookmanager->executeHooks('formSetupBeforeGenerateTableOutput', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + + if ($reshook > 0) { + return $hookmanager->resPrint; + } else { + $out = ''; + $out .= ''; + $out .= ''; + $out .= ' '; + $out .= ' '; + $out .= ''; + $out .= ''; + + // Sort items before render + $this->sortingItems(); + + $out .= ''; + foreach ($this->items as $item) { + $out .= $this->generateLineOutput($item, $editMode); + } + $out .= ''; + + $out .= '
' . $this->langs->trans("Parameter") . '' . $this->langs->trans("Value") . '
'; + return $out; + } + } + + /** + * saveConfFromPost + * + * @param bool $noMessageInUpdate display event message on errors and success + * @return int -1 if KO, 1 if OK + */ + public function saveConfFromPost($noMessageInUpdate = false) + { + global $hookmanager, $conf; + + $parameters = array(); + $reshook = $hookmanager->executeHooks('formSetupBeforeSaveConfFromPost', $parameters, $this); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + $this->setErrors($hookmanager->errors); + return -1; + } + + if ($reshook > 0) { + return $reshook; + } + + + if (empty($this->items)) { + return null; + } + + $this->db->begin(); + $error = 0; + foreach ($this->items as $item) { + if ($item->getType() == 'yesno' && !empty($conf->use_javascript_ajax)) { + continue; + } + + $res = $item->setValueFromPost(); + if ($res > 0) { + $item->saveConfValue(); + } elseif ($res < 0) { + $error++; + break; + } + } + + if (!$error) { + $this->db->commit(); + if (empty($noMessageInUpdate)) { + setEventMessages($this->langs->trans("SetupSaved"), null); + } + return 1; + } else { + $this->db->rollback(); + if (empty($noMessageInUpdate)) { + setEventMessages($this->langs->trans("SetupNotSaved"), null, 'errors'); + } + return -1; + } + } + + /** + * generateLineOutput + * + * @param FormSetupItem $item the setup item + * @param bool $editMode Display as edit mod + * @return string the html output for an setup item + */ + public function generateLineOutput($item, $editMode = false) + { + + $out = ''; + if ($item->enabled==1) { + $trClass = 'oddeven'; + if ($item->getType() == 'title') { + $trClass = 'liste_titre'; + } + + $this->setupNotEmpty++; + $out.= ''; + + $out.= ''; + $out.= ''; + $out.= $this->form->textwithpicto($item->getNameText(), $item->getHelpText(), 1, 'info', '', 0, 3, 'tootips'.$item->confKey); + $out.= ''; + $out.= ''; + + $out.= ''; + + if ($editMode) { + $out.= $item->generateInputField(); + } else { + $out.= $item->generateOutputField(); + } + + if (!empty($item->errors)) { + // TODO : move set event message in a methode to be called by cards not by this class + setEventMessages(null, $item->errors, 'errors'); + } + + $out.= ''; + $out.= ''; + } + + return $out; + } + + + /** + * Method used to test module builder convertion to this form usage + * + * @param array $params an array of arrays of params from old modulBuilder params + * @return boolean + */ + public function addItemsFromParamsArray($params) + { + if (!is_array($params) || empty($params)) { return false; } + foreach ($params as $confKey => $param) { + $this->addItemFromParams($confKey, $param); // todo manage error + } + return true; + } + + + /** + * From old + * Method was used to test module builder convertion to this form usage. + * + * @param string $confKey the conf name to store + * @param array $params an array of params from old modulBuilder params + * @return bool + */ + public function addItemFromParams($confKey, $params) + { + if (empty($confKey) || empty($params['type'])) { return false; } + + /* + * Exemple from old module builder setup page + * // 'WEBPORTALBIS_MYPARAM1'=>array('type'=>'string', 'css'=>'minwidth500' ,'enabled'=>1), + // 'WEBPORTALBIS_MYPARAM2'=>array('type'=>'textarea','enabled'=>1), + //'WEBPORTALBIS_MYPARAM3'=>array('type'=>'category:'.Categorie::TYPE_CUSTOMER, 'enabled'=>1), + //'WEBPORTALBIS_MYPARAM4'=>array('type'=>'emailtemplate:thirdparty', 'enabled'=>1), + //'WEBPORTALBIS_MYPARAM5'=>array('type'=>'yesno', 'enabled'=>1), + //'WEBPORTALBIS_MYPARAM5'=>array('type'=>'thirdparty_type', 'enabled'=>1), + //'WEBPORTALBIS_MYPARAM6'=>array('type'=>'securekey', 'enabled'=>1), + //'WEBPORTALBIS_MYPARAM7'=>array('type'=>'product', 'enabled'=>1), + */ + + $item = new FormSetupItem($confKey); + // need to be ignored from scrutinizer setTypeFromTypeString was created as deprecated to incite developper to use object oriented usage + /** @scrutinizer ignore-deprecated */ $item->setTypeFromTypeString($params['type']); + + if (!empty($params['enabled'])) { + $item->enabled = $params['enabled']; + } + + if (!empty($params['css'])) { + $item->cssClass = $params['css']; + } + + $this->items[$item->confKey] = $item; + + return true; + } + + /** + * Used to export param array for /core/actions_setmoduleoptions.inc.php template + * Method exists only for manage setup convertion + * + * @return array $arrayofparameters for /core/actions_setmoduleoptions.inc.php + */ + public function exportItemsAsParamsArray() + { + $arrayofparameters = array(); + foreach ($this->items as $item) { + $arrayofparameters[$item->confKey] = array( + 'type' => $item->getType(), + 'enabled' => $item->enabled + ); + } + + return $arrayofparameters; + } + + /** + * Reload for each item default conf + * note: this will override custom configuration + * + * @return bool + */ + public function reloadConfs() + { + + if (!array($this->items)) { return false; } + foreach ($this->items as $item) { + $item->loadValueFromConf(); + } + + return true; + } + + + /** + * Create a new item + * the tagret is useful with hooks : that allow externals modules to add setup items on good place + * + * @param string $confKey the conf key used in database + * @param string $targetItemKey target item used to place the new item beside + * @param bool $insertAfterTarget insert before or after target item ? + * @return FormSetupItem the new setup item created + */ + public function newItem($confKey, $targetItemKey = false, $insertAfterTarget = false) + { + $item = new FormSetupItem($confKey); + + // set item rank if not defined as last item + if (empty($item->rank)) { + $item->rank = $this->getCurentItemMaxRank() + 1; + $this->setItemMaxRank($item->rank); // set new max rank if needed + } + + // try to get rank from target column, this will override item->rank + if (!empty($targetItemKey)) { + if (isset($this->items[$targetItemKey])) { + $targetItem = $this->items[$targetItemKey]; + $item->rank = $targetItem->rank; // $targetItem->rank will be increase after + if ($targetItem->rank >= 0 && $insertAfterTarget) { + $item->rank++; + } + } + + // calc new rank for each item to make place for new item + foreach ($this->items as $fItem) { + if ($item->rank <= $fItem->rank) { + $fItem->rank = $fItem->rank + 1; + $this->setItemMaxRank($fItem->rank); // set new max rank if needed + } + } + } + + $this->items[$item->confKey] = $item; + return $this->items[$item->confKey]; + } + + /** + * Sort items according to rank + * + * @return bool + */ + public function sortingItems() + { + // Sorting + return uasort($this->items, array($this, 'itemSort')); + } + + /** + * getCurentItemMaxRank + * + * @param bool $cache To use cache or not + * @return int + */ + public function getCurentItemMaxRank($cache = true) + { + if (empty($this->items)) { + return 0; + } + + if ($cache && $this->maxItemRank > 0) { + return $this->maxItemRank; + } + + $this->maxItemRank = 0; + foreach ($this->items as $item) { + $this->maxItemRank = max($this->maxItemRank, $item->rank); + } + + return $this->maxItemRank; + } + + + /** + * set new max rank if needed + * + * @param int $rank the item rank + * @return int|void new max rank + */ + public function setItemMaxRank($rank) + { + $this->maxItemRank = max($this->maxItemRank, $rank); + } + + + /** + * get item position rank from item key + * + * @param string $itemKey the item key + * @return int rank on success and -1 on error + */ + public function getLineRank($itemKey) + { + if (!isset($this->items[$itemKey]->rank)) { + return -1; + } + return $this->items[$itemKey]->rank; + } + + + /** + * uasort callback function to Sort params items + * + * @param FormSetupItem $a formSetup item + * @param FormSetupItem $b formSetup item + * @return int Return compare result + */ + public function itemSort(FormSetupItem $a, FormSetupItem $b) + { + if (empty($a->rank)) { + $a->rank = 0; + } + if (empty($b->rank)) { + $b->rank = 0; + } + if ($a->rank == $b->rank) { + return 0; + } + return ($a->rank < $b->rank) ? -1 : 1; + } +} + +/** + * This class help to create item for class formSetup + */ +class FormSetupItem +{ + /** + * @var DoliDB Database handler. + */ + public $db; + + /** @var Translate */ + public $langs; + + /** @var int */ + public $entity; + + /** @var Form */ + public $form; + + /** @var string $confKey the conf key used in database */ + public $confKey; + + /** @var string|false $nameText */ + public $nameText = false; + + /** @var string $helpText */ + public $helpText = ''; + + /** @var string $fieldValue */ + public $fieldValue; + + /** @var string $defaultFieldValue */ + public $defaultFieldValue = null; + + /** @var array $fieldAttr fields attribute only for compatible fields like input text */ + public $fieldAttr = array(); + + /** @var bool|string set this var to override field output will override $fieldInputOverride and $fieldOutputOverride too */ + public $fieldOverride = false; + + /** @var bool|string set this var to override field input */ + public $fieldInputOverride = false; + + /** @var bool|string set this var to override field output */ + public $fieldOutputOverride = false; + + /** @var int $rank */ + public $rank = 0; + + /** @var array set this var for options on select and multiselect items */ + public $fieldOptions = array(); + + /** @var callable $saveCallBack */ + public $saveCallBack; + + /** @var callable $setValueFromPostCallBack */ + public $setValueFromPostCallBack; + + /** + * @var string $errors + */ + public $errors = array(); + + /** + * TODO each type must have setAs{type} method to help configuration + * And set var as protected when its done configuration must be done by method + * this is important for retrocompatibility of futures versions + * @var string $type 'string', 'textarea', 'category:'.Categorie::TYPE_CUSTOMER', 'emailtemplate', 'thirdparty_type' + */ + protected $type = 'string'; + + public $enabled = 1; + + public $cssClass = ''; + + /** + * Constructor + * + * @param string $confKey the conf key used in database + */ + public function __construct($confKey) + { + global $langs, $db, $conf, $form; + $this->db = $db; + + if (!empty($form) && is_object($form) && get_class($form) == 'Form') { // the form class has a cache inside so I am using it to optimize + $this->form = $form; + } else { + $this->form = new Form($this->db); + } + + $this->langs = $langs; + $this->entity = $conf->entity; + + $this->confKey = $confKey; + $this->loadValueFromConf(); + } + + /** + * load conf value from databases + * @return bool + */ + public function loadValueFromConf() + { + global $conf; + if (isset($conf->global->{$this->confKey})) { + $this->fieldValue = getDolGlobalString($this->confKey); + return true; + } else { + $this->fieldValue = null; + return false; + } + } + + /** + * reload conf value from databases is an aliase of loadValueFromConf + * @deprecated + * @return bool + */ + public function reloadValueFromConf() + { + return $this->loadValueFromConf(); + } + + + /** + * Save const value based on htdocs/core/actions_setmoduleoptions.inc.php + * + * @return int -1 if KO, 1 if OK + */ + public function saveConfValue() + { + global $hookmanager; + + $parameters = array(); + $reshook = $hookmanager->executeHooks('formSetupBeforeSaveConfValue', $parameters, $this); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + $this->setErrors($hookmanager->errors); + return -1; + } + + if ($reshook > 0) { + return $reshook; + } + + + if (!empty($this->saveCallBack) && is_callable($this->saveCallBack)) { + return call_user_func($this->saveCallBack, $this); + } + + // Modify constant only if key was posted (avoid resetting key to the null value) + if ($this->type != 'title') { + $result = dolibarr_set_const($this->db, $this->confKey, $this->fieldValue, 'chaine', 0, '', $this->entity); + if ($result < 0) { + return -1; + } else { + return 1; + } + } + + return 0; + } + + /** + * Set an override function for saving data + * + * @param callable $callBack a callable function + * @return void + */ + public function setSaveCallBack(callable $callBack) + { + $this->saveCallBack = $callBack; + } + + /** + * Set an override function for get data from post + * @param callable $callBack a callable function + * @return void + */ + public function setValueFromPostCallBack(callable $callBack) + { + $this->setValueFromPostCallBack = $callBack; + } + + /** + * Save const value based on htdocs/core/actions_setmoduleoptions.inc.php + * @return int -1 if KO, 0 nothing to do , 1 if OK + */ + public function setValueFromPost() + { + if (!empty($this->setValueFromPostCallBack) && is_callable($this->setValueFromPostCallBack)) { + return call_user_func($this->setValueFromPostCallBack); + } + + // Modify constant only if key was posted (avoid resetting key to the null value) + if ($this->type != 'title') { + if (preg_match('/category:/', $this->type)) { + if (GETPOST($this->confKey, 'int') == '-1') { + $val_const = ''; + } else { + $val_const = GETPOST($this->confKey, 'int'); + } + } elseif ($this->type == 'multiselect') { + $val = GETPOST($this->confKey, 'array'); + if ($val && is_array($val)) { + $val_const = implode(',', $val); + } else { + $val_const = ''; + } + } elseif ($this->type == 'html') { + $val_const = GETPOST($this->confKey, 'restricthtml'); + } else { + $val_const = GETPOST($this->confKey, 'alpha'); + } + + // TODO add value check with class validate + $this->fieldValue = $val_const; + + return 1; + } + + return 0; + } + + /** + * Get help text or generate it + * @return int|string + */ + public function getHelpText() + { + if (!empty($this->helpText)) { return $this->helpText; } + return (($this->langs->trans($this->confKey . 'Tooltip') != $this->confKey . 'Tooltip') ? $this->langs->trans($this->confKey . 'Tooltip') : ''); + } + + /** + * Get field name text or generate it + * @return false|int|string + */ + public function getNameText() + { + if (!empty($this->nameText)) { return $this->nameText; } + return (($this->langs->trans($this->confKey) != $this->confKey) ? $this->langs->trans($this->confKey) : $this->langs->trans('MissingTranslationForConfKey', $this->confKey)); + } + + /** + * generate input field + * @return bool|string + */ + public function generateInputField() + { + global $conf; + + if (!empty($this->fieldOverride)) { + return $this->fieldOverride; + } + + if (!empty($this->fieldInputOverride)) { + return $this->fieldInputOverride; + } + + // Set default value + if (is_null($this->fieldValue)) { + $this->fieldValue = $this->defaultFieldValue; + } + + + $this->fieldAttr['name'] = $this->confKey; + $this->fieldAttr['id'] = 'setup-'.$this->confKey; + $this->fieldAttr['value'] = $this->fieldValue; + + $out = ''; + + if ($this->type == 'title') { + $out.= $this->generateOutputField(); // title have no input + } elseif ($this->type == 'multiselect') { + $out.= $this->generateInputFieldMultiSelect(); + } elseif ($this->type == 'select') { + $out.= $this->generateInputFieldSelect(); + } elseif ($this->type == 'selectUser') { + $out.= $this->generateInputFieldSelectUser(); + } elseif ($this->type == 'textarea') { + $out.= $this->generateInputFieldTextarea(); + } elseif ($this->type== 'html') { + $out.= $this->generateInputFieldHtml(); + } elseif ($this->type== 'color') { + $out.= $this->generateInputFieldColor(); + } elseif ($this->type == 'yesno') { + if (!empty($conf->use_javascript_ajax)) { + $out.= ajax_constantonoff($this->confKey); + } else { + $out.= $this->form->selectyesno($this->confKey, $this->fieldValue, 1); + } + } elseif (preg_match('/emailtemplate:/', $this->type)) { + $out.= $this->generateInputFieldEmailTemplate(); + } elseif (preg_match('/category:/', $this->type)) { + $out.=$this->generateInputFieldCategories(); + } elseif (preg_match('/thirdparty_type/', $this->type)) { + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; + $formcompany = new FormCompany($this->db); + $out.= $formcompany->selectProspectCustomerType($this->fieldValue, $this->confKey); + } elseif ($this->type == 'securekey') { + $out.= $this->generateInputFieldSecureKey(); + } elseif ($this->type == 'product') { + if (isModEnabled("product") || isModEnabled("service")) { + $selected = (empty($this->fieldValue) ? '' : $this->fieldValue); + $out.= $this->form->select_produits($selected, $this->confKey, '', 0, 0, 1, 2, '', 0, array(), 0, '1', 0, $this->cssClass, 0, '', null, 1); + } + } else { + $out.= $this->generateInputFieldText(); + } + + return $out; + } + + /** + * generatec default input field + * @return string + */ + public function generateInputFieldText() + { + if (empty($this->fieldAttr)) { $this->fieldAttr['class'] = 'flat '.(empty($this->cssClass) ? 'minwidth200' : $this->cssClass); } + return 'fieldAttr).' />'; + } + + /** + * generate input field for textarea + * @return string + */ + public function generateInputFieldTextarea() + { + $out = '\n"; + return $out; + } + + /** + * generate input field for html + * @return string + */ + public function generateInputFieldHtml() + { + global $conf; + require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + $doleditor = new DolEditor($this->confKey, $this->fieldValue, '', 160, 'dolibarr_notes', '', false, false, isModEnabled('fckeditor'), ROWS_5, '90%'); + return $doleditor->Create(1); + } + + /** + * generate input field for categories + * @return string + */ + public function generateInputFieldCategories() + { + global $conf; + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; + $formother = new FormOther($this->db); + + $tmp = explode(':', $this->type); + $out= img_picto('', 'category', 'class="pictofixedwidth"'); + $out.= $formother->select_categories($tmp[1], $this->fieldValue, $this->confKey, 0, $this->langs->trans('CustomersProspectsCategoriesShort')); + return $out; + } + + /** + * generate input field for email template selector + * @return string + */ + public function generateInputFieldEmailTemplate() + { + global $conf, $user; + $out = ''; + if (preg_match('/emailtemplate:/', $this->type)) { + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($this->db); + + $tmp = explode(':', $this->type); + $nboftemplates = $formmail->fetchAllEMailTemplate($tmp[1], $user, null, 1); // We set lang=null to get in priority record with no lang + $arrayOfMessageName = array(); + if (is_array($formmail->lines_model)) { + foreach ($formmail->lines_model as $modelMail) { + $moreonlabel = ''; + if (!empty($arrayOfMessageName[$modelMail->label])) { + $moreonlabel = ' (' . $this->langs->trans("SeveralLangugeVariatFound") . ')'; + } + // The 'label' is the key that is unique if we exclude the language + $arrayOfMessageName[$modelMail->id] = $this->langs->trans(preg_replace('/\(|\)/', '', $modelMail->label)) . $moreonlabel; + } + } + $out .= $this->form->selectarray($this->confKey, $arrayOfMessageName, $this->fieldValue, 'None', 0, 0, '', 0, 0, 0, '', '', 1); + } + + return $out; + } + + + /** + * generate input field for secure key + * @return string + */ + public function generateInputFieldSecureKey() + { + global $conf; + $out = ''; + if (!empty($conf->use_javascript_ajax)) { + $out.= ' '.img_picto($this->langs->trans('Generate'), 'refresh', 'id="generate_token'.$this->confKey.'" class="linkobject"'); + } + + // Add button to autosuggest a key + include_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; + $out .= dolJSToSetRandomPassword($this->confKey, 'generate_token'.$this->confKey); + + return $out; + } + + + /** + * @return string + */ + public function generateInputFieldMultiSelect() + { + $TSelected = array(); + if ($this->fieldValue) { + $TSelected = explode(',', $this->fieldValue); + } + + return $this->form->multiselectarray($this->confKey, $this->fieldOptions, $TSelected, 0, 0, '', 0, 0, 'style="min-width:100px"'); + } + + + /** + * @return string + */ + public function generateInputFieldSelect() + { + return $this->form->selectarray($this->confKey, $this->fieldOptions, $this->fieldValue); + } + + /** + * @return string + */ + public function generateInputFieldSelectUser() + { + return $this->form->select_dolusers($this->fieldValue, $this->confKey); + } + + /** + * get the type : used for old module builder setup conf style conversion and tests + * because this two class will quickly evolve it's important to not set or get directly $this->type (will be protected) so this method exist + * to be sure we can manage evolution easily + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * set the type from string : used for old module builder setup conf style conversion and tests + * because this two class will quickly evolve it's important to not set directly $this->type (will be protected) so this method exist + * to be sure we can manage evolution easily + * + * @param string $type possible values based on old module builder setup : 'string', 'textarea', 'category:'.Categorie::TYPE_CUSTOMER', 'emailtemplate', 'thirdparty_type' + * @deprecated yes this setTypeFromTypeString came deprecated because it exists only for manage setup convertion + * @return bool + */ + public function setTypeFromTypeString($type) + { + $this->type = $type; + + return true; + } + + /** + * Add error + * + * @param array|string $errors the error text + * @return null + */ + public function setErrors($errors) + { + if (is_array($errors)) { + if (!empty($errors)) { + foreach ($errors as $error) { + $this->setErrors($error); + } + } + } elseif (!empty($errors)) { + $this->errors[] = $errors; + } + } + + /** + * generateOutputField + * + * @return bool|string Generate the output html for this item + */ + public function generateOutputField() + { + global $conf, $user, $langs; + + if (!empty($this->fieldOverride)) { + return $this->fieldOverride; + } + + if (!empty($this->fieldOutputOverride)) { + return $this->fieldOutputOverride; + } + + $out = ''; + + if ($this->type == 'title') { + // nothing to do + } elseif ($this->type == 'textarea') { + $out.= dol_nl2br($this->fieldValue); + } elseif ($this->type == 'multiselect') { + $out.= $this->generateOutputFieldMultiSelect(); + } elseif ($this->type == 'select') { + $out.= $this->generateOutputFieldSelect(); + } elseif ($this->type == 'selectUser') { + $out.= $this->generateOutputFieldSelectUser(); + } elseif ($this->type== 'html') { + $out.= $this->fieldValue; + } elseif ($this->type== 'color') { + $out.= $this->generateOutputFieldColor(); + } elseif ($this->type == 'yesno') { + if (!empty($conf->use_javascript_ajax)) { + $out.= ajax_constantonoff($this->confKey); + } else { + if ($this->fieldValue == 1) { + $out.= $langs->trans('yes'); + } else { + $out.= $langs->trans('no'); + } + } + } elseif (preg_match('/emailtemplate:/', $this->type)) { + if ($this->fieldValue > 0) { + include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php'; + $formmail = new FormMail($this->db); + + $tmp = explode(':', $this->type); + + $template = $formmail->getEMailTemplate($this->db, $tmp[1], $user, $this->langs, $this->fieldValue); + if (is_numeric($template) && $template < 0) { + $this->setErrors($formmail->errors); + } + $out.= $this->langs->trans($template->label); + } + } elseif (preg_match('/category:/', $this->type)) { + require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $result = $c->fetch($this->fieldValue); + if ($result < 0) { + $this->setErrors($c->errors); + } + $ways = $c->print_all_ways(' >> ', 'none', 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text + $toprint = array(); + foreach ($ways as $way) { + $toprint[] = '
  • color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '
  • '; + } + $out.='
      ' . implode(' ', $toprint) . '
    '; + } elseif (preg_match('/thirdparty_type/', $this->type)) { + if ($this->fieldValue==2) { + $out.= $this->langs->trans("Prospect"); + } elseif ($this->fieldValue==3) { + $out.= $this->langs->trans("ProspectCustomer"); + } elseif ($this->fieldValue==1) { + $out.= $this->langs->trans("Customer"); + } elseif ($this->fieldValue==0) { + $out.= $this->langs->trans("NorProspectNorCustomer"); + } + } elseif ($this->type == 'product') { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + + $product = new Product($this->db); + $resprod = $product->fetch($this->fieldValue); + if ($resprod > 0) { + $out.= $product->ref; + } elseif ($resprod < 0) { + $this->setErrors($product->errors); + } + } else { + $out.= $this->fieldValue; + } + + return $out; + } + + + /** + * generateOutputFieldMultiSelect + * + * @return string + */ + public function generateOutputFieldMultiSelect() + { + $outPut = ''; + $TSelected = array(); + if (!empty($this->fieldValue)) { + $TSelected = explode(',', $this->fieldValue); + } + + if (!empty($TSelected)) { + foreach ($TSelected as $selected) { + if (!empty($this->fieldOptions[$selected])) { + $outPut.= dolGetBadge('', $this->fieldOptions[$selected], 'info').' '; + } + } + } + return $outPut; + } + + /** + * generateOutputFieldColor + * + * @return string + */ + public function generateOutputFieldColor() + { + $this->fieldAttr['disabled']=null; + return $this->generateInputField(); + } + /** + * generateInputFieldColor + * + * @return string + */ + public function generateInputFieldColor() + { + $this->fieldAttr['type']= 'color'; + return $this->generateInputFieldText(); + } + + /** + * generateOutputFieldSelect + * + * @return string + */ + public function generateOutputFieldSelect() + { + $outPut = ''; + if (!empty($this->fieldOptions[$this->fieldValue])) { + $outPut = $this->fieldOptions[$this->fieldValue]; + } + + return $outPut; + } + + /** + * generateOutputFieldSelectUser + * + * @return string + */ + public function generateOutputFieldSelectUser() + { + $outPut = ''; + $user = new User($this->db); + $user->fetch($this->fieldValue); + $outPut = $user->firstname . " " . $user->lastname; + return $outPut; + } + + /* + * METHODS FOR SETTING DISPLAY TYPE + */ + + /** + * Set type of input as string + * + * @return self + */ + public function setAsString() + { + $this->type = 'string'; + return $this; + } + + /** + * Set type of input as color + * + * @return self + */ + public function setAsColor() + { + $this->type = 'color'; + return $this; + } + + /** + * Set type of input as textarea + * + * @return self + */ + public function setAsTextarea() + { + $this->type = 'textarea'; + return $this; + } + + /** + * Set type of input as html editor + * + * @return self + */ + public function setAsHtml() + { + $this->type = 'html'; + return $this; + } + + /** + * Set type of input as emailtemplate selector + * + * @param string $templateType email template type + * @return self + */ + public function setAsEmailTemplate($templateType) + { + $this->type = 'emailtemplate:'.$templateType; + return $this; + } + + /** + * Set type of input as thirdparty_type selector + * + * @return self + */ + public function setAsThirdpartyType() + { + $this->type = 'thirdparty_type'; + return $this; + } + + /** + * Set type of input as Yes + * + * @return self + */ + public function setAsYesNo() + { + $this->type = 'yesno'; + return $this; + } + + /** + * Set type of input as secure key + * + * @return self + */ + public function setAsSecureKey() + { + $this->type = 'securekey'; + return $this; + } + + /** + * Set type of input as product + * + * @return self + */ + public function setAsProduct() + { + $this->type = 'product'; + return $this; + } + + /** + * Set type of input as a category selector + * TODO add default value + * + * @param int $catType Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated. + * @return self + */ + public function setAsCategory($catType) + { + $this->type = 'category:'.$catType; + return $this; + } + + /** + * Set type of input as a simple title. No data to store + * + * @return self + */ + public function setAsTitle() + { + $this->type = 'title'; + return $this; + } + + + /** + * Set type of input as a simple title. No data to store + * + * @param array $fieldOptions A table of field options + * @return self + */ + public function setAsMultiSelect($fieldOptions) + { + if (is_array($fieldOptions)) { + $this->fieldOptions = $fieldOptions; + } + + $this->type = 'multiselect'; + return $this; + } + + /** + * Set type of input as a simple title. No data to store + * + * @param array $fieldOptions A table of field options + * @return self + */ + public function setAsSelect($fieldOptions) + { + if (is_array($fieldOptions)) { + $this->fieldOptions = $fieldOptions; + } + + $this->type = 'select'; + return $this; + } + + /** + * Set type of input as a simple title. No data to store + * + * @return self + */ + public function setAsSelectUser() + { + $this->type = 'selectUser'; + return $this; + } +} diff --git a/htdocs/webportal/build/makepack-webportal.conf b/htdocs/webportal/build/makepack-webportal.conf new file mode 100644 index 0000000000000..16dc1e7b82b4e --- /dev/null +++ b/htdocs/webportal/build/makepack-webportal.conf @@ -0,0 +1,11 @@ +# Your module name here +# +# Goal: Goal of module +# Version: +# Author: Copyright - +# License: GPLv3 +# Install: Just unpack content of module package in Dolibarr directory. +# Setup: Go on Dolibarr setup - modules to enable module. +# +# Files in module +mymodule/ \ No newline at end of file diff --git a/htdocs/webportal/class/webportalinvoice.class.php b/htdocs/webportal/class/webportalinvoice.class.php new file mode 100644 index 0000000000000..af673ddcd3a74 --- /dev/null +++ b/htdocs/webportal/class/webportalinvoice.class.php @@ -0,0 +1,340 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/webportalinvoice.class.php + * \ingroup webportal + * \brief This file is a CRUD class file for WebPortalInvoice (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + +/** + * Class for WebPortalInvoice + */ +class WebPortalInvoice extends Facture +{ + /** + * @var string ID of module. + */ + public $module = 'webportal'; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * Status list (short label) + */ + const status_short_list = array( + Facture::STATUS_DRAFT => 'BillShortStatusDraft', + Facture::STATUS_VALIDATED => 'BillShortStatusNotPaid', + Facture::STATUS_CLOSED => 'BillShortStatusPaid', + Facture::STATUS_ABANDONED => 'BillShortStatusCanceled', + ); + + /** + * @var Facture Invoice for static methods + */ + protected $invoice_static = null; + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]', + * 'chkbxlst:...', + * 'varchar(x)', + * 'text', 'text:none', 'html', + * 'double(24,8)', 'real', 'price', + * 'date', 'datetime', 'timestamp', 'duration', + * 'boolean', 'checkbox', 'radio', 'array', + * 'mail', 'phone', 'url', 'password', 'ip' + * Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt('MY_SETUP_PARAM') or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>1,), + 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>20, 'index'=>1,), + 'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>2, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>5,), + 'type' =>array('type'=>'smallint(6)', 'label'=>'Type', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>15,), + 'datef' =>array('type'=>'date', 'label'=>'DateInvoice', 'enabled'=>1, 'visible'=>2, 'position'=>20,), + 'date_lim_reglement' =>array('type'=>'date', 'label'=>'DateDue', 'enabled'=>1, 'visible'=>2, 'position'=>25,), + 'paye' =>array('type'=>'smallint(6)', 'label'=>'InvoicePaidCompletely', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>80,), + 'total_ht' =>array('type'=>'price', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>2, 'position'=>95, 'isameasure'=>1,), + 'total_tva' =>array('type'=>'price', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>2, 'position'=>100, 'isameasure'=>1,), + 'total_ttc' =>array('type'=>'price', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>2, 'position'=>130, 'isameasure'=>1,), + 'multicurrency_total_ht' =>array('type'=>'price', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>290, 'isameasure'=>1,), + 'multicurrency_total_tva' =>array('type'=>'price', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>291, 'isameasure'=>1,), + 'multicurrency_total_ttc' =>array('type'=>'price', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>292, 'isameasure'=>1,), + 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>2, 'notnull'=>1, 'position'=>1000, 'arrayofkeyval' => self::status_short_list,), + ); + //public $rowid; + //public $ref; + public $datef; + //public $date_lim_reglement; + //public $total_ht; + //public $total_tva; + //public $total_ttc; + //public $multicurrency_total_ht; + //public $multicurrency_total_tva; + //public $multicurrency_total_ttc; + public $fk_statut; + // END MODULEBUILDER PROPERTIES + + + /** + * Get invoice for static methods + * + * @return Facture + */ + protected function getInvoiceStatic() + { + if (!$this->invoice_static) { + $this->invoice_static= new Facture($this->db); + } + + return $this->invoice_static; + } + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + $this->db = $db; + + $this->getInvoiceStatic(); + } + + /** + * getTooltipContentArray + * + * @param array $params ex option, infologin + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $langs; + + $langs->load('bills'); + + $datas = []; + + return $datas; + } + + /** + * Return clicable link of object (with eventually picto) + * + * @param int $withpicto Add picto into link + * @param string $option Where point the link + * @param int $max Maxlength of ref + * @param int $short 1=Return just URL + * @param string $moretitle Add more text to title tooltip + * @param int $notooltip 1=Disable tooltip + * @param int $addlinktonotes 1=Add link to notes + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @param string $target Target of link ('', '_self', '_blank', '_parent', '_backoffice', ...) + * @return string String with URL + */ + public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $addlinktonotes = 0, $save_lastsearch_value = -1, $target = '') + { + global $langs, $conf; + + if (!empty($conf->dol_no_mouse_hover)) { + $notooltip = 1; // Force disable tooltips + } + + $result = ''; + + $url = ''; + $option = 'nolink'; + + if ($short) { + return $url; + } + + $picto = $this->picto; + if ($this->type == self::TYPE_REPLACEMENT) { + $picto .= 'r'; // Replacement invoice + } + if ($this->type == self::TYPE_CREDIT_NOTE) { + $picto .= 'a'; // Credit note + } + if ($this->type == self::TYPE_DEPOSIT) { + $picto .= 'd'; // Deposit invoice + } + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'moretitle' => $moretitle, + 'option' => $option, + ]; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"'; + $label = ''; + } else { + $label = implode($this->getTooltipContentArray($params)); + } + + $linkclose = ($target ? ' target="'.$target.'"' : ''); + + $linkstart = ''; + $linkend = ''; + + if ($option == 'nolink') { + $linkstart = ''; + $linkend = ''; + } + + $result .= $linkstart; + if ($withpicto) { + $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1); + } + if ($withpicto != 2) { + $result .= ($max ?dol_trunc($this->ref, $max) : $this->ref); + } + $result .= $linkend; + + global $action, $hookmanager; + $hookmanager->initHooks(array('invoicedao')); + $parameters = array('id'=>$this->id, 'getnomurl' => &$result, 'notooltip' => $notooltip, 'addlinktonotes' => $addlinktonotes, 'save_lastsearch_value'=> $save_lastsearch_value, 'target' => $target); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + $result = $hookmanager->resPrint; + } else { + $result .= $hookmanager->resPrint; + } + + return $result; + } + + /** + * Return clicable link of object (with eventually picto) + * + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param array $arraydata Array of data + * @return string HTML Code for Kanban thumb. + */ + public function getKanbanView($option = '', $arraydata = null) + { + $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); + + $return = '
    '; + $return .= '
    '; + $return .= ''; + $return .= img_picto('', $this->picto); + //$return .= ''; // Can be image + $return .= ''; + $return .= '
    '; + $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).''; + $return .= ''; + if (property_exists($this, 'socid')) { + $return .= '
    '.$this->socid.''; + } + if (property_exists($this, 'fk_user_author')) { + $return .= '
    '.$this->fk_user_author.''; + } + if (method_exists($this, 'getLibStatut')) { + $return .= '
    '.$this->getLibStatut(3).'
    '; + } + $return .= '
    '; + $return .= '
    '; + $return .= '
    '; + + return $return; + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLabelStatus($mode = 0) + { + return $this->LibStatut($this->paye, $this->fk_statut, $mode, -1, $this->type); + } + + /** + * Return label of object status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=Long label + picto + * @param int $alreadypaid 0=No payment already done, >0=Some payments were already done + * @return string Label of status + */ + public function getLibStatut($mode = 0, $alreadypaid = -1) + { + return $this->LibStatut($this->paye, $this->fk_statut, $mode, $alreadypaid, $this->type); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return label of a status + * + * @param int $paye Status field paye + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=long label + picto + * @param int $alreadypaid 0=No payment already done, >0=Some payments were already done + * @param int $type Type invoice. If -1, we use $this->type + * @return string Label of status + */ + public function LibStatut($paye, $status, $mode = 0, $alreadypaid = -1, $type = -1) + { + // phpcs:enable + return $this->getInvoiceStatic()->LibStatut($paye, $status, $mode, $alreadypaid, $type); + } +} diff --git a/htdocs/webportal/class/webportalmember.class.php b/htdocs/webportal/class/webportalmember.class.php new file mode 100644 index 0000000000000..9dd98e89008bc --- /dev/null +++ b/htdocs/webportal/class/webportalmember.class.php @@ -0,0 +1,533 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/webportalmember.class.php + * \ingroup webportal + * \brief This file is a CRUD class file for WebPortalMember (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; + +/** + * Class for WebPortalMember + */ +class WebPortalMember extends Adherent +{ + /** + * @var string ID of module. + */ + public $module = 'webportal'; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * Status list (short label) + */ + const status_short_list = array( + Adherent::STATUS_DRAFT => 'Draft', + Adherent::STATUS_VALIDATED => 'Validated', + Adherent::STATUS_RESILIATED => 'MemberStatusResiliatedShort', + Adherent::STATUS_EXCLUDED => 'MemberStatusExcludedShort', + ); + + /** + * MorPhy list : Moral or Physical + */ + const morphy_list = array( + 'phy' => 'Physical', + 'mor' => 'Moral', + ); + + /** + * Gender list + */ + const gender_list = array( + 'man' => 'Genderman', + 'woman' => 'Genderwoman', + 'other' => 'Genderother', + ); + + /** + * @var Adherent Member for static methods + */ + protected $member_static = null; + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'varchar(x)', + * 'text', 'html', + * 'double(24,8)', 'price', + * 'date', 'datetime', + * 'checkbox', 'radio', + * 'mail', 'phone', 'url', 'password' + * Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt('MY_SETUP_PARAM') or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * 'showonheader' is 1 to show on the top of the card (header section) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 10,), + 'ref' => array('type' => 'varchar(30)', 'label' => 'Ref', 'default' => 1, 'enabled' => 1, 'visible' => 5, 'notnull' => 1, 'position' => 12, 'index' => 1, 'showonheader' => 1,), + 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => 1, 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 15, 'index' => 1,), + + 'lastname' => array('type' => 'varchar(50)', 'label' => 'Lastname', 'enabled' => 1, 'visible' => 4, 'position' => 30, 'showonheader' => 1,), + 'firstname' => array('type' => 'varchar(50)', 'label' => 'Firstname', 'enabled' => 1, 'visible' => 4, 'position' => 35, 'showonheader' => 1,), + 'gender' => array('type' => 'varchar(10)', 'label' => 'Gender', 'enabled' => 1, 'visible' => 4, 'position' => 50, 'arrayofkeyval' => self::gender_list, 'showonheader' => 1,), + 'company' => array('type' => 'varchar(128)', 'label' => 'Societe', 'enabled' => 1, 'visible' => 4, 'position' => 65, 'showonheader' => 1,), + 'address' => array('type' => 'text', 'label' => 'Address', 'enabled' => 1, 'visible' => 4, 'position' => 75, 'showonheader' => 1,), + 'zip' => array('type' => 'varchar(10)', 'label' => 'Zip', 'enabled' => 1, 'visible' => 4, 'position' => 80, 'showonheader' => 1,), + 'town' => array('type' => 'varchar(50)', 'label' => 'Town', 'enabled' => 1, 'visible' => 4, 'position' => 85, 'showonheader' => 1,), + 'state_id' => array('type' => 'integer', 'label' => 'State id', 'enabled' => '!getDolGlobalString("MEMBER_DISABLE_STATE")', 'visible' => -5, 'position' => 90, 'showonheader' => 1,), + 'country_id' => array('type' => 'integer:Ccountry:core/class/ccountry.class.php', 'label' => 'Country', 'enabled' => 1, 'visible' => 4, 'position' => 95, 'showonheader' => 1,), + 'phone' => array('type' => 'varchar(30)', 'label' => 'Phone', 'enabled' => 1, 'visible' => 4, 'position' => 115, 'showonheader' => 1,), + 'phone_perso' => array('type' => 'varchar(30)', 'label' => 'Phone perso', 'enabled' => 1, 'visible' => 4, 'position' => 120, 'showonheader' => 1,), + 'phone_mobile' => array('type' => 'varchar(30)', 'label' => 'Phone mobile', 'enabled' => 1, 'visible' => 4, 'position' => 125, 'showonheader' => 1,), + 'email' => array('type' => 'varchar(255)', 'label' => 'Email', 'enabled' => 1, 'visible' => 4, 'position' => 200, 'showonheader' => 1,), + 'url' =>array('type'=>'varchar(255)', 'label'=>'Url', 'enabled'=>1, 'visible' => 4, 'position'=>210, 'showonheader' => 1,), + + 'login' => array('type' => 'varchar(50)', 'label' => 'Login', 'enabled' => 1, 'visible' => 4, 'position' => 240,), + 'typeid' => array('type' => 'integer:AdherentType:adherents/class/adherent_type.class.php', 'label' => 'Type', 'enabled' => 1, 'visible' => 4, 'notnull' => 1, 'position' => 255), + 'morphy' => array('type' => 'varchar(3)', 'label' => 'MemberNature', 'enabled' => 1, 'visible' => 4, 'notnull' => 1, 'position' => 260, 'arrayofkeyval' => self::morphy_list,), + 'civility_id' => array('type' => 'sellist:c_civility:label:rowid::active=1', 'label' => 'Civility', 'enabled' => 1, 'visible' => 4, 'position' => 270,), + 'datefin' => array('type' => 'date', 'label' => 'SubscriptionEndDate', 'enabled' => 1, 'visible' => 5, 'position' => 280,), + 'birth' => array('type' => 'date', 'label' => 'DateOfBirth', 'enabled' => 1, 'visible' => 4, 'position' => 290,), + 'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'enabled' => 1, 'visible' => 5, 'position' => 300,), + + 'status' => array('type' => 'smallint(6)', 'label' => 'Status', 'enabled' => 1, 'visible' => 5, 'notnull' => 1, 'position' => 500, 'arrayofkeyval' => self::status_short_list, 'showonheader' => 1,), + ); + public $rowid; + //public $ref; + //public $lastname; + //public $firstname; + //public $gender; + //public $address; + //public $zip; + //public $town; + //public $state_id; + //public $country; + //public $phone; + //public $phone_perso; + //public $phone_mobile; + //public $email; + //public $url; + //public $socialnetworks; + //public $login; + public $fk_adherent_type; + //public $morphy; + //public $societe; + //public $civility_id; + //public $datefin; + //public $birth; + //public $fk_soc; + //public $status; + // END MODULEBUILDER PROPERTIES + + /** + * Get member for static method + * + * @return Adherent + */ + protected function getMemberStatic() + { + if (!$this->member_static) { + $this->member_static= new Adherent($this->db); + } + + return $this->member_static; + } + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $langs; + + $this->db = $db; + + $this->getMemberStatic(); + + // Translate some data of arrayofkeyval + if (is_object($langs)) { + foreach ($this->fields as $key => $val) { + if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { + foreach ($val['arrayofkeyval'] as $key2 => $val2) { + $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2); + } + } + } + } + } + + /** + * getTooltipContentArray + * + * @param array $params Params to construct tooltip data + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $conf, $langs; + + $datas = []; + + if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) { + return ['optimize' => $langs->trans("ShowWebPortalMember")]; + } + $datas['picto'] = img_picto('', $this->picto).' '.$langs->trans("WebPortalMember").''; + if (isset($this->status)) { + $datas['picto'] .= ' '.$this->getLibStatut(5); + } + $datas['ref'] .= '
    '.$langs->trans('Ref').': '.$this->ref; + + return $datas; + } + + /** + * Return clicable name (with picto eventually) + * + * @param int $withpictoimg 0=No picto, 1=Include picto into link, 2=Only picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very small) + * @param int $maxlen length max label + * @param string $option Page for link ('card', 'category', 'subscription', ...) + * @param string $mode ''=Show firstname+lastname as label (using default order), 'firstname'=Show only firstname, 'lastname'=Show only lastname, 'login'=Show login, 'ref'=Show ref + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @param int $notooltip 1=Disable tooltip + * @param int $addlinktonotes 1=Add link to notes + * @return string Chaine avec URL + */ + public function getNomUrl($withpictoimg = 0, $maxlen = 0, $option = 'card', $mode = '', $morecss = '', $save_lastsearch_value = -1, $notooltip = 0, $addlinktonotes = 0) + { + global $conf, $langs, $hookmanager; + + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpictoimg) { + $withpictoimg = 0; + } + + $option = 'nolink'; + + $result = ''; + $linkstart = ''; + $linkend = ''; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'option' => $option, + 'nofetch' => 1, + ]; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"'; + $label = ''; + } else { + $label = implode($this->getTooltipContentArray($params)); + } + + $url = ''; + //$url = DOL_URL_ROOT.'/adherents/card.php?rowid='.((int) $this->id); + //if ($option == 'subscription') { + // $url = DOL_URL_ROOT.'/adherents/subscription.php?rowid='.((int) $this->id); + //} + + //if ($option != 'nolink') { + // // Add param to save lastsearch_values or not + // $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); + // if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { + // $add_save_lastsearch_values = 1; + // } + // if ($add_save_lastsearch_values) { + // $url .= '&save_lastsearch_values=1'; + // } + //} + + $linkstart .= 'global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $langs->load("users"); + $label = $langs->trans("ShowUser"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"'); + $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"'; + } + + $linkstart .= $linkclose.'>'; + $linkend = ''; + + if ($option === 'nolink') { + $linkstart = ''; + $linkend = ''; + } + + $result .= $linkstart; + if ($withpictoimg) { + $result .= '
    '; + } + if ($withpictoimg) { + $paddafterimage = ''; + if (abs($withpictoimg) == 1 || abs($withpictoimg) == 4) { + $morecss .= ' paddingrightonly'; + } + // Only picto + if ($withpictoimg > 0) { + $picto = ''.img_object('', 'user', $paddafterimage.' '.($notooltip ? '' : $dataparams), 0, 0, $notooltip ? 0 : 1).''; + } else { + // Picto must be a photo + $picto = ''; + $picto .= Form::showphoto('memberphoto', $this, 0, 0, 0, 'userphoto'.(($withpictoimg == -3 || $withpictoimg == -4) ? 'small' : ''), 'mini', 0, 1); + $picto .= ''; + } + $result .= $picto; + } + if (($withpictoimg > -2 && $withpictoimg != 2) || $withpictoimg == -4) { + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $result .= ''; + } + if ($mode == 'login') { + $result .= dol_trunc($this->login, $maxlen); + } elseif ($mode == 'ref') { + $result .= $this->ref; + } else { + $result .= $this->getFullName($langs, '', ($mode == 'firstname' ? 2 : ($mode == 'lastname' ? 4 : -1)), $maxlen); + } + if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $result .= ''; + } + } + if ($withpictoimg) { + $result .= '
    '; + } + $result .= $linkend; + + //if ($addlinktonotes) { + // if ($this->note_private) { + // $notetoshow = $langs->trans("ViewPrivateNote").':
    '.dol_string_nohtmltag($this->note_private, 1); + // $result .= ' '; + // $result .= ''; + // $result .= img_picto('', 'note'); + // $result .= ''; + // $result .= ''; + // } + //} + global $action; + $hookmanager->initHooks(array($this->element . 'dao')); + $parameters = array('id'=>$this->id, 'getnomurl' => &$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + $result = $hookmanager->resPrint; + } else { + $result .= $hookmanager->resPrint; + } + return $result; + } + + /** + * Retourne le libelle du statut d'un adherent (brouillon, valide, resilie, exclu) + * + * @param int $mode 0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto + * @return string Label + */ + public function getLibStatut($mode = 0) + { + return $this->LibStatut($this->status, $this->need_subscription, $this->datefin, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Renvoi le libelle d'un statut donne + * + * @param int $status Id status + * @param int $need_subscription 1 if member type need subscription, 0 otherwise + * @param int $date_end_subscription Date fin adhesion + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label + */ + public function LibStatut($status, $need_subscription, $date_end_subscription, $mode = 0) + { + // phpcs:enable + return $this->getMemberStatic()->LibStatut($status, $need_subscription, $date_end_subscription, $mode); + } + + /** + * Return full address for banner + * + * @param string $htmlkey HTML id to make banner content unique + * @return string Full address string + */ + public function getBannerAddressForWebPortal($htmlkey) + { + global $conf, $langs, $form, $extralanguages; + + $countriesusingstate = array('AU', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS + + $contactid = 0; + $thirdpartyid = 0; + $elementforaltlanguage = $this->element; + + $out = ''; + + $outdone = 0; + $coords = $this->getFullAddress(1, ', ', getDolGlobalInt('MAIN_SHOW_REGION_IN_STATE_SELECT')); + if ($coords) { + $address = dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); + if ($address) { + $out .= $address; + $outdone++; + } + $outdone++; + + // List of extra languages + $arrayoflangcode = array(); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) { + $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE; + } + + if (is_array($arrayoflangcode) && count($arrayoflangcode)) { + if (!is_object($extralanguages)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php'; + $extralanguages = new ExtraLanguages($this->db); + } + $extralanguages->fetch_name_extralanguages($elementforaltlanguage); + + if (!empty($extralanguages->attributes[$elementforaltlanguage]['address']) || !empty($extralanguages->attributes[$elementforaltlanguage]['town'])) { + $out .= "\n"; + $this->fetchValuesForExtraLanguages(); + if (!is_object($form)) { + $form = new Form($this->db); + } + $htmltext = ''; + // If there is extra languages + foreach ($arrayoflangcode as $extralangcode) { + $s = picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"'); + // This also call dol_format_address() + $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT, $extralangcode); + $htmltext .= $s.dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); + } + $out .= $form->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft'); + } + } + } + + // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress + if (!in_array($this->country_code, $countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS) + && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state) { + if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) { + $out .= ($outdone ? ' - ' : '').$this->region.' - '.$this->state; + } else { + $out .= ($outdone ? ' - ' : '').$this->state; + } + $outdone++; + } + + if ($outdone) { + $out = '
    '.$out.'
    '; + } + + if (!empty($this->phone) || !empty($this->phone_pro) || !empty($this->phone_mobile) || !empty($this->phone_perso) || !empty($this->fax) || !empty($this->office_phone) || !empty($this->user_mobile) || !empty($this->office_fax)) { + $out .= ($outdone ? '
    ' : ''); + } + if (!empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone + $out .= dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'phone', $langs->trans("PhonePro")); + $outdone++; + } + if (!empty($this->phone_pro)) { + $out .= dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'phone', $langs->trans("PhonePro")); + $outdone++; + } + if (!empty($this->phone_mobile)) { + $out .= dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'mobile', $langs->trans("PhoneMobile")); + $outdone++; + } + if (!empty($this->phone_perso)) { + $out .= dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'phone', $langs->trans("PhonePerso")); + $outdone++; + } + if (!empty($this->office_phone)) { + $out .= dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'phone', $langs->trans("PhonePro")); + $outdone++; + } + if (!empty($this->user_mobile)) { + $out .= dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'mobile', $langs->trans("PhoneMobile")); + $outdone++; + } + if (!empty($this->fax)) { + $out .= dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'fax', $langs->trans("Fax")); + $outdone++; + } + if (!empty($this->office_fax)) { + $out .= dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, '', ' ', 'fax', $langs->trans("Fax")); + $outdone++; + } + + if ($out) { + $out .= '
    '; + } + $outdone = 0; + if (!empty($this->email)) { + $out .= dol_print_email($this->email, $this->id, $this->id, '', 0, 0, 1); + $outdone++; + } + if (!empty($this->url)) { + $out .= dol_print_url($this->url, '_blank', 0, 1); + $outdone++; + } + + return $out; + } +} diff --git a/htdocs/webportal/class/webportalorder.class.php b/htdocs/webportal/class/webportalorder.class.php new file mode 100644 index 0000000000000..0e328a6f9e690 --- /dev/null +++ b/htdocs/webportal/class/webportalorder.class.php @@ -0,0 +1,348 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/webportalorder.class.php + * \ingroup webportal + * \brief This file is a CRUD class file for WebPortalOrder (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; + +/** + * Class for WebPortalOrder + */ +class WebPortalOrder extends Commande +{ + /** + * @var string ID of module. + */ + public $module = 'webportal'; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * Status list (short label) + */ + const status_short_list = array( + Commande::STATUS_DRAFT => 'StatusOrderDraftShort', + Commande::STATUS_VALIDATED => 'StatusOrderValidated', + Commande::STATUS_SHIPMENTONPROCESS => 'StatusOrderSentShort', + Commande::STATUS_CLOSED => 'StatusOrderDelivered', + Commande::STATUS_CANCELED => 'StatusOrderCanceledShort', + ); + + /** + * @var Commande Order for static methods + */ + protected $order_static = null; + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]', + * 'chkbxlst:...', + * 'varchar(x)', + * 'text', 'text:none', 'html', + * 'double(24,8)', 'real', 'price', + * 'date', 'datetime', 'timestamp', 'duration', + * 'boolean', 'checkbox', 'radio', 'array', + * 'mail', 'phone', 'url', 'password', 'ip' + * Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt('MY_SETUP_PARAM') or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>10,), + 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1,), + 'ref' =>array('type'=>'varchar(30)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>2, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>25,), + 'date_commande' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>2, 'position'=>60,), + 'date_livraison' =>array('type'=>'date', 'label'=>'DateDeliveryPlanned', 'enabled'=>1, 'visible'=>2, 'position'=>70,), + 'total_ht' =>array('type'=>'price', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>2, 'position'=>125, 'isameasure'=>1,), + 'total_tva' =>array('type'=>'price', 'label'=>'VAT', 'enabled'=>1, 'visible'=>2, 'position'=>130, 'isameasure'=>1,), + 'total_ttc' =>array('type'=>'price', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>2, 'position'=>145, 'isameasure'=>1,), + 'multicurrency_total_ht' =>array('type'=>'price', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>255, 'isameasure'=>1,), + 'multicurrency_total_tva' =>array('type'=>'price', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>260, 'isameasure'=>1,), + 'multicurrency_total_ttc' =>array('type'=>'price', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>265, 'isameasure'=>1,), + 'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>2, 'position'=>500, 'notnull'=>-5, 'arrayofkeyval' => self::status_short_list,), + ); + //public $rowid; + //public $ref; + //public $date_commande; + //public $date_livraison; + //public $total_ht; + //public $total_tva; + //public $total_ttc; + //public $multicurrency_total_ht; + //public $multicurrency_total_tva; + //public $multicurrency_total_ttc; + public $fk_statut; + // END MODULEBUILDER PROPERTIES + + + /** + * Get order for static method + * + * @return Commande + */ + protected function getOrderStatic() + { + if (!$this->order_static) { + $this->order_static= new Commande($this->db); + } + + return $this->order_static; + } + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $langs; + + $this->db = $db; + + $this->getOrderStatic(); + + // translate label status +// $statusLabelList = array(); +// foreach (self::status_short_list as $id => $transKey) { +// $statusFinalLabelList = array(); +// $statusTransKeyArr = explode('+', $transKey); +// foreach ($statusTransKeyArr as $subStatusTransKey) { +// $statusFinalLabelList[] = $langs->trans($subStatusTransKey); +// } +// $statusLabelList[$id] = implode('+', $statusFinalLabelList); +// } +// $this->fields['fk_statut']['arrayofkeyval'] = $statusLabelList; + } + + /** + * getTooltipContentArray + * + * @param array $params Params to construct tooltip data + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $conf, $langs; + + $datas = []; + + if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) { + return ['optimize' => $langs->trans("ShowWebPortalOrder")]; + } + $datas['picto'] = img_picto('', $this->picto).' '.$langs->trans("WebPortalOrder").''; + if (isset($this->status)) { + $datas['picto'] .= ' '.$this->getLibStatut(5); + } + $datas['ref'] .= '
    '.$langs->trans('Ref').': '.$this->ref; + + return $datas; + } + + /** + * Return clicable link of object (with eventually picto) + * + * @param int $withpicto Add picto into link + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param int $max Max length to show + * @param int $short ??? + * @param int $notooltip 1=Disable tooltip + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @param int $addlinktonotes Add link to notes + * @param string $target attribute target for link + * @return string String with URL + */ + public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0, $target = '') + { + global $conf, $langs, $hookmanager; + + if (!empty($conf->dol_no_mouse_hover)) { + $notooltip = 1; // Force disable tooltips + } + + $result = ''; + + $url = ''; + + $option = 'nolink'; + + if ($short) { + return $url; + } + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'option' => $option, + 'nofetch' => 1, + ]; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"'; + $label = ''; + } else { + $label = implode($this->getTooltipContentArray($params)); + } + + $linkclose = ''; + + $linkstart = ''; + $linkend = ''; + + if ($option === 'nolink') { + $linkstart = ''; + $linkend = ''; + } + + $result .= $linkstart; + if ($withpicto) { + $result .= img_object(($notooltip ? '' : $label), $this->picto, (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1); + } + if ($withpicto != 2) { + $result .= $this->ref; + } + $result .= $linkend; + + global $action; + $hookmanager->initHooks(array($this->element . 'dao')); + $parameters = array('id'=>$this->id, 'getnomurl' => &$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + $result = $hookmanager->resPrint; + } else { + $result .= $hookmanager->resPrint; + } + return $result; + } + + /** + * Return clicable link of object (with eventually picto) + * + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param array $arraydata Array of data + * @return string HTML Code for Kanban thumb. + */ + public function getKanbanView($option = '', $arraydata = null) + { + global $langs, $conf; + + $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); + + $return = '
    '; + $return .= '
    '; + $return .= '
    '; + $return .= img_picto('', 'order'); + $return .= '
    '; + $return .= '
    '; + $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).''; + $return .= ''; + + if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) { + $return .= '
    '.$this->thirdparty->getNomUrl(1).'
    '; + } + if (property_exists($this, 'total_ht')) { + $return .= '
    '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency).' '.$langs->trans('HT').'
    '; + } + if (method_exists($this, 'getLibStatut')) { + $return .= '
    '.$this->getLibStatut(5).'
    '; + } + $return .= '
    '; + $return .= '
    '; + $return .= '
    '; + + return $return; + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLabelStatus($mode = 0) + { + return $this->LibStatut($this->fk_statut, $this->billed, $mode); + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode = 0) + { + return $this->LibStatut($this->fk_statut, $this->billed, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return label of status + * + * @param int $status Id status + * @param int $billed If invoiced + * @param int $mode 0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto + * @param int $donotshowbilled Do not show billed status after order status + * @return string Label of status + */ + public function LibStatut($status, $billed, $mode, $donotshowbilled = 0) + { + // phpcs:enable + return $this->getOrderStatic()->LibStatut($status, $billed, $mode, $donotshowbilled); + } +} diff --git a/htdocs/webportal/class/webportalpartnership.class.php b/htdocs/webportal/class/webportalpartnership.class.php new file mode 100644 index 0000000000000..2baa5d453554b --- /dev/null +++ b/htdocs/webportal/class/webportalpartnership.class.php @@ -0,0 +1,365 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/webportalpartnership.class.php + * \ingroup webportal + * \brief This file is a CRUD class file for WebPortalPartnership (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/partnership/class/partnership.class.php'; + +/** + * Class for WebPortalPartnership + */ +class WebPortalPartnership extends Partnership +{ + /** + * @var string ID of module. + */ + public $module = 'webportal'; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * Status list (short label) + */ + const status_short_list = array( + Partnership::STATUS_DRAFT => 'Draft', + Partnership::STATUS_VALIDATED => 'Accepted', + Partnership::STATUS_APPROVED => 'Refused', + Partnership::STATUS_REFUSED => 'Suspended', + Partnership::STATUS_CANCELED => 'Terminated', + ); + + /** + * @var Partnership Partnership for static methods + */ + protected $partnership_static = null; + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'varchar(x)', + * 'text', 'html', + * 'double(24,8)', 'price', + * 'date', 'datetime', + * 'checkbox', 'radio', + * 'mail', 'phone', 'url', 'password' + * Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt('MY_SETUP_PARAM') or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * 'showonheader' is 1 to show on the top of the card (header section) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id",), + 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>'1', 'position'=>10, 'notnull'=>1, 'visible'=>5, 'noteditable'=>'1', 'default'=>'(PROV)', 'index'=>1, 'searchall'=>1, 'showoncombobox'=>'1', 'comment'=>"Reference of object", 'showonheader' => 1,), + 'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>'1', 'position'=>15, 'notnull'=>1, 'visible'=>-2, 'default'=>'1', 'index'=>1,), + + 'fk_type' => array('type'=>'integer:PartnershipType:partnership/class/partnership_type.class.php:0:(active:=:1)', 'label'=>'Type', 'enabled'=>'1', 'position'=>20, 'notnull'=>1, 'visible'=>5, 'csslist'=>'',), + 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))', 'label'=>'ThirdParty', 'picto'=>'company', 'enabled'=>'1', 'position'=>50, 'notnull'=>-1, 'visible'=>5, 'index'=>1, 'css'=>'', 'csslist'=>'',), + 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>'1', 'position'=>61, 'notnull'=>0, 'visible'=>0,), + 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>'1', 'position'=>62, 'notnull'=>0, 'visible'=>0,), + 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>'1', 'position'=>500, 'notnull'=>1, 'visible'=>-2,), + 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>'1', 'position'=>501, 'notnull'=>0, 'visible'=>-2,), + 'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>'1', 'position'=>510, 'notnull'=>1, 'visible'=>-2, 'foreignkey'=>'user.rowid',), + 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>'1', 'position'=>511, 'notnull'=>-1, 'visible'=>-2,), + 'last_main_doc' => array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>'1', 'position'=>600, 'notnull'=>0, 'visible'=>0,), + 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>'1', 'position'=>1000, 'notnull'=>-1, 'visible'=>-2,), + 'model_pdf' => array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>'1', 'position'=>1010, 'notnull'=>-1, 'visible'=>0,), + 'date_partnership_start' => array('type'=>'date', 'label'=>'DatePartnershipStart', 'enabled'=>'1', 'position'=>52, 'notnull'=>1, 'visible'=>5,), + 'date_partnership_end' => array('type'=>'date', 'label'=>'DatePartnershipEnd', 'enabled'=>'1', 'position'=>53, 'notnull'=>0, 'visible'=>5,), + 'url_to_check' => array('type'=>'varchar(255)', 'label'=>'UrlToCheck', 'enabled'=>'1', 'position'=>70, 'notnull'=>0, 'visible'=>-5,), + 'count_last_url_check_error' => array('type'=>'integer', 'label'=>'CountLastUrlCheckError', 'enabled'=>'1', 'position'=>71, 'notnull'=>0, 'visible'=>-2, 'default'=>'0',), + 'last_check_backlink' => array('type'=>'datetime', 'label'=>'LastCheckBacklink', 'enabled'=>'1', 'position'=>72, 'notnull'=>0, 'visible'=>-2,), + 'reason_decline_or_cancel' => array('type'=>'text', 'label'=>'ReasonDeclineOrCancel', 'enabled'=>'1', 'position'=>73, 'notnull'=>0, 'visible'=>-2,), + 'ip' => array('type'=>'varchar(250)', 'label'=>'Ip', 'enabled'=>'1', 'position'=>74, 'notnull'=>0, 'visible'=>-2,), + + 'status' => array('type'=>'smallint', 'label'=>'Status', 'enabled'=>'1', 'position'=>2000, 'notnull'=>1, 'visible'=>5, 'default'=>'0', 'index'=>1, 'arrayofkeyval'=>self::status_short_list, 'showonheader' => 1,), + ); + //public $rowid; + //public $ref; + //public $entity; + //public $fk_type; + //public $note_public; + //public $note_private; + //public $date_creation; + //public $tms; + //public $fk_user_creat; + //public $fk_user_modif; + //public $last_main_doc; + //public $import_key; + //public $model_pdf; + //public $date_partnership_start; + //public $date_partnership_end; + //public $url_to_check; + //public $count_last_url_check_error; + //public $last_check_backlink; + //public $reason_decline_or_cancel; + //public $fk_soc; + //public $fk_member; + //public $ip; + //public $status; + // END MODULEBUILDER PROPERTIES + + /** + * Get partnership for static method + * + * @return Partnership + */ + protected function getPartnershipStatic() + { + if (!$this->partnership_static) { + $this->partnership_static= new Partnership($this->db); + } + + return $this->partnership_static; + } + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $langs; + + $this->db = $db; + + $this->getPartnershipStatic(); + + // Translate some data of arrayofkeyval + if (is_object($langs)) { + foreach ($this->fields as $key => $val) { + if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { + foreach ($val['arrayofkeyval'] as $key2 => $val2) { + $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2); + } + } + } + } + } + + /** + * getTooltipContentArray + * + * @param array $params Params to construct tooltip data + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $conf, $langs; + + $datas = []; + + if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) { + return ['optimize' => $langs->trans("ShowWebPortalPartnership")]; + } + $datas['picto'] = img_picto('', $this->picto).' '.$langs->trans("WebPortalPartnership").''; + if (isset($this->status)) { + $datas['picto'] .= ' '.$this->getLibStatut(5); + } + $datas['ref'] .= '
    '.$langs->trans('Ref').': '.$this->ref; + + return $datas; + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) + * @param string $option On what the link point to ('nolink', ...) + * @param int $notooltip 1=Disable tooltip + * @param string $morecss Add more css on link + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @return string String with URL + */ + public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1) + { + global $conf, $langs, $hookmanager; + + if (!empty($conf->dol_no_mouse_hover)) { + $notooltip = 1; // Force disable tooltips + } + + $option = 'nolink'; + + $result = ''; + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'option' => $option, + ]; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"'; + $label = ''; + } else { + $label = implode($this->getTooltipContentArray($params)); + } + + $url = ''; + //$url = DOL_URL_ROOT.'/partnership/partnership_card.php?id='.$this->id; + + if ($option != 'nolink') { + // Add param to save lastsearch_values or not + $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); + if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { + $add_save_lastsearch_values = 1; + } + if ($add_save_lastsearch_values) { + $url .= '&save_lastsearch_values=1'; + } + } + + $linkclose = ''; + if (empty($notooltip)) { + if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { + $label = $langs->trans("ShowPartnership"); + $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; + } + $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"'); + $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"'; + } else { + $linkclose = ($morecss ? ' class="'.$morecss.'"' : ''); + } + + if ($option == 'nolink') { + $linkstart = ''; + if ($option == 'nolink') { + $linkend = ''; + } else { + $linkend = ''; + } + + $result .= $linkstart; + + if (empty($this->showphoto_on_popup)) { + if ($withpicto) { + $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1); + } + } else { + if ($withpicto) { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + list($class, $module) = explode('@', $this->picto); + $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref); + $filearray = dol_dir_list($upload_dir, "files"); + $filename = $filearray[0]['name']; + if (!empty($filename)) { + $pospoint = strpos($filearray[0]['name'], '.'); + + $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint); + if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) { + $result .= '
    No photo
    '; + } else { + $result .= '
    No photo
    '; + } + + $result .= ''; + } else { + $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); + } + } + } + + if ($withpicto != 2) { + $result .= $this->ref; + } + + $result .= $linkend; + //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); + + global $action, $hookmanager; + $hookmanager->initHooks(array('partnershipdao')); + $parameters = array('id'=>$this->id, 'getnomurl' => &$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + $result = $hookmanager->resPrint; + } else { + $result .= $hookmanager->resPrint; + } + + return $result; + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode = 0) + { + return $this->LibStatut($this->status, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function LibStatut($status, $mode = 0) + { + // phpcs:enable + return $this->getPartnershipStatic()->LibStatut($status, $mode); + } +} diff --git a/htdocs/webportal/class/webportalpropal.class.php b/htdocs/webportal/class/webportalpropal.class.php new file mode 100644 index 0000000000000..95b6aaace51bc --- /dev/null +++ b/htdocs/webportal/class/webportalpropal.class.php @@ -0,0 +1,329 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file class/webportalpropal.class.php + * \ingroup webportal + * \brief This file is a CRUD class file for WebPortalPropal (Create/Read/Update/Delete) + */ + +// Put here all includes required by your class file +require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; + +/** + * Class for WebPortalPropal + */ +class WebPortalPropal extends Propal +{ + /** + * @var string ID of module. + */ + public $module = 'webportal'; + + /** + * @var int Does object support extrafields ? 0=No, 1=Yes + */ + public $isextrafieldmanaged = 0; + + /** + * Status list (short label) + */ + const status_short_list = array( + Propal::STATUS_DRAFT => 'PropalStatusDraftShort', + Propal::STATUS_VALIDATED => 'PropalStatusValidatedShort', + Propal::STATUS_SIGNED => 'PropalStatusSignedShort', + Propal::STATUS_NOTSIGNED => 'PropalStatusNotSignedShort', + Propal::STATUS_BILLED => 'PropalStatusBilledShort', + ); + + /** + * @var Propal Propal for static methods + */ + protected $propal_static = null; + + /** + * 'type' field format: + * 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]', + * 'select' (list of values are in 'options'), + * 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]', + * 'chkbxlst:...', + * 'varchar(x)', + * 'text', 'text:none', 'html', + * 'double(24,8)', 'real', 'price', + * 'date', 'datetime', 'timestamp', 'duration', + * 'boolean', 'checkbox', 'radio', 'array', + * 'mail', 'phone', 'url', 'password', 'ip' + * Note: Filter must be a Dolibarr Universal Filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)" + * 'label' the translation key. + * 'picto' is code of a picto to show before value in forms + * 'enabled' is a condition when the field must be managed (Example: 1 or 'getDolGlobalInt('MY_SETUP_PARAM') or 'isModEnabled("multicurrency")' ...) + * 'position' is the sort order of field. + * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). + * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) + * 'noteditable' says if field is not editable (1 or 0) + * 'alwayseditable' says if field can be modified also when status is not draft ('1' or '0') + * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. + * 'index' if we want an index in database. + * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). + * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. + * 'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage) + * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200' + * 'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. + * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record + * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. + * 'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar' + * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. + * 'comment' is not used. You can store here any text of your choice. It is not used by application. + * 'validate' is 1 if need to validate with $this->validateField() + * 'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value) + * + * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. + */ + + // BEGIN MODULEBUILDER PROPERTIES + /** + * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. + */ + public $fields=array( + 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id",), + 'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>15, 'index'=>1,), + 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>'1', 'position'=>20, 'notnull'=>1, 'visible'=>2, 'index'=>1, 'searchall'=>1, 'showoncombobox'=>'1', 'validate'=>'1', 'comment'=>"Reference of object",), + 'datep' => array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>2, 'position'=>60,), + 'fin_validite' => array('type'=>'date', 'label'=>'DateEnd', 'enabled'=>1, 'visible'=>2, 'position'=>65,), + 'total_ht' => array('type'=>'price', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>2, 'position'=>125, 'isameasure'=>1,), + 'total_tva' => array('type'=>'price', 'label'=>'VAT', 'enabled'=>1, 'visible'=>2, 'position'=>130, 'isameasure'=>1,), + 'total_ttc' => array('type'=>'price', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>2, 'position'=>145, 'isameasure'=>1,), + 'multicurrency_total_ht' => array('type'=>'price', 'label'=>'MulticurrencyAmountHT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>245, 'isameasure'=>1,), + 'multicurrency_total_tva' => array('type'=>'price', 'label'=>'MulticurrencyAmountVAT', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>250, 'isameasure'=>1,), + 'multicurrency_total_ttc' => array('type'=>'price', 'label'=>'MulticurrencyAmountTTC', 'enabled'=>'isModEnabled("multicurrency")', 'visible'=>-2, 'position'=>255, 'isameasure'=>1,), + 'fk_statut' => array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>2, 'notnull'=>1, 'position'=>500, 'arrayofkeyval' => self::status_short_list,), + ); + //public $rowid; + //public $ref; + //public $datep; + //public $fin_validite; + //public $total_ht; + //public $total_tva; + //public $total_ttc; + //public $multicurrency_total_ht; + //public $multicurrency_total_tva; + //public $multicurrency_total_ttc; + public $fk_statut; + // END MODULEBUILDER PROPERTIES + + + /** + * Get propal for static method + * + * @return Propal + */ + protected function getPropalStatic() + { + if (!$this->propal_static) { + $this->propal_static= new Propal($this->db); + } + + return $this->propal_static; + } + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + global $conf, $langs; + + $this->db = $db; + + $this->getPropalStatic(); + } + + /** + * getTooltipContentArray + * + * @param array $params Params to construct tooltip data + * @since v18 + * @return array + */ + public function getTooltipContentArray($params) + { + global $conf, $langs; + + $datas = []; + + if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) { + return ['optimize' => $langs->trans("ShowWebPortalPropal")]; + } + $datas['picto'] = img_picto('', $this->picto).' '.$langs->trans("WebPortalPropal").''; + if (isset($this->status)) { + $datas['picto'] .= ' '.$this->getLibStatut(5); + } + $datas['ref'] .= '
    '.$langs->trans('Ref').': '.$this->ref; + + return $datas; + } + + /** + * Return a link to the object card (with optionaly the picto) + * + * @param int $withpicto Add picto into link + * @param string $option Where point the link ('expedition', 'document', ...) + * @param string $get_params Parametres added to url + * @param int $notooltip 1=Disable tooltip + * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking + * @param int $addlinktonotes -1=Disable, 0=Just add label show notes, 1=Add private note (only internal user), 2=Add public note (internal or external user), 3=Add private (internal user) and public note (internal and external user) + * @return string String with URL + */ + public function getNomUrl($withpicto = 0, $option = '', $get_params = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = -1) + { + global $langs, $conf, $hookmanager; + + if (!empty($conf->dol_no_mouse_hover)) { + $notooltip = 1; // Force disable tooltips + } + + $result = ''; + $params = [ + 'id' => $this->id, + 'objecttype' => $this->element, + 'option' => $option, + 'nofetch' => 1, + ]; + $classfortooltip = 'classfortooltip'; + $dataparams = ''; + if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { + $classfortooltip = 'classforajaxtooltip'; + $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"'; + $label = ''; + } else { + $label = implode($this->getTooltipContentArray($params)); + } + + $url = ''; + $option = 'nolink'; + + $linkclose = ''; + + $linkstart = ''; + $linkend = ''; + + if ($option === 'nolink') { + $linkstart = ''; + $linkend = ''; + } + + $result .= $linkstart; + if ($withpicto) { + $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1); + } + if ($withpicto != 2) { + $result .= $this->ref; + } + $result .= $linkend; + + global $action; + $hookmanager->initHooks(array($this->element . 'dao')); + $parameters = array('id'=>$this->id, 'getnomurl' => &$result); + $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook > 0) { + $result = $hookmanager->resPrint; + } else { + $result .= $hookmanager->resPrint; + } + return $result; + } + + /** + * Return a thumb for kanban views + * + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param array $arraydata Array of data + * @return string HTML Code for Kanban thumb. + */ + public function getKanbanView($option = '', $arraydata = null) + { + global $conf, $langs; + + $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); + + $return = '
    '; + $return .= '
    '; + $return .= ''; + $return .= img_picto('', $this->picto); + $return .= ''; + $return .= '
    '; + $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).''; + if ($selected >= 0) { + $return .= ''; + } + if (property_exists($this, 'label')) { + $return .= '
    '.$this->label.'
    '; + } + if (property_exists($this, 'amount')) { + $return .= '
    '; + $return .= ''.price($this->amount, 0, $langs, 1, -1, -1, $conf->currency).''; + } + if (method_exists($this, 'getLibStatut')) { + $return .= '
    '.$this->getLibStatut(3).'
    '; + } + $return .= '
    '; + $return .= '
    '; + $return .= '
    '; + + return $return; + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLabelStatus($mode = 0) + { + return $this->LibStatut($this->fk_statut, $mode); + } + + /** + * Return the label of the status + * + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function getLibStatut($mode = 0) + { + return $this->LibStatut($this->fk_statut, $mode); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the label of a given status + * + * @param int $status Id status + * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto + * @return string Label of status + */ + public function LibStatut($status, $mode = 0) + { + // phpcs:enable + return $this->getPropalStatic()->LibStatut($status, $mode); + } +} diff --git a/htdocs/webportal/core/modules/modWebPortal.class.php b/htdocs/webportal/core/modules/modWebPortal.class.php new file mode 100644 index 0000000000000..f9e3637a69a13 --- /dev/null +++ b/htdocs/webportal/core/modules/modWebPortal.class.php @@ -0,0 +1,392 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \defgroup webportal Module WebPortal + * \brief WebPortal module descriptor. + * + * \file core/modules/modWebPortal.class.php + * \ingroup webportal + * \brief Description and activation file for module WebPortal + */ +include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php'; + +/** + * Description and activation class for module WebPortal + */ +class modWebPortal extends DolibarrModules +{ + /** + * Constructor. Define names, constants, directories, boxes, permissions + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $langs, $conf; + $this->db = $db; + + // Id for module (must be unique). + // Use here a free id (See in Home -> System information -> Dolibarr for list of used modules id). + $this->numero = 11000; // TODO Go on page https://wiki.dolibarr.org/index.php/List_of_modules_id to reserve an id number for your module + + // Key text used to identify module (for permissions, menus, etc...) + $this->rights_class = 'webportal'; + + // Family can be 'base' (core modules),'crm','financial','hr','projects','products','ecm','technic' (transverse modules),'interface' (link with external tools),'other','...' + // It is used to group modules by family in module setup page + $this->family = "portal"; + + // Module position in the family on 2 digits ('01', '10', '20', ...) + $this->module_position = '80'; + + // Gives the possibility for the module, to provide his own family info and position of this family (Overwrite $this->family and $this->module_position. Avoid this) + //$this->familyinfo = array('myownfamily' => array('position' => '01', 'label' => $langs->trans("MyOwnFamily"))); + // Module label (no space allowed), used if translation string 'ModuleWebPortalName' not found (WebPortal is name of module). + $this->name = preg_replace('/^mod/i', '', get_class($this)); + + // Module description, used if translation string 'ModuleWebPortalDesc' not found (WebPortal is name of module). + $this->description = "WebPortalDescription"; + // Used only if file README.md and README-LL.md not found. + $this->descriptionlong = "WebPortalDescription"; + + // Author + //$this->editor_name = 'Dolibarr'; + $this->editor_url = 'dolibarr.org'; + + // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z' + $this->version = 'dolibarr'; + // Url to the file with your last numberversion of this module + //$this->url_last_version = 'http://www.example.com/versionmodule.txt'; + + // Key used in llx_const table to save module status enabled/disabled (where WEBPORTAL is value of property name of module in uppercase) + $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); + + // Name of image file used for this module. + // If file is in theme/yourtheme/img directory under name object_pictovalue.png, use this->picto='pictovalue' + // If file is in module/img directory under name object_pictovalue.png, use this->picto='pictovalue@module' + // To use a supported fa-xxx css style of font awesome, use this->picto='xxx' + $this->picto = 'fa-file-o'; + + // Define some features supported by module (triggers, login, substitutions, menus, css, etc...) + $this->module_parts = array( + // Set this to 1 if module has its own trigger directory (core/triggers) + 'triggers' => 0, + // Set this to 1 if module has its own login method file (core/login) + 'login' => 0, + // Set this to 1 if module has its own substitution function file (core/substitutions) + 'substitutions' => 0, + // Set this to 1 if module has its own menus handler directory (core/menus) + 'menus' => 0, + // Set this to 1 if module overwrite template dir (core/tpl) + 'tpl' => 0, + // Set this to 1 if module has its own barcode directory (core/modules/barcode) + 'barcode' => 0, + // Set this to 1 if module has its own models directory (core/modules/xxx) + 'models' => 0, + // Set this to 1 if module has its own printing directory (core/modules/printing) + 'printing' => 0, + // Set this to 1 if module has its own theme directory (theme) + 'theme' => 0, + // Set this to relative path of css file if module has its own css file + 'css' => array( + // '/webportal/css/webportal.css.php', + ), + // Set this to relative path of js file if module must load a js on all pages + 'js' => array( + // '/webportal/js/webportal.js.php', + ), + // Set here all hooks context managed by module. To find available hook context, make a "grep -r '>initHooks(' *" on source code. You can also set hook context to 'all' + 'hooks' => array( + // 'data' => array( + // 'hookcontext1', + // 'hookcontext2', + // ), + // 'entity' => '0', + ), + // Set this to 1 if features of module are opened to external users + 'moduleforexternal' => 0, + ); + + // Data directories to create when module is enabled. + // Example: this->dirs = array("/webportal/temp","/webportal/subdir"); + $this->dirs = array("/webportal/temp"); + + // Config pages. Put here list of php page, stored into webportal/admin directory, to use to setup module. + $this->config_page_url = array("setup.php@webportal"); + + // Dependencies + // A condition to hide module + $this->hidden = false; + // List of module class names that must be enabled if this module is enabled. Example: array('always'=>array('modModuleToEnable1','modModuleToEnable2'), 'FR'=>array('modModuleToEnableFR')...) + $this->depends = array(); + // List of module class names to disable if this one is disabled. Example: array('modModuleToDisable1', ...) + $this->requiredby = array(); + // List of module class names this module is in conflict with. Example: array('modModuleToDisable1', ...) + $this->conflictwith = array(); + + // The language file dedicated to your module + $this->langfiles = array("webportal@webportal"); + + // Prerequisites + $this->phpmin = array(7, 0); // Minimum version of PHP required by module + //$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module + //$this->need_javascript_ajax = 0; + + // Messages at activation + $this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...) + $this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...) + //$this->automatic_activation = array('FR'=>'WebPortalWasAutomaticallyActivatedBecauseOfYourCountryChoice'); + //$this->always_enabled = true; // If true, can't be disabled + + // Constants + // List of particular constants to add when module is enabled (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive) + // Example: $this->const=array(1 => array('WEBPORTAL_MYNEWCONST1', 'chaine', 'myvalue', 'This is a constant to add', 1), + // 2 => array('WEBPORTAL_MYNEWCONST2', 'chaine', 'myvalue', 'This is another constant to add', 0, 'current', 1) + // ); + $this->const = array(); + + // Some keys to add into the overwriting translation tables + /*$this->overwrite_translation = array( + 'en_US:ParentCompany'=>'Parent company or reseller', + 'fr_FR:ParentCompany'=>'Maison mère ou revendeur' + )*/ + + if (!isset($conf->webportal) || !isset($conf->webportal->enabled)) { + $conf->webportal = new stdClass(); + $conf->webportal->enabled = 0; + } + + // Array to add new pages in new tabs + $this->tabs = array(); + // Example: + // $this->tabs[] = array('data'=>'objecttype:+tabname1:Title1:mylangfile@webportal:$user->rights->webportal->read:/webportal/mynewtab1.php?id=__ID__'); // To add a new tab identified by code tabname1 + // $this->tabs[] = array('data'=>'objecttype:+tabname2:SUBSTITUTION_Title2:mylangfile@webportal:$user->rights->othermodule->read:/webportal/mynewtab2.php?id=__ID__', // To add another new tab identified by code tabname2. Label will be result of calling all substitution functions on 'Title2' key. + // $this->tabs[] = array('data'=>'objecttype:-tabname:NU:conditiontoremove'); // To remove an existing tab identified by code tabname + // + // Where objecttype can be + // 'categories_x' to add a tab in category view (replace 'x' by type of category (0=product, 1=supplier, 2=customer, 3=member) + // 'contact' to add a tab in contact view + // 'contract' to add a tab in contract view + // 'group' to add a tab in group view + // 'intervention' to add a tab in intervention view + // 'invoice' to add a tab in customer invoice view + // 'invoice_supplier' to add a tab in supplier invoice view + // 'member' to add a tab in fundation member view + // 'opensurveypoll' to add a tab in opensurvey poll view + // 'order' to add a tab in sale order view + // 'order_supplier' to add a tab in supplier order view + // 'payment' to add a tab in payment view + // 'payment_supplier' to add a tab in supplier payment view + // 'product' to add a tab in product view + // 'propal' to add a tab in propal view + // 'project' to add a tab in project view + // 'stock' to add a tab in stock view + // 'thirdparty' to add a tab in third party view + // 'user' to add a tab in user view + + // Dictionaries + $this->dictionaries = array(); + /* Example: + $this->dictionaries=array( + 'langs'=>'webportal@webportal', + // List of tables we want to see into dictonnary editor + 'tabname'=>array("table1", "table2", "table3"), + // Label of tables + 'tablib'=>array("Table1", "Table2", "Table3"), + // Request to select fields + 'tabsql'=>array('SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table1 as f', 'SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table2 as f', 'SELECT f.rowid as rowid, f.code, f.label, f.active FROM '.MAIN_DB_PREFIX.'table3 as f'), + // Sort order + 'tabsqlsort'=>array("label ASC", "label ASC", "label ASC"), + // List of fields (result of select to show dictionary) + 'tabfield'=>array("code,label", "code,label", "code,label"), + // List of fields (list of fields to edit a record) + 'tabfieldvalue'=>array("code,label", "code,label", "code,label"), + // List of fields (list of fields for insert) + 'tabfieldinsert'=>array("code,label", "code,label", "code,label"), + // Name of columns with primary key (try to always name it 'rowid') + 'tabrowid'=>array("rowid", "rowid", "rowid"), + // Condition to show each dictionary + 'tabcond'=>array(isModEnabled('webportal'), isModEnabled('webportal'), isModEnabled('webportal')), + // Tooltip for every fields of dictionaries: DO NOT PUT AN EMPTY ARRAY + 'tabhelp'=>array(array('code'=>$langs->trans('CodeTooltipHelp'), 'field2' => 'field2tooltip'), array('code'=>$langs->trans('CodeTooltipHelp'), 'field2' => 'field2tooltip'), ...), + ); + */ + + // Boxes/Widgets + // Add here list of php file(s) stored in webportal/core/boxes that contains a class to show a widget. + $this->boxes = array( + // 0 => array( + // 'file' => 'webportalwidget1.php@webportal', + // 'note' => 'Widget provided by WebPortal', + // 'enabledbydefaulton' => 'Home', + // ), + // ... + ); + + // Cronjobs (List of cron jobs entries to add when module is enabled) + // unit_frequency must be 60 for minute, 3600 for hour, 86400 for day, 604800 for week + $this->cronjobs = array( + // 0 => array( + // 'label' => 'MyJob label', + // 'jobtype' => 'method', + // 'class' => '/webportal/class/webportalpropal.class.php', + // 'objectname' => 'WebPortalPropal', + // 'method' => 'doScheduledJob', + // 'parameters' => '', + // 'comment' => 'Comment', + // 'frequency' => 2, + // 'unitfrequency' => 3600, + // 'status' => 0, + // 'test' => 'isModEnabled("webportal")', + // 'priority' => 50, + // ), + ); + // Example: $this->cronjobs=array( + // 0=>array('label'=>'My label', 'jobtype'=>'method', 'class'=>'/dir/class/file.class.php', 'objectname'=>'MyClass', 'method'=>'myMethod', 'parameters'=>'param1, param2', 'comment'=>'Comment', 'frequency'=>2, 'unitfrequency'=>3600, 'status'=>0, 'test'=>'isModEnabled("webportal")', 'priority'=>50), + // 1=>array('label'=>'My label', 'jobtype'=>'command', 'command'=>'', 'parameters'=>'param1, param2', 'comment'=>'Comment', 'frequency'=>1, 'unitfrequency'=>3600*24, 'status'=>0, 'test'=>'isModEnabled("webportal")', 'priority'=>50) + // ); + + // Permissions provided by this module + $this->rights = array(); + $r = 0; + // Add here entries to declare new permissions + $this->rights[$r][0] = $this->numero . sprintf("%02d", $r + 1); // Permission id (must not be already used) + $this->rights[$r][1] = 'Administer users of the customer/partner webportal module'; // Permission label + $this->rights[$r][3] = 0; + $this->rights[$r][4] = 'write'; + //$r++; + //$this->rights[$r][0] = $this->numero . sprintf("%02d", $r + 1); // Permission id (must not be already used) + //$this->rights[$r][1] = 'Delete objects of WebPortal'; // Permission label + //$this->rights[$r][3] = 0; + //$this->rights[$r][4] = 'delete'; + //$r++; + + // Main menu entries to add + $this->menu = array(); + $r = 0; + // Add here entries to declare new menus + $this->menu[$r++] = array( + 'fk_menu'=>'', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode + 'type'=>'top', // This is a Top menu entry + 'titre'=>'ModuleWebPortalName', + 'prefix' => img_picto('', $this->picto, 'class="paddingright pictofixedwidth valignmiddle"'), + 'mainmenu'=>'webportal', + 'leftmenu'=>'', + 'url'=>'/webportal/public/index.php', + 'langs'=>'webportal@webportal', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory. + 'position'=>1000 + $r, + 'enabled'=>'isModEnabled("webportal")', // Define condition to show or hide menu entry. Use 'isModEnabled("webportal")' if entry must be visible if module is enabled. + 'perms'=>'1', // Use 'perms'=>'$user->hasRight("webportal", "webportalpropal", "read")' if you want your menu with a permission rules + 'target'=>'', + 'user'=>2, // 0=Menu for internal users, 1=external users, 2=both + ); + /*$this->menu[$r++]=array( + 'fk_menu'=>'fk_mainmenu=webportal', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode + 'type'=>'left', // This is a Left menu entry + 'titre'=>'WebPortalPropal', + 'prefix' => img_picto('', $this->picto, 'class="paddingright pictofixedwidth valignmiddle"'), + 'mainmenu'=>'webportal', + 'leftmenu'=>'webportalpropal', + 'url'=>'/webportal/webportalindex.php', + 'langs'=>'webportal@webportal', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory. + 'position'=>1000+$r, + 'enabled'=>'isModEnabled("webportal")', // Define condition to show or hide menu entry. Use 'isModEnabled("webportal")' if entry must be visible if module is enabled. + 'perms'=>'$user->hasRight("webportal", "webportalpropal", "read")', + 'target'=>'', + 'user'=>2, // 0=Menu for internal users, 1=external users, 2=both + ); + $this->menu[$r++]=array( + 'fk_menu'=>'fk_mainmenu=webportal,fk_leftmenu=webportalpropal', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode + 'type'=>'left', // This is a Left menu entry + 'titre'=>'List_WebPortalPropal', + 'mainmenu'=>'webportal', + 'leftmenu'=>'webportal_webportalpropal_list', + 'url'=>'/webportal/webportalpropal_list.php', + 'langs'=>'webportal@webportal', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory. + 'position'=>1000+$r, + 'enabled'=>'isModEnabled("webportal")', // Define condition to show or hide menu entry. Use 'isModEnabled("webportal")' if entry must be visible if module is enabled. + 'perms'=>'$user->hasRight("webportal", "webportalpropal", "read")' + 'target'=>'', + 'user'=>2, // 0=Menu for internal users, 1=external users, 2=both + ); + $this->menu[$r++]=array( + 'fk_menu'=>'fk_mainmenu=webportal,fk_leftmenu=webportalpropal', // '' if this is a top menu. For left menu, use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode + 'type'=>'left', // This is a Left menu entry + 'titre'=>'New_WebPortalPropal', + 'mainmenu'=>'webportal', + 'leftmenu'=>'webportal_webportalpropal_new', + 'url'=>'/webportal/webportalpropal_card.php?action=create', + 'langs'=>'webportal@webportal', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory. + 'position'=>1000+$r, + 'enabled'=>'isModEnabled("webportal")', // Define condition to show or hide menu entry. Use 'isModEnabled("webportal")' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected. + 'perms'=>'$user->hasRight("webportal", "webportalpropal", "write")' + 'target'=>'', + 'user'=>2, // 0=Menu for internal users, 1=external users, 2=both + );*/ + } + + /** + * Function called when module is enabled. + * The init function add constants, boxes, permissions and menus (defined in constructor) into Dolibarr database. + * It also creates data directories + * + * @param string $options Options when enabling module ('', 'noboxes') + * @return int 1 if OK, 0 if KO + */ + public function init($options = '') + { + global $conf, $langs; + + //$result = $this->_load_tables('/install/mysql/', 'webportal'); + $result = $this->_load_tables('/webportal/sql/'); + if ($result < 0) { + return -1; // Do not activate module if error 'not allowed' returned when loading module SQL queries (the _load_table run sql with run_sql with the error allowed parameter set to 'default') + } + + // Create extrafields during init + //include_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; + //$extrafields = new ExtraFields($this->db); + //$result1=$extrafields->addExtraField('webportal_myattr1', "New Attr 1 label", 'boolean', 1, 3, 'thirdparty', 0, 0, '', '', 1, '', 0, 0, '', '', 'webportal@webportal', 'isModEnabled("webportal")'); + //$result2=$extrafields->addExtraField('webportal_myattr2', "New Attr 2 label", 'varchar', 1, 10, 'project', 0, 0, '', '', 1, '', 0, 0, '', '', 'webportal@webportal', 'isModEnabled("webportal")'); + //$result3=$extrafields->addExtraField('webportal_myattr3', "New Attr 3 label", 'varchar', 1, 10, 'bank_account', 0, 0, '', '', 1, '', 0, 0, '', '', 'webportal@webportal', 'isModEnabled("webportal")'); + //$result4=$extrafields->addExtraField('webportal_myattr4', "New Attr 4 label", 'select', 1, 3, 'thirdparty', 0, 1, '', array('options'=>array('code1'=>'Val1','code2'=>'Val2','code3'=>'Val3')), 1,'', 0, 0, '', '', 'webportal@webportal', 'isModEnabled("webportal")'); + //$result5=$extrafields->addExtraField('webportal_myattr5', "New Attr 5 label", 'text', 1, 10, 'user', 0, 0, '', '', 1, '', 0, 0, '', '', 'webportal@webportal', 'isModEnabled("webportal")'); + + // Permissions + $this->remove($options); + + $sql = array(); + + return $this->_init($sql, $options); + } + + /** + * Function called when module is disabled. + * Remove from database constants, boxes and permissions from Dolibarr database. + * Data directories are not deleted + * + * @param string $options Options when enabling module ('', 'noboxes') + * @return int 1 if OK, 0 if KO + */ + public function remove($options = '') + { + $sql = array(); + return $this->_remove($sql, $options); + } +} diff --git a/htdocs/webportal/langs/en_US/webportal.lang b/htdocs/webportal/langs/en_US/webportal.lang new file mode 100644 index 0000000000000..b764cd3ee562e --- /dev/null +++ b/htdocs/webportal/langs/en_US/webportal.lang @@ -0,0 +1,82 @@ +# Copyright (C) 2023 Lionel Vessiller +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# +# Generic +# + +# Module label 'ModuleWebPortalName' +ModuleWebPortalName = Web portal +# Module description 'ModuleWebPortalDesc' +ModuleWebPortalDesc = Public web portal +WebPortalDescription = Public web portal module for membership and partnership + +# +# Admin page +# +WebPortalSetup = WebPortal setup +Settings = Settings +WebPortalSetupPage = WebPortal setup page +WEBPORTAL_ROOT_URL = Public access url +WebPortalRootUrlHelp = Public access url (http or https) if you have a virtual host or keep empty +WebPortalAccessHidden = Hidden +WebPortalAccessVisible = Visible +WebPortalAccessEdit = Editable +WEBPORTAL_MEMBER_CARD_ACCESS = Enable access to the membership record +WebPortalMemberCardAccessHelp = Enable access to the membership record (Hidden / Visible or Editable) +WEBPORTAL_PARTNERSHIP_CARD_ACCESS = Enable access to the partnership record +WebPortalPartnerShipCardAccessHelp = Enable access to the partnership record (Hidden / Visible or Editable) +WEBPORTAL_PROPAL_LIST_ACCESS = Enable access to the proposals +WEBPORTAL_ORDER_LIST_ACCESS = Enable access to the orders +WEBPORTAL_INVOICE_LIST_ACCESS = Enable access to the invoices +WEBPORTAL_USER_LOGGED = Select a user +WebPortalUserLoggedHelp = This user is used to update cards + +# +# Public pages +# +WebPortalHomeTitle = Welcome +WebPortalHomeDesc = Welcome to the public interface +WebPortalPropalListMenu = Proposals +WebPortalPropalListTitle = List of proposals +WebPortalPropalListDesc = List of proposals +WebPortalPropalListNothing = Proposals not found +WebPortalOrderListMenu = Orders +WebPortalOrderListTitle = List of orders +WebPortalOrderListDesc = List of orders +WebPortalOrderListNothing = Orders not found +WebPortalInvoiceListMenu = Invoices +WebPortalInvoiceListTitle = List of invoices +WebPortalInvoiceListDesc = List of invoices +WebPortalInvoiceListNothing = Invoices not found +WebPortalMemberCardMenu = Member +WebPortalMemberCardTitle = Member card +WebPortalMemberCardDesc = Member card +WebPortalPartnershipCardMenu = Partnership +WebPortalPartnershipCardTitle = Partnership card +WebPortalPartnershipCardDesc = Partnership card + +# +# Errors +# +WebPortalError404 = Page not found +WebPortalErrorPageNotExist = Page not exist +WebPortalErrorFetchThirdPartyAccountFromLogin = Error when loading third-party account (login : %s) +WebPortalErrorAuthentication = Authentication error +WebPortalErrorFetchLoggedThirdPartyAccount = Error when loading third-party account (login : %s) +WebPortalErrorFetchLoggedUser = Error when loading user (Id : %s) +WebPortalErrorFetchLoggedThirdParty = Error when loading third-party (Id : %s) +WebPortalErrorFetchLoggedMember = Error when loading member (Id : %s) +WebPortalErrorFetchLoggedPartnership = Error when loading partnership (Third-party Id : %s, Member Id : %s) diff --git a/htdocs/webportal/lib/webportal.lib.php b/htdocs/webportal/lib/webportal.lib.php new file mode 100644 index 0000000000000..3a52f0eb90b7e --- /dev/null +++ b/htdocs/webportal/lib/webportal.lib.php @@ -0,0 +1,76 @@ + + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file lib/webportal.lib.php + * \ingroup webportal + * \brief Library files with common functions for WebPortal + */ + +/** + * Prepare admin pages header + * + * @return array + */ +function webportalAdminPrepareHead() +{ + global $langs, $conf; + + // global $db; + // $extrafields = new ExtraFields($db); + // $extrafields->fetch_name_optionals_label('myobject'); + + $langs->load("webportal@webportal"); + + $h = 0; + $head = array(); + + $head[$h][0] = dol_buildpath("/webportal/admin/setup.php", 1); + $head[$h][1] = $langs->trans("Settings"); + $head[$h][2] = 'settings'; + $h++; + + $head[$h][0] = dol_buildpath("/webportal/admin/configcss.php", 1); + $head[$h][1] = $langs->trans("CSSPage"); + $head[$h][2] = 'css'; + $h++; + + /* + $head[$h][0] = dol_buildpath("/webportal/admin/myobject_extrafields.php", 1); + $head[$h][1] = $langs->trans("ExtraFields"); + $nbExtrafields = is_countable($extrafields->attributes['myobject']['label']) ? count($extrafields->attributes['myobject']['label']) : 0; + if ($nbExtrafields > 0) { + $head[$h][1] .= ' ' . $nbExtrafields . ''; + } + $head[$h][2] = 'myobject_extrafields'; + $h++; + */ + + // Show more tabs from modules + // Entries must be declared in modules descriptor with line + //$this->tabs = array( + // 'entity:+tabname:Title:@webportal:/webportal/mypage.php?id=__ID__' + //); // to add new tab + //$this->tabs = array( + // 'entity:-tabname:Title:@webportal:/webportal/mypage.php?id=__ID__' + //); // to remove a tab + complete_head_from_modules($conf, $langs, null, $head, $h, 'webportal@webportal'); + + complete_head_from_modules($conf, $langs, null, $head, $h, 'webportal@webportal', 'remove'); + + return $head; +} diff --git a/htdocs/webportal/modulebuilder.txt b/htdocs/webportal/modulebuilder.txt new file mode 100644 index 0000000000000..670a177492904 --- /dev/null +++ b/htdocs/webportal/modulebuilder.txt @@ -0,0 +1,3 @@ +# DO NOT DELETE THIS FILE MANUALLY +# File to flag module built using official module template. +# When this file is present into a module directory, you can edit it with the module builder tool. \ No newline at end of file diff --git a/htdocs/webportal/public/class/context.class.php b/htdocs/webportal/public/class/context.class.php new file mode 100644 index 0000000000000..c61b5997626a8 --- /dev/null +++ b/htdocs/webportal/public/class/context.class.php @@ -0,0 +1,684 @@ +tplDir = __DIR__.'/../'; + + $this->getControllerUrl(); + + $this->topMenu = new stdClass(); + + $this->tplPath = realpath(__DIR__ .'/../tpl'); + + $this->controller = GETPOST('controller', 'aZ09'); // for security, limited to 'aZ09' + $this->action = GETPOST('action', 'aZ09');// for security, limited to 'aZ09' + + if (empty($this->controller)) { + $this->controller = 'default'; + } + + $this->appliName = !empty($conf->global->WEBPORTAL_TITLE) ? $conf->global->WEBPORTAL_TITLE : $conf->global->MAIN_INFO_SOCIETE_NOM; + + $this->generateNewToken(); + + $this->initController(); + + // Init de l'url de base + $this->rootUrl = self::getRootConfigUrl(); + } + + /** + * Singleton method to create one instance of this object + * + * @return Context Instance + */ + public static function getInstance() + { + if (is_null(self::$_instance)) { + self::$_instance = new Context(); + } + + return self::$_instance; + } + + /** + * Init controller + * + * @return bool + */ + public function initController() + { + global $db, $conf, $langs; + + $defaultControllersPath = __DIR__ . '/../controllers/'; + + // define controllers definition + $this->addControllerDefinition('login', $defaultControllersPath.'login.controller.php', 'LoginController'); + $this->addControllerDefinition('default', $defaultControllersPath.'default.controller.php', 'DefaultController'); + $this->addControllerDefinition('document', $defaultControllersPath.'document.controller.php', 'DocumentController'); + $this->addControllerDefinition('propallist', $defaultControllersPath.'propallist.controller.php', 'PropalListController'); + $this->addControllerDefinition('orderlist', $defaultControllersPath.'orderlist.controller.php', 'OrderListController'); + $this->addControllerDefinition('invoicelist', $defaultControllersPath.'invoicelist.controller.php', 'InvoiceListController'); + $this->addControllerDefinition('membercard', $defaultControllersPath.'membercard.controller.php', 'MemberCardController'); + $this->addControllerDefinition('partnershipcard', $defaultControllersPath.'partnershipcard.controller.php', 'PartnershipCardController'); + + // call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($db); + //$interface->run_triggers('WebPortalInitController', $this, $logged_user, $langs, $conf); + + // search for controller + $this->controllerInstance = new Controller(); + if (isset($this->controllers[$this->controller]) && file_exists($this->controllers[$this->controller]->path)) { + require_once $this->controllers[$this->controller]->path; + + if (class_exists($this->controllers[$this->controller]->class)) { + $this->controllerInstance = new $this->controllers[$this->controller]->class(); + $this->setControllerFound(); + } + } + } + + /** + * Add controller definition + * + * @param string $controller Name + * @param string $path Path + * @param string $className Class name + * @return bool + */ + public function addControllerDefinition($controller, $path, $className) + { + $fileName = basename($path); + $needle = '.controller.php'; + $length = strlen($needle); + $isControllerFile = $length > 0 ? substr($fileName, -$length) === $needle : true; + if (!$isControllerFile) { + $this->setError('Error: controller definition '.$fileName); + return false; + } + + $this->controllers[$controller] = new stdClass(); + $this->controllers[$controller]->path = $path; + $this->controllers[$controller]->class = $className; + + return true; + } + + /** + * Set controller found + * + * @return void + */ + public function setControllerFound() + { + $this->controller_found = true; + } + + /** + * Get WebPortal root url + * + * @return string Web Portal root url + */ + static public function getRootConfigUrl() + { + global $conf; + + // Init de l'url de base + if (!empty($conf->global->WEBPORTAL_ROOT_URL)) { + $rootUrl = $conf->global->WEBPORTAL_ROOT_URL; + if (substr($rootUrl, -1) !== '/') { + $rootUrl .= '/'; + } + } else { + $rootUrl = dol_buildpath('/webportal/public/', 2); + } + + return $rootUrl; + } + + /** + * Get root url + * + * @param string $controller Controller name + * @param string|array $moreParams More parameters + * @param bool $addToken Add token hash only if $controller is setted + * @return string + * @deprecated see getControllerUrl() + */ + public function getRootUrl($controller = false, $moreParams = '', $addToken = true) + { + return self::getControllerUrl($controller, $moreParams, $addToken); + } + + + /** + * Get controller url according to context + * + * @param string $controller Controller name + * @param string|array $moreParams More parameters + * @param bool $addToken add token hash only if $controller is setted + * @return string + */ + public function getControllerUrl($controller = false, $moreParams = '', $addToken = true) + { + $url = $this->rootUrl; + + if (empty($controller)) { + // because can be called without params to get only rootUrl + return $url; + } + + $Tparams = array(); + + $Tparams['controller'] = $controller; + + if (!empty($addToken)) { + $Tparams[$this->tokenKey] = $this->newToken(); + } + + return self::getPublicControllerUrl($controller, $moreParams, $Tparams); + } + + /** + * Generate public controller URL + * Used for external link (like email or web page) + * so remove token and contextual behavior associate with current user + * + * @param bool $controller Controller + * @param string|array $moreParams More parameters + * @param array $Tparams + * @return string + */ + static public function getPublicControllerUrl($controller = false, $moreParams = '', $Tparams = array()) + { + $url = self::getRootConfigUrl(); + + if (empty($controller)) { + // because can be called without params to get only rootUrl + return $url; + } + + $Tparams['controller'] = $controller; + + // if $moreParams is an array + if (!empty($moreParams) && is_array($moreParams)){ + if (isset($moreParams['controller'])) unset($moreParams['controller']); + if (!empty($moreParams)){ + foreach ($moreParams as $paramKey => $paramVal){ + $Tparams[$paramKey] = $paramVal; + } + } + } + + if (!empty($Tparams)){ + $TCompiledAttr = array(); + foreach ($Tparams as $key => $value) { + $TCompiledAttr[] = $key.'='.$value; + } + $url .= '?'.implode("&", $TCompiledAttr); + } + + // if $moreParams is a string + if (!empty($moreParams) && !is_array($moreParams)) { + if (empty($Tparams)) { + if ($moreParams[0] !== '?') $url .= '?'; + if ($moreParams[0] === '&') $moreParams = substr($moreParams, 1); + } + $url .= $moreParams; + } + + return $url; + } + + + /** + * Url origin + * + * @param bool $withRequestUri With request URI + * @param bool $use_forwarded_host Use formatted host + * @return string + */ + static public function urlOrigin($withRequestUri = true, $use_forwarded_host = false) + { + $s = $_SERVER; + + $ssl = ( ! empty($s['HTTPS']) && $s['HTTPS'] == 'on' ); + $sp = strtolower($s['SERVER_PROTOCOL']); + $protocol = substr($sp, 0, strpos($sp, '/')) . ( ( $ssl ) ? 's' : '' ); + $port = $s['SERVER_PORT']; + $port = ( ( ! $ssl && $port=='80' ) || ( $ssl && $port=='443' ) ) ? '' : ':'.$port; + $host = ( $use_forwarded_host && isset($s['HTTP_X_FORWARDED_HOST']) ) ? $s['HTTP_X_FORWARDED_HOST'] : ( isset($s['HTTP_HOST']) ? $s['HTTP_HOST'] : null ); + $host = isset($host) ? $host : $s['SERVER_NAME'] . $port; + + $url = $protocol . '://' . $host; + + if ($withRequestUri) { + $url.=$s['REQUEST_URI']; + } + + return $url; + } + + /** + * Check if user is logged + * + * @return bool + */ + public function userIsLog() + { + if (!empty($_SESSION["webportal_logged_thirdparty_account_id"])) { + return true; + } else { + return false; + } + } + + /** + * Is menu enabled ? + * + * @param $menuName Menu name + * @return bool + */ + public function menuIsActive($menuName) + { + return in_array($menuName, $this->menu_active); + } + + /** + * Set errors + * + * @param array $errors Errors + * @return void + */ + public function setError($errors) + { + if (!is_array($errors)) $errors = array($errors); + if (!isset($_SESSION['webportal_errors'])) $_SESSION['webportal_errors'] = array(); + foreach ($errors as $msg) + { + if (!in_array($msg, $_SESSION['webportal_errors'])) $_SESSION['webportal_errors'][] = $msg; + } + } + + /** + * Get errors + * + * @return int + */ + public function getErrors() + { + if (!empty($_SESSION['webportal_errors'])) { + $this->errors = array_values($_SESSION['webportal_errors']); + return count($this->errors); + } + + return 0; + } + + /** + * Clear errors + * + * @return void + */ + public function clearErrors() + { + unset($_SESSION['webportal_errors']); + $this->errors = array(); + } + + + /** + * Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events. + * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. + * + * @param string|string[] $mesgs Message string or array + * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') + * @return void + */ + public function setEventMessage($mesgs, $style = 'mesgs') + { + $TAcceptedStyle = array('mesgs', 'warnings', 'errors'); + + if (!in_array($style, $TAcceptedStyle)) { + $style='mesgs'; + } + + if (!is_array($mesgs)) { + $mesgs = array($mesgs); + } + if (!isset($_SESSION['webportal_events'])) { + $_SESSION['webportal_events'] = array( + 'mesgs' => array(), 'warnings' => array(), 'errors' => array() + ); + } + + foreach ($mesgs as $msg) { + if (!in_array($msg, $_SESSION['webportal_events'][$style])) { + $_SESSION['webportal_events'][$style][] = $msg; + } + } + } + + /** + * Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events. + * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. + * + * @param string $mesg Message string + * @param array|null $mesgs Message array + * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') + * @return void + */ + function setEventMessages($mesg, $mesgs, $style = 'mesgs') + { + if (empty($mesg) && empty($mesgs)) { + dol_syslog(__METHOD__.' Try to add a message in stack, but value to add is empty message', LOG_WARNING); + } else { + if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) { + dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages'); + } + if (empty($mesgs)) { + $this->setEventMessage($mesg, $style); + } else { + if (!empty($mesg) && !in_array($mesg, $mesgs)) { + $this->setEventMessage($mesg, $style); // Add message string if not already into array + } + $this->setEventMessage($mesgs, $style); + } + } + } + + + /** + * Load event messages + * + * @return int + */ + public function loadEventMessages() + { + if (!empty($_SESSION['webportal_events'])) { + $this->eventMessages = $_SESSION['webportal_events']; + return 1; + } + + return 0; + } + + /** + * Clear event messages + * + * @return void + */ + public function clearEventMessages() + { + unset($_SESSION['webportal_events']); + $this->eventMessages = array(); + } + + /** + * Return the value of token currently saved into session with name 'newToken'. + * This token must be send by any POST as it will be used by next page for comparison with value in session. + * This token depend of controller + * + * @param false|string $controller Controller + * @param bool $generateIfNull Generate if null + * @return string + */ + public function newToken($controller = false, $generateIfNull = true) + { + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + + if ( + !isset($_SESSION['controllers_tokens'][$controller]['newToken']) + && $generateIfNull + ) { + $this->generateNewToken($controller); + } + + return !empty($_SESSION['controllers_tokens'][$controller]['newToken']) ? $_SESSION['controllers_tokens'][$controller]['newToken'] : ''; + } + + /** + * Generate new token. + * + * @param false|string $controller Controller + * @return string + */ + protected function generateNewToken($controller = false) + { + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + + if (empty($_SESSION['controllers_tokens'])) { + $_SESSION['controllers_tokens'] = array(); + } + if (empty($_SESSION['controllers_tokens'][$controller])) { + $_SESSION['controllers_tokens'][$controller] = array(); + } + + // Creation of a token against CSRF vulnerabilities + if (!defined('NOTOKENRENEWAL')) { + // Rolling token at each call ($_SESSION['token'] contains token of previous page) + if (isset($_SESSION['controllers_tokens'][$controller]['newToken'])) { + $_SESSION['controllers_tokens'][$controller]['token'] = $_SESSION['controllers_tokens'][$controller]['newToken']; + } + + // Save what will be next token. Into forms, we will add param $context->newToken(); + $token = dol_hash(uniqid(mt_rand(), true)); // Generat + $_SESSION['controllers_tokens'][$controller]['newToken'] = $token; + + return $token; + } else { + return $this->newToken($controller, false); + } + } + + /** + * Return the value of token currently saved into session with name 'token'. + * + * @param bool $controller controller + * @return string + */ + public function currentToken($controller = false) + { + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + + return isset($_SESSION['controllers_tokens'][$controller]['token']) ? $_SESSION['controllers_tokens'][$controller]['token'] : false; + } + + /** + * Validate token + * + * @param false $controller Controller + * @param bool $erase Erase + * @return bool + */ + public function validateToken($controller = false, $erase = true) + { + $token = GETPOST($this->tokenKey, 'aZ09'); + + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + $currentToken = $this->currentToken($controller); + + if (empty($currentToken)) return false; + if (empty($token)) return false; + if ($currentToken === $token) { + if ($erase) { + unset($_SESSION['controllers_tokens'][$controller]['token']); + } + return true; + } + } + + /** + * Get token url + * + * @param false|string $controller Controller + * @return string|null + */ + public function getUrlToken($controller = false) + { + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + + $token = $this->newToken($controller); + if ($token) { + return '&'.$this->tokenKey.'='.$this->newToken($controller); + } + } + + /** + * Get token input for form + * + * @param false|string $controller Controller + * @return string|null + */ + public function getFormToken($controller = false) + { + if (empty($controller)) { + $controller = !empty($this->controller) ? $this->controller : 'default'; + } + + $token = $this->newToken($controller); + if ($token) { + return ''; + } + } + + /** + * Try to find the third-party account id from + * + * @param string $login Login + * @param string $pass Password + * @return int Third-party account id + */ + public function getThirdPartyAccountFromLogin($login, $pass) + { + global $db; + + $id = 0; + + $sql = "SELECT sa.rowid as id"; + $sql .= " FROM ".$db->prefix()."societe_account as sa"; + $sql .= " WHERE sa.login = '".$db->escape($login)."'"; + $sql .= " AND sa.pass_crypted = '".$db->escape($pass)."'"; + $sql .= " AND sa.site = 'dolibarr_portal'"; + $sql .= " AND sa.status = 1"; + $sql .= " AND sa.entity IN (".getEntity('societe').")"; + + dol_syslog(__METHOD__.' Try to find the third-party account id for login"'.$login.'" and site="dolibarr_portal"', LOG_DEBUG); + $result = $db->query($sql); + if ($result) { + if ($db->num_rows($result) == 1) { + $obj = $db->fetch_object($result); + $id = $obj->id; + } + } else { + $this->error = $db->lasterror(); + return -1; + } + + return $id; + } +} diff --git a/htdocs/webportal/public/class/controller.class.php b/htdocs/webportal/public/class/controller.class.php new file mode 100644 index 0000000000000..4c4840a8f1d6f --- /dev/null +++ b/htdocs/webportal/public/class/controller.class.php @@ -0,0 +1,196 @@ +tplPath if empty + */ + public $tplPath; + + + /** + * Constructeur de la classe + * + * @param void + * @return void + */ + public function __construct() + { + global $hookmanager; + + // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context + $hookmanager->initHooks(array('webportalpage', 'webportal')); + } + + /** + * Action method is called before html output + * can be used to manage security and change context + * + * @param void + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + public function action() + { + return $this->hookDoAction(); + } + + /** + * Check current access to controller + * + * @param void + * @return bool + */ + public function checkAccess() + { + $context = Context::getInstance(); + + if ($this->accessNeedLoggedUser) { + if (!$context->userIslog()) { + return false; + } + } + + if (!$this->accessRight) { + return false; + } + + return true; + } + + /** + * Display + * + * @param void + * @return void + */ + public function display() + { + $context = Context::getInstance(); + + $this->loadTemplate('header'); + + $this->hookPrintPageView(); + + if (!$context->controller_found) { + $this->loadTemplate('404'); + } + + $this->loadTemplate('footer'); + } + + /** + * Display error template + * + * @param void + * @return void + */ + public function display404() + { + $this->loadTemplate('header'); + $this->loadTemplate('404'); + $this->loadTemplate('footer'); + } + + /** + * Execute hook doActions + * + * @param array $parameters + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + public function hookDoAction($parameters = array()) + { + global $hookmanager; + + $context = Context::getInstance(); + + /* Use $context singleton to modify menu, */ + $parameters['controller'] = $context->controller; + + $reshook = $hookmanager->executeHooks('doActions',$parameters,$context, $context->action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + $context->setEventMessages($hookmanager->error,$hookmanager->errors,'errors'); + } + + return $reshook; + } + + /** + * Execute hook PrintPageView + * + * @param array $parameters + * @return int < 0 on error, 0 on success, 1 to replace standard code + */ + public function hookPrintPageView($parameters = array()) + { + global $hookmanager; + + $context = Context::getInstance(); + + /* Use $context singleton to modify menu, */ + $parameters['controller'] = $context->controller; + + $reshook = $hookmanager->executeHooks('PrintPageView', $parameters,$context, $context->action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + $context->setEventMessages($hookmanager->error,$hookmanager->errors,'errors'); + } + + return $reshook; + } + + /** + * Load a template + * + * @param string $templateName + * @param mixed $vars data to transmit to template + */ + public function loadTemplate($templateName, $vars = false) + { + global $conf, $langs, $hookmanager, $db; // load for tpl + + $context = Context::getInstance(); // load for tpl + + if (!preg_match('/^[0-9\.A-ZaZ_\-]*$/ui', $templateName)) { + return false; + } + + if (!empty($this->tplPath)) { + $tplPath = $this->tplPath . '/' . $templateName.'.tpl.php'; + if (file_exists($tplPath)) { + include $tplPath; + return true; + } + } + + $tplPath = $context->tplPath . '/' . $templateName.'.tpl.php'; + + if (!file_exists($tplPath)) { + print 'ERROR TPL NOT FOUND : '.$templateName; + return false; + } + + include $tplPath; + + return true; + } +} diff --git a/htdocs/webportal/public/class/html.formcardwebportal.class.php b/htdocs/webportal/public/class/html.formcardwebportal.class.php new file mode 100644 index 0000000000000..4f554b4197ac2 --- /dev/null +++ b/htdocs/webportal/public/class/html.formcardwebportal.class.php @@ -0,0 +1,845 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/** + * \file htdocs/webportal/public/class/html.formlistwebportal.class.php + * \ingroup webportal + * \brief File of class with all html predefined components for WebPortal + */ + +dol_include_once('/webportal/public/class/html.formwebportal.class.php'); + +/** + * Class to manage generation of HTML components + * Only common components for WebPortal must be here. + * + */ +class FormCardWebPortal +{ + /** + * @var string Action + */ + public $action = ''; + + /** + * @var string Back to page + */ + public $backtopage = ''; + + /** + * @var string Back to page for cancel + */ + public $backtopageforcancel = ''; + + /** + * @var string Back to page fo JS fields + */ + public $backtopagejsfields = ''; + + /** + * @var string Cancel + */ + public $cancel = ''; + + /** + * @var DoliDB Database + */ + public $db; + + /** + * @var string Element in english + */ + public $elementEn = ''; + + /** + * @var Form Instance of the Form + */ + public $form; + + /** + * @var int Id + */ + public $id; + + /** + * @var CommonObject Object + */ + public $object; + + /** + * @var int Permission to read + */ + public $permissiontoread = 0; + + /** + * @var int Permission to add + */ + public $permissiontoadd = 0; + + /** + * @var int Permission to delete + */ + public $permissiontodelete = 0; + + /** + * @var int Permission to note + */ + public $permissionnote = 0; + + /** + * @var int Permission to delete links + */ + public $permissiondellink = 0; + + /** + * @var string Ref + */ + public $ref; + + /** + * @var string Title key to translate + */ + public $titleKey = ''; + + /** + * @var string Title desc key to translate + */ + public $titleDescKey = ''; + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + $this->form = new FormWebPortal($this->db); + } + + /** + * Init + * + * @param string $elementEn Element (english) : "member" (for adherent), "partnership" + * @param int $id [=0] ID element + * @param int $permissiontoread [=0] Permission to read (0 : access forbidden by default) + * @param int $permissiontoadd [=0] Permission to add (0 : access forbidden by default), used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php + * @param int $permissiontodelete [=0] Permission to delete (0 : access forbidden by default) + * @param int $permissionnote [=0] Permission to note (0 : access forbidden by default) + * @param int $permissiondellink [=0] Permission to delete links (0 : access forbidden by default) + * @return void + */ + public function init($elementEn, $id = 0, $permissiontoread = 0, $permissiontoadd = 0, $permissiontodelete = 0, $permissionnote = 0, $permissiondellink = 0) + { + global $hookmanager, $langs; + + $elementEnUpper = strtoupper($elementEn); + $objectclass = 'WebPortal'.ucfirst($elementEn); + + $elementCardAccess = getDolGlobalString('WEBPORTAL_'.$elementEnUpper.'_CARD_ACCESS', 'hidden'); + if ($elementCardAccess == 'hidden' || $id <= 0) { + accessforbidden(); + } + + // load module libraries + dol_include_once('/webportal/class/webportal'.$elementEn.'.class.php'); + dol_include_once('/webportal/public/lib/webportal_webportal'.$elementEn.'.lib.php'); + + // Load translation files required by the page + $langs->loadLangs(array('webportal@webportal', 'other')); + + // Get parameters + //$id = $id > 0 ? $id : GETPOST('id', 'int'); + $ref = GETPOST('ref', 'alpha'); + $action = GETPOST('action', 'aZ09'); + $confirm = GETPOST('confirm', 'alpha'); + $cancel = GETPOST('cancel', 'aZ09'); + $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'webportal'.$elementEn.'card'; // To manage different context of search + $backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used + $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used + $backtopagejsfields = GETPOST('backtopagejsfields', 'alpha'); + + // Initialize technical objects + $object = new $objectclass($this->db); + //$extrafields = new ExtraFields($db); + $hookmanager->initHooks(array('webportal'.$elementEn.'card', 'globalcard')); // Note that conf->hooks_modules contains array + + // Fetch optionals attributes and labels + //$extrafields->fetch_name_optionals_label($object->table_element); + //$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); + + if (empty($action) && empty($id) && empty($ref)) { + $action = 'view'; + } + + // Load object + include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once. + + // Security check (enable the most restrictive one) + if (!isModEnabled('webportal')) { + accessforbidden(); + } + if (!$permissiontoread) { + accessforbidden(); + } + + // set form card + $this->action = $action; + $this->backtopage = $backtopage; + $this->backtopageforcancel = $backtopageforcancel; + $this->backtopagejsfields = $backtopagejsfields; + $this->cancel = $cancel; + $this->elementEn = $elementEn; + $this->id = $id; + $this->object = $object; + $this->permissiontoread = $permissiontoread; + $this->permissiontoadd = $permissiontoadd; + $this->permissiontodelete = $permissiontodelete; + $this->permissionnote = $permissionnote; + $this->permissiondellink = $permissiondellink; + $this->titleKey = $objectclass.'CardTitle'; + $this->ref = $ref; + } + + /** + * Do actions + * + * @return void + */ + public function doActions() + { + global $langs; + + // initialize + $action = $this->action; + $backtopage = $this->backtopage; + $backtopageforcancel = $this->backtopageforcancel; + $cancel = $this->cancel; + $elementEn = $this->elementEn; + $id = $this->id; + $object = $this->object; + //$permissiontoread = $this->permissiontoread; + $permissiontoadd = $this->permissiontoadd; + + $error = 0; + + $context = Context::getInstance(); + + $backurlforlist = $context->getControllerUrl('default'); + + if (empty($backtopage) || ($cancel && empty($id))) { + if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { + $backtopage = $context->getControllerUrl($elementEn.'card'); + } + } + + // Action to cancel record + if ($cancel) { + if (!empty($backtopageforcancel)) { + header("Location: ".$backtopageforcancel); + exit; + } elseif (!empty($backtopage)) { + header("Location: ".$backtopage); + exit; + } + $action = ''; + } + + // Action to update record + if ($action == 'update' && !empty($permissiontoadd)) { + foreach ($object->fields as $key => $val) { + // Check if field was submited to be edited + if ($object->fields[$key]['type'] == 'duration') { + if (!GETPOSTISSET($key.'hour') || !GETPOSTISSET($key.'min')) { + continue; // The field was not submited to be saved + } + } elseif ($object->fields[$key]['type'] == 'boolean') { + if (!GETPOSTISSET($key)) { + $object->$key = 0; // use 0 instead null if the field is defined as not null + continue; + } + } else { + if (!GETPOSTISSET($key) && !preg_match('/^chkbxlst:/', $object->fields[$key]['type']) && $object->fields[$key]['type']!=='checkbox') { + continue; // The field was not submited to be saved + } + } + // Ignore special fields + if (in_array($key, array('rowid', 'entity', 'import_key'))) { + continue; + } + if (in_array($key, array('date_creation', 'tms', 'fk_user_creat', 'fk_user_modif'))) { + if (!in_array(abs($val['visible']), array(1, 3, 4))) { + continue; // Only 1 and 3 and 4, that are cases to update + } + } + + // Set value to update + if (preg_match('/^text/', $object->fields[$key]['type'])) { + $tmparray = explode(':', $object->fields[$key]['type']); + if (!empty($tmparray[1])) { + $value = GETPOST($key, $tmparray[1]); + } else { + $value = GETPOST($key, 'nohtml'); + } + } elseif (preg_match('/^html/', $object->fields[$key]['type'])) { + $tmparray = explode(':', $object->fields[$key]['type']); + if (!empty($tmparray[1])) { + $value = GETPOST($key, $tmparray[1]); + } else { + $value = GETPOST($key, 'restricthtml'); + } + } elseif (in_array($object->fields[$key]['type'], array('date', 'datetime'))) { + $postDate = GETPOST($key, 'alphanohtml'); + // extract date YYYY-MM-DD for year, month and day + $dateArr = explode('-', $postDate); + $dateYear = 0; + $dateMonth = 0; + $dateDay = 0; + if (count($dateArr) == 3) { + $dateYear = (int)$dateArr[0]; + $dateMonth = (int)$dateArr[1]; + $dateDay = (int)$dateArr[2]; + } + // extract time HH:ii:ss for hours, minutes and seconds + $postTime = GETPOST($key . '_time', 'alphanohtml'); + $timeArr = explode(':', $postTime); + $timeHours = 12; + $timeMinutes = 0; + $timeSeconds = 0; + if (!empty($timeArr)) { + if (isset($timeArr[0])) { + $timeHours = (int)$timeArr[0]; + } + if (isset($timeArr[1])) { + $timeMinutes = (int)$timeArr[1]; + } + if (isset($timeArr[2])) { + $timeSeconds = (int)$timeArr[2]; + } + } + $value = dol_mktime($timeHours, $timeMinutes, $timeSeconds, $dateMonth, $dateDay, $dateYear); + } elseif ($object->fields[$key]['type'] == 'duration') { + if (GETPOST($key.'hour', 'int') != '' || GETPOST($key.'min', 'int') != '') { + $value = 60 * 60 * GETPOST($key.'hour', 'int') + 60 * GETPOST($key.'min', 'int'); + } else { + $value = ''; + } + } elseif (preg_match('/^(integer|price|real|double)/', $object->fields[$key]['type'])) { + $value = price2num(GETPOST($key, 'alphanohtml')); // To fix decimal separator according to lang setup + } elseif ($object->fields[$key]['type'] == 'boolean') { + $value = ((GETPOST($key, 'aZ09') == 'on' || GETPOST($key, 'aZ09') == '1') ? 1 : 0); + //} + //elseif ($object->fields[$key]['type'] == 'reference') { + // $value = array_keys($object->param_list)[GETPOST($key)].','.GETPOST($key.'2'); + } elseif (preg_match('/^chkbxlst:/', $object->fields[$key]['type']) || $object->fields[$key]['type'] == 'checkbox') { + $value = ''; + $values_arr = GETPOST($key, 'array'); + if (!empty($values_arr)) { + $value = implode(',', $values_arr); + } + } else { + if ($key == 'lang') { + $value = GETPOST($key, 'aZ09'); + } else { + $value = GETPOST($key, 'alphanohtml'); + } + } + if (preg_match('/^integer:/i', $object->fields[$key]['type']) && $value == '-1') { + $value = ''; // This is an implicit foreign key field + } + if (!empty($object->fields[$key]['foreignkey']) && $value == '-1') { + $value = ''; // This is an explicit foreign key field + } + + $object->$key = $value; + if ($val['notnull'] > 0 && $object->$key == '' && is_null($val['default'])) { + $error++; + $context->setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv($val['label'])), null, 'errors'); + } + + // Validation of fields values + if (getDolGlobalInt('MAIN_FEATURES_LEVEL') >= 2 || !empty($conf->global->MAIN_ACTIVATE_VALIDATION_RESULT)) { + if (!$error && !empty($val['validate']) && is_callable(array($object, 'validateField'))) { + if (!$object->validateField($object->fields, $key, $value)) { + $error++; + } + } + } + + if (isModEnabled('categorie')) { + $categories = GETPOST('categories', 'array'); + if (method_exists($object, 'setCategories')) { + $object->setCategories($categories); + } + } + } + + // Fill array 'array_options' with data from add form + //if (!$error) { + // $ret = $extrafields->setOptionalsFromPost(null, $object, '@GETPOSTISSET'); + // if ($ret < 0) { + // $error++; + // } + //} + + if (!$error) { + $result = $object->update($context->logged_user); + if ($result >= 0) { + $action = 'view'; + $urltogo = $backtopage ? str_replace('__ID__', $result, $backtopage) : $backurlforlist; + $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $object->id, $urltogo); // New method to autoselect project after a New on another form object creation + if ($urltogo && empty($noback)) { + header("Location: " . $urltogo); + exit; + } + } else { + $error++; + // Creation KO + $context->setEventMessages($object->error, $object->errors, 'errors'); + $action = 'edit'; + } + } else { + $action = 'edit'; + } + } + + $this->object = $object; + $this->action = $action; + } + + /** + * Html for header + * + * @param Context $context Context object + * @return string + */ + protected function header($context) + { + global $langs; + + $html = ''; + + // initialize + $object = $this->object; + $addgendertxt = ''; + //if (property_exists($object, 'gender') && !empty($object->gender)) { + // switch ($object->gender) { + // case 'man': + // $addgendertxt .= ''; + // break; + // case 'woman': + // $addgendertxt .= ''; + // break; + // case 'other': + // $addgendertxt .= ''; + // break; + // } + //} + + $html .= '
    '; + + // Left block - begin + $html .= '
    '; + $html .= '
    '; + + // logo or photo + $html .= '
    '; + + // main information - begin + $html .= '
    '; + // ref + $html .= '
    ' . $object->ref . '
    '; + // full name + $fullname = ''; + if (method_exists($object, 'getFullName')) { + $fullname = $object->getFullName($langs); + } + $html .= '
    '; + if ($object->element == 'member') { + if ($object->morphy == 'mor' && !empty($object->societe)) { + $html .= dol_htmlentities($object->societe); + $html .= (!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : ''; + } else { + $html .= dol_htmlentities($fullname) . $addgendertxt; + if (empty($object->fk_soc)) { + $html .= (!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities($object->societe) . ')' : ''; + } + } + } else { + $html .= dol_htmlentities(!empty($object->ref) ? $object->ref : ''); + } + $html .= '
    '; + // address + if (method_exists($object, 'getBannerAddressForWebPortal')) { + $moreaddress = $object->getBannerAddressForWebPortal('refaddress'); + if ($moreaddress) { + $html .= '
    '; + $html .= $moreaddress; + $html .= '
    '; + } + } + $html .= '
    '; + // main information - end + + $html .= '
    '; + $html .= '
    '; + // Left block - end + + // Right block - begin + $html .= '
    '; + // show status + $htmlStatus = $object->getLibStatut(6); + if (empty($htmlStatus) || $htmlStatus == $object->getLibStatut(3)) { + $htmlStatus = $object->getLibStatut(5); + } + $html .= $htmlStatus; + $html .= '
    '; + // Right block - end + + $html .= '
    '; + + return $html; + } + + /** + * Html for body (view mode) + * + * @return string + */ + protected function bodyView($keyforbreak = '') + { + global $langs; + + $html = ''; + + // initialize + $object = $this->object; + + $object->fields = dol_sort_array($object->fields, 'position'); + + // separate fields to show on the left and on the right + $fieldShowList = array(); + foreach ($object->fields as $key => $val) { + // discard if it's a hidden field on form + if (abs($val['visible']) != 1 && abs($val['visible']) != 3 && abs($val['visible']) != 4 && abs($val['visible']) != 5) { + continue; + } + + if (array_key_exists('enabled', $val) && isset($val['enabled']) && !verifCond($val['enabled'])) { + continue; // we don't want this field + } + + if (!empty($val['showonheader'])) { + continue; // already on header + } + + $fieldShowList[$key] = $val; + } + + $nbFieldShow = count($fieldShowList); + $lastKeyFieldLeft = $keyforbreak; + $lastNumFieldLeft = 0; + if ($lastKeyFieldLeft == '') { + $lastNumFieldLeft = ceil($nbFieldShow / 2); + } + $numField = 0; + $html .= '
    '; + $html .= '
    '; + foreach ($object->fields as $key => $val) { + if (!key_exists($key, $fieldShowList)) { + continue; // not to show + } + + $value = $object->$key; + + $html .= '
    '; + + $html .= '
    '; + $labeltoshow = ''; + $labeltoshow .= ''.$langs->trans($val['label']).''; + $html .= $labeltoshow; + $html .= '
    '; + + $html .= '
    '; + if ($key == 'lang') { + $langs->load('languages'); + $labellang = ($value ? $langs->trans('Language_'.$value) : ''); + //$html .= picto_from_langcode($value, 'class="paddingrightonly saturatemedium opacitylow"'); + $html .= $labellang; + } else { + $html .= $this->form->showOutputFieldForObject($object, $val, $key, $value, '', '', '', 0); + } + $html .= '
    '; + + $html .= '
    '; + + $numField++; + + // fields on the right + $cardRight = false; + if ($keyforbreak != '') { + if ($key == $keyforbreak) { + $cardRight = true; + } + } else { + if ($numField == $lastNumFieldLeft) { + $cardRight = true; + } + } + if ($cardRight === true) { + $html .= '
    '; + $html .= '
    '; + } + } + $html .= '
    '; + $html .= '
    '; + + return $html; + } + + /** + * Html for body (edit mode) + * + * @return string + */ + protected function bodyEdit() + { + global $langs; + + $html = ''; + + // initialize + $object = $this->object; + + $object->fields = dol_sort_array($object->fields, 'position'); + + foreach ($object->fields as $key => $val) { + // Discard if filed is a hidden field on form + if (abs($val['visible']) != 1 && abs($val['visible']) != 3 && abs($val['visible']) != 4) { + continue; + } + + if (array_key_exists('enabled', $val) && isset($val['enabled']) && !verifCond($val['enabled'])) { + continue; // We don't want this field + } + + $html .= '
    '; + $html .= '
    '; + $html .= $langs->trans($val['label']); + $html .= '
    '; + + $html .= '
    '; + if (in_array($val['type'], array('int', 'integer'))) { + $value = GETPOSTISSET($key) ?GETPOST($key, 'int') : $object->$key; + } elseif ($val['type'] == 'double') { + $value = GETPOSTISSET($key) ? price2num(GETPOST($key, 'alphanohtml')) : $object->$key; + } elseif (preg_match('/^text/', $val['type'])) { + $tmparray = explode(':', $val['type']); + if (!empty($tmparray[1])) { + $check = $tmparray[1]; + } else { + $check = 'nohtml'; + } + $value = GETPOSTISSET($key) ? GETPOST($key, $check) : $object->$key; + } elseif (preg_match('/^html/', $val['type'])) { + $tmparray = explode(':', $val['type']); + if (!empty($tmparray[1])) { + $check = $tmparray[1]; + } else { + $check = 'restricthtml'; + } + $value = GETPOSTISSET($key) ? GETPOST($key, $check) : $object->$key; + } elseif (in_array($val['type'], array('date', 'datetime'))) { + $isPostDate = GETPOSTISSET($key); + $isPostTime = GETPOSTISSET($key.'_time'); + if ($isPostDate) { + $postDate = GETPOST($key, 'alphanohtml'); + if ($isPostTime) { + $postTime = GETPOST($key . '_time', 'alphanohtml').':00'; + } else { + $postTime = '00:00:00'; + } + $valueDateTimeStr = $postDate.' '.$postTime; + } else { + // format date timestamp to YYYY-MM-DD HH:ii:ss + $valueDateTimeStr = dol_print_date($object->$key, '%Y-%m-%d %H:%M:%S'); + } + + $value = $valueDateTimeStr; + } elseif ($val['type'] == 'price') { + $value = GETPOSTISSET($key) ? price2num(GETPOST($key)) : price2num($object->$key); + } elseif ($key == 'lang') { + $value = GETPOSTISSET($key) ? GETPOST($key, 'aZ09') : $object->lang; + } else { + $value = GETPOSTISSET($key) ? GETPOST($key, 'alphanohtml') : $object->$key; + } + + if (!empty($val['noteditable'])) { + $html .= $this->form->showOutputFieldForObject($object, $val, $key, $value, '', '', '', 0); + } else { + $html .= $this->form->showInputField($val, $key, $value, '', '', '', ''); + } + $html .= '
    '; + $html .= '
    '; + } + + return $html; + } + + /** + * Html for footer + * + * @return string + */ + protected function footer() + { + $html = ''; + $html .= '
    '; + $html .= '
    '; + + return $html; + } + + /** + * Card for an element in the page context + * + * @param Context $context Context object + * @return string Html output + */ + public function elementCard($context) + { + global $hookmanager, $langs; + + $html = ''; + + // initialize + $action = $this->action; + $backtopage = $this->backtopage; + $backtopageforcancel = $this->backtopageforcancel; + //$backtopagejsfields = $this->backtopagejsfields; + //$elementEn = $this->elementEn; + $id = $this->id; + $object = $this->object; + //$permissiontoread = $this->permissiontoread; + $permissiontoadd = $this->permissiontoadd; + $ref = $this->ref; + $titleKey = $this->titleKey; + $title = $langs->trans($titleKey); + $url_file = $context->getControllerUrl($context->controller); + + // Part to edit record + if (($id || $ref) && $action == 'edit') { + $html .= '
    '; + //$html .= load_fiche_titre($title, '', 'object_'.$object->picto); + $html .= '
    '; + $html .= '

    '.$title.'

    '; + $html .= '
    '; + + $html .= '
    '; + $html .= $context->getFormToken(); + $html .= ''; + $html .= ''; + if ($backtopage) { + $html .= ''; + } + if ($backtopageforcancel) { + $html .= ''; + } + + //$html .= ''."\n"; + // Common attributes + //include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_edit.tpl.php'; + $html .= $this->bodyEdit(); + + // Other attributes + //include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_edit.tpl.php'; + //$html .= '
    '; + + // Save and Cancel buttons + $html .= '
    '; + $html .= '
    '; + $html .= '
    '; + $html .= '
    '; + + $html .= '
    '; + $html .= '
    '; + } + + // Part to show record + if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) { + $html .= '
    '; + + $formconfirm = ''; + + // Call Hook formConfirm + $parameters = array('formConfirm' => $formconfirm); + $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if (empty($reshook)) { + $formconfirm .= $hookmanager->resPrint; + } elseif ($reshook > 0) { + $formconfirm = $hookmanager->resPrint; + } + + // Print form confirm + $html .= $formconfirm; + + // Object card + // ------------------------------------------------------------ + $html .= $this->header($context); + + // Common attributes + $keyforbreak = ''; + $html .= $this->bodyView($keyforbreak); + + // Other attributes. Fields from hook formObjectOptions and Extrafields. + //include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php'; + + //$html .= $this->footer(); + $html .= '
    '; + + // Buttons for actions + if ($action != 'presend' && $action != 'editline') { + $html .= '
    '."\n"; + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + $context->setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + + if (empty($reshook)) { + if ($permissiontoadd) { + $html .= ''.$langs->trans('Modify').''; + } + } + $html .= '
    '."\n"; + } + } + + return $html; + } +} diff --git a/htdocs/webportal/public/class/html.formlistwebportal.class.php b/htdocs/webportal/public/class/html.formlistwebportal.class.php new file mode 100644 index 0000000000000..a99acef9e31be --- /dev/null +++ b/htdocs/webportal/public/class/html.formlistwebportal.class.php @@ -0,0 +1,736 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/** + * \file htdocs/webportal/public/class/html.formlistwebportal.class.php + * \ingroup webportal + * \brief File of class with all html predefined components for WebPortal + */ + +require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php'; +dol_include_once('/webportal/public/class/html.formwebportal.class.php'); + +/** + * Class to manage generation of HTML components + * Only common components for WebPortal must be here. + * + */ +class FormListWebPortal +{ + /** + * @var DoliDB Database + */ + public $db; + + /** + * @var Form Instance of the Form + */ + public $form; + + /** + * @var CommonObject Object + */ + public $object; + + /** + * @var int Limit (-1 to get limit from conf, 0 no limit, or Nb to show) + */ + public $limit = -1; + + /** + * @var int Page (1 by default) + */ + public $page = 1; + + /** + * @var string Sort field + */ + public $sortfield = ''; + + /** + * @var string Sort order + */ + public $sortorder = ''; + + /** + * @var string Title key to translate + */ + public $titleKey = ''; + + /** + * @var string Title desc key to translate + */ + public $titleDescKey = ''; + + /** + * @var string Page context + */ + public $contextpage = ''; + + /** + * @var array Search filters + */ + public $search = array(); + + /** + * @var array Array of fields + */ + public $arrayfields = array(); + + /** + * @var array Company static list (cache) + */ + public $companyStaticList = array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + $this->form = new FormWebPortal($this->db); + } + + /** + * Init + * + * @param string $elementEn Element (english) : "propal", "order", "invoice" + * @return void + */ + public function init($elementEn) + { + // keep compatibility + if ($elementEn == 'commande') { + $elementEn = 'order'; + }elseif ($elementEn == 'facture') { + $elementEn = 'invoice'; + } + + // load module libraries + dol_include_once('/webportal/class/webportal'.$elementEn.'.class.php'); + + // Initialize technical objects + $objectclass = 'WebPortal'.ucfirst($elementEn); + $object = new $objectclass($this->db); + + // set form list + $this->object = $object; + $this->limit = GETPOSTISSET('limit') ? (int) GETPOST('limit', 'int') : -1; + $this->sortfield = GETPOST('sortfield', 'aZ09comma'); + $this->sortorder = GETPOST('sortorder', 'aZ09comma'); + $this->page = GETPOSTISSET('page') ? (int) GETPOST('page', 'int') : 1; + $this->titleKey = $objectclass.'ListTitle'; + + // Initialize array of search criterias + //$search_all = GETPOST('search_all', 'alphanohtml'); + $search = array(); + foreach ($object->fields as $key => $val) { + if (GETPOST('search_'.$key, 'alpha') !== '') { + $search[$key] = GETPOST('search_'.$key, 'alpha'); + } + if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { + $postDateStart = GETPOST('search_'.$key.'_dtstart', 'alphanohtml'); + $postDateEnd = GETPOST('search_'.$key.'_dtend', 'alphanohtml'); + // extract date YYYY-MM-DD for year, month and day + $dateStartArr = explode('-', $postDateStart); + $dateEndArr = explode('-', $postDateEnd); + if (count($dateStartArr) == 3) { + $dateStartYear = (int) $dateStartArr[0]; + $dateStartMonth = (int) $dateStartArr[1]; + $dateStartDay = (int) $dateStartArr[2]; + $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, $dateStartMonth, $dateStartDay, $dateStartYear); + } + if (count($dateEndArr) == 3) { + $dateEndYear = (int) $dateEndArr[0]; + $dateEndMonth = (int) $dateEndArr[1]; + $dateEndDay = (int)$dateEndArr[2]; + $search[$key.'_dtend'] = dol_mktime(23, 59, 59, $dateEndMonth, $dateEndDay, $dateEndYear); + } + } + } + $this->search = $search; + + // List of fields to search into when doing a "search in all" + //$fieldstosearchall = array(); + + // Definition of array of fields for columns + $arrayfields = array(); + foreach ($object->fields as $key => $val) { + // If $val['visible']==0, then we never show the field + if (!empty($val['visible'])) { + $visible = (int) dol_eval($val['visible'], 1); + $arrayfields['t.'.$key] = array( + 'label'=>$val['label'], + 'checked'=>(($visible < 0) ? 0 : 1), + 'enabled'=>(abs($visible) != 3 && dol_eval($val['enabled'], 1)), + 'position'=>$val['position'], + 'help'=> isset($val['help']) ? $val['help'] : '' + ); + } + } + if ($elementEn == 'invoice') { + $arrayfields['remain_to_pay'] = array('type' => 'price', 'label' => 'RemainderToPay', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10000, 'help' => '',); + } + $arrayfields['download_link'] = array('label' => 'File', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10001, 'help' => '',); + + $object->fields = dol_sort_array($object->fields, 'position'); + //$arrayfields['anotherfield'] = array('type'=>'integer', 'label'=>'AnotherField', 'checked'=>1, 'enabled'=>1, 'position'=>90, 'csslist'=>'right'); + $arrayfields = dol_sort_array($arrayfields, 'position'); + + $this->arrayfields = $arrayfields; + } + + /** + * Do actions + * + * @return void + */ + public function doActions() + { + $object = $this->object; + $search = $this->search; + + // Purge search criteria + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers + foreach ($object->fields as $key => $val) { + $search[$key] = ''; + if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { + $search[$key.'_dtstart'] = ''; + $search[$key.'_dtend'] = ''; + } + } + $this->search = $search; + } + } + + /** + * List for an element in the page context + * + * @param Context $context Context object + * @return string Html output + */ + public function elementList($context) + { + global $conf, $hookmanager, $langs; + + $html = ''; + + // initialize + $object = $this->object; + $limit = $this->limit; + $page = $this->page; + $sortfield = $this->sortfield; + $sortorder = $this->sortorder; + $titleKey = $this->titleKey; + $contextpage = $this->contextpage; + $search = $this->search; + $arrayfields = $this->arrayfields; + $elementEn = $object->element; + if ($object->element == 'commande') { + $elementEn = 'order'; + } elseif ($object->element == 'facture') { + $elementEn = 'invoice'; + } + + // specific for invoice and remain to pay + $discount = null; + if ($elementEn == 'invoice') { + $discount = new DiscountAbsolute($this->db); + } + + // empty value for select + $emptyValueKey = ($elementEn == 'order' ? -5 : -1); + + if ($limit < 0) { + $limit = $conf->liste_limit; + } + if ($page <= 0) { + $page = 1; + } + $offset = $limit * ($page - 1); + if (!$sortfield) { + reset($object->fields); // Reset is required to avoid key() to return null. + $sortfield = 't.'.key($object->fields); // Set here default search field. By default 1st field in definition. + } + if (!$sortorder) { + $sortorder = 'DESC'; + } + + // Build and execute select + // -------------------------------------------------------------------- + $sql = 'SELECT '; + $sql .= $object->getFieldList('t'); + $sql .= ', t.entity as element_entity'; + // Add fields from hooks + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $sql .= $hookmanager->resPrint; + $sql = preg_replace('/,\s*$/', '', $sql); + + $sqlfields = $sql; // $sql fields to remove for count total + + $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; + // Add table from hooks + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $sql .= $hookmanager->resPrint; + if ($object->ismultientitymanaged == 1) { + $sql .= " WHERE t.entity IN (".getEntity($object->element, (GETPOST('search_current_entity', 'int') ? 0 : 1)).")"; + } else { + $sql .= " WHERE 1 = 1"; + } + // filter on logged third-party + $sql .= " AND t.fk_soc = ".$context->logged_thirdparty->id; + foreach ($search as $key => $val) { + if (array_key_exists($key, $object->fields)) { + if (($key == 'status' || $key == 'fk_statut') && $search[$key] == $emptyValueKey) { + continue; + } + $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0); + if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) { + if ($search[$key] == "$emptyValueKey" || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) { + $search[$key] = ''; + } + $mode_search = 2; + } + if ($search[$key] != '') { + $sql .= natural_search("t.".$this->db->escape($key), $search[$key], (($key == 'status' || $key == 'fk_statut') ? ($search[$key] < 0 ? 1 : 2) : $mode_search)); + } + } else { + if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') { + $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key); + if (preg_match('/^(date|timestamp|datetime)/', $object->fields[$columnName]['type'])) { + if (preg_match('/_dtstart$/', $key)) { + $sql .= " AND t.".$this->db->escape($columnName)." >= '".$this->db->idate($search[$key])."'"; + } + if (preg_match('/_dtend$/', $key)) { + $sql .= " AND t.".$this->db->escape($columnName)." <= '".$this->db->idate($search[$key])."'"; + } + } + } + } + } + //if ($search_all) { + // $sql .= natural_search(array_keys($fieldstosearchall), $search_all); + //} + // Add where from hooks + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $sql .= $hookmanager->resPrint; + + // Count total nb of records + $nbtotalofrecords = 0; + if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) { + /* The fast and low memory method to get and count full list converts the sql into a sql count */ + $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql); + $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount); + $resql = $this->db->query($sqlforcount); + if ($resql) { + $objforcount = $this->db->fetch_object($resql); + $nbtotalofrecords = (int) $objforcount->nbtotalofrecords; + } else { + dol_print_error($this->db); + } + + if ($offset > $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 1 + $page = 1; + $offset = 0; + } + + $this->db->free($resql); + } + + // Complete request and execute it with limit + $sql .= $this->db->order($sortfield, $sortorder); + if ($limit) { + $sql .= $this->db->plimit($limit, $offset); + } + + $resql = $this->db->query($sql); + if (!$resql) { + dol_print_error($this->db); + return ''; + } + + $num = $this->db->num_rows($resql); + if ($limit > 0) { + $nbpages = ceil($nbtotalofrecords / $limit); + } + if ($nbpages <= 0) { + $nbpages = 1; + } + + // make array[sort field => sort order] for this list + $sortList = array(); + $sortFieldList = explode(",", $sortfield); + $sortOrderList = explode(",", $sortorder); + $sortFieldIndex = 0; + if (!empty($sortFieldList)) { + foreach ($sortFieldList as $sortField) { + if (isset($sortOrderList[$sortFieldIndex])) { + $sortList[$sortField] = $sortOrderList[$sortFieldIndex]; + } + $sortFieldIndex++; + } + } + + $param = ''; + $param .= '&contextpage='.urlencode($contextpage); + $param .= '&limit='.$limit; + foreach ($search as $key => $val) { + if (is_array($search[$key])) { + foreach ($search[$key] as $skey) { + if ($skey != '') { + $param .= '&search_'.$key.'[]='.urlencode($skey); + } + } + } elseif (preg_match('/(_dtstart|_dtend)$/', $key) && !empty($val)) { + $param .= '&search_'.$key.'month='.((int) GETPOST('search_'.$key.'month', 'int')); + $param .= '&search_'.$key.'day='.((int) GETPOST('search_'.$key.'day', 'int')); + $param .= '&search_'.$key.'year='.((int) GETPOST('search_'.$key.'year', 'int')); + } elseif ($search[$key] != '') { + $param .= '&search_'.$key.'='.urlencode($search[$key]); + } + } + // Add $param from hooks + $parameters = array(); + $reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $param .= $hookmanager->resPrint; + + $url_file = $context->getControllerUrl($context->controller); + $html .= '
    '."\n"; + $html .= $context->getFormToken(); + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + + // pagination + $pagination_param = $param.'&sortfield='.$sortfield.'&sortorder='.$sortorder; + $html .= ''; + + // table with search filters and column titles + $html .= ''; + // title and desc for table + //if ($titleKey != '') { + // $html .= ''; + //} + + $html .= ''; + + // Fields title search + // -------------------------------------------------------------------- + $html .= ''; + // Action column + // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + $html .= ''; + // } + foreach ($object->fields as $key => $val) { + if (!empty($arrayfields['t.'.$key]['checked'])) { + $html .= ''; + } + } + // Fields from hook + $parameters = array('arrayfields'=>$arrayfields); + $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $html .= $hookmanager->resPrint; + // Remain to pay + if (!empty($arrayfields['remain_to_pay']['checked'])) { + $html .= ''; + } + // Download link + if (!empty($arrayfields['download_link']['checked'])) { + $html .= ''; + } + $html .= ''; + + + $totalarray = array(); + $totalarray['nbfield'] = 0; + + // Fields title label + // -------------------------------------------------------------------- + $html .= ''; + // Action column + // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + $html .= ''; + $totalarray['nbfield']++; + // } + foreach ($object->fields as $key => $val) { + $tableKey = 't.'.$key; + if (!empty($arrayfields[$tableKey]['checked'])) { + $tableOrder = ''; + if (key_exists($tableKey, $sortList)) { + $tableOrder = strtolower($sortList[$tableKey]); + } + $url_param = $url_file.'&sortfield='.$tableKey.'&sortorder='.($tableOrder == 'desc' ? 'asc' : 'desc').$param; + $html .= ''; + $totalarray['nbfield']++; + } + } + // Remain to pay + if (!empty($arrayfields['remain_to_pay']['checked'])) { + $html .= ''; + $totalarray['nbfield']++; + } + // Download link + if (!empty($arrayfields['download_link']['checked'])) { + $html .= ''; + $totalarray['nbfield']++; + } + + // Hook fields + $parameters = array('arrayfields'=>$arrayfields, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder, 'totalarray'=>&$totalarray); + $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $html .= $hookmanager->resPrint; + $html .= ''; + + $html .= ''; + + $html .= ''; + // Loop on record + // -------------------------------------------------------------------- + $i = 0; + $totalarray = array(); + $totalarray['nbfield'] = 0; + $imaxinloop = ($limit ? min($num, $limit) : $num); + while ($i < $imaxinloop) { + $obj = $this->db->fetch_object($resql); + if (empty($obj)) { + break; // Should not happen + } + + // Store properties in $object + $object->setVarsFromFetchObj($obj); + + // specific to get invoice status (depends on payment) + $payment = -1; + if ($elementEn == 'invoice') { + // store company + $idCompany = (int) $obj->fk_soc; + if (!isset($companyStaticList[$obj->fk_soc])) { + $companyStatic = new Societe($this->db); + $companyStatic->fetch($idCompany); + $companyStaticList[$idCompany] = $companyStatic; + } + $companyStatic = $companyStaticList[$obj->fk_soc]; + + // paid sum + $payment = $object->getSommePaiement(); + $totalcreditnotes = $object->getSumCreditNotesUsed(); + $totaldeposits = $object->getSumDepositsUsed(); + + // remain to pay + $totalpay = $payment + $totalcreditnotes + $totaldeposits; + $remaintopay = price2num($object->total_ttc - $totalpay); + if ($object->status == Facture::STATUS_CLOSED && $object->close_code == 'discount_vat') { // If invoice closed with discount for anticipated payment + $remaintopay = 0; + } + if ($object->type == Facture::TYPE_CREDIT_NOTE && $obj->paye == 1 && $discount) { + $remaincreditnote = $discount->getAvailableDiscounts($companyStatic, '', 'rc.fk_facture_source='.$object->id); + $remaintopay = -$remaincreditnote; + } + } + + // Show line of result + $html .= ''; + // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { + $html .= ''; + if (!$i) { + $totalarray['nbfield']++; + } + // } + foreach ($object->fields as $key => $val) { + if (!empty($arrayfields['t.'.$key]['checked'])) { + $html .= ''; + + + if (!$i) { + $totalarray['nbfield']++; + } + if (!empty($val['isameasure']) && $val['isameasure'] == 1) { + if (!$i) { + $totalarray['pos'][$totalarray['nbfield']] = 't.' . $key; + } + if (!isset($totalarray['val'])) { + $totalarray['val'] = array(); + } + if (!isset($totalarray['val']['t.' . $key])) { + $totalarray['val']['t.' . $key] = 0; + } + $totalarray['val']['t.' . $key] += $object->$key; + } + } + } + // Remain to pay + if (!empty($arrayfields['remain_to_pay']['checked'])) { + $html .= ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + // Download link + if (!empty($arrayfields['download_link']['checked'])) { + $element = $object->element; + $html .= ''; + if (!$i) { + $totalarray['nbfield']++; + } + } + // Fields from hook + $parameters = array('arrayfields'=>$arrayfields, 'object'=>$object, 'obj'=>$obj, 'i'=>$i, 'totalarray'=>&$totalarray); + $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $html .= $hookmanager->resPrint; + + + $html .= ''; + + $i++; + } + + // Move fields of totalizable into the common array pos and val + if (!empty($totalarray['totalizable']) && is_array($totalarray['totalizable'])) { + foreach ($totalarray['totalizable'] as $keytotalizable => $valtotalizable) { + $totalarray['pos'][$valtotalizable['pos']] = $keytotalizable; + $totalarray['val'][$keytotalizable] = isset($valtotalizable['total']) ? $valtotalizable['total'] : 0; + } + } + // Show total line + if (isset($totalarray['pos'])) { + $html .= ''; + $i = 0; + while ($i < $totalarray['nbfield']) { + $i++; + if (!empty($totalarray['pos'][$i])) { + $html .= ''; + } else { + if ($i == 1) { + $html .= ''; + } else { + $html .= ''; + } + } + } + $html .= ''; + } + + // If no record found + if ($num == 0) { + $colspan = 1; + foreach ($arrayfields as $key => $val) { + if (!empty($val['checked'])) { + $colspan++; + } + } + $html .= ''; + } + + $html .= ''; + + $this->db->free($resql); + + $parameters = array('arrayfields' => $arrayfields, 'sql' => $sql); + $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + $html .= $hookmanager->resPrint; + + $html .= '
    '; + // $html .= $langs->trans($titleKey) . '
    '; + // if ($titleDescKey != '') { + // $html .= '' . $langs->trans($titleDescKey) . ''; + // } + // $html .= '
    '; + $html .= ''; + $html .= ''; + $html .= ''; + if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { + $html .= $this->form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', ''); + } + //elseif ((strpos($val['type'], 'integer:') === 0) || (strpos($val['type'], 'sellist:') === 0)) { + // $html .= $object->showInputField($val, $key, (isset($search[$key]) ? $search[$key] : ''), '', '', 'search_', '', 1); + //} + elseif (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { + $postDateStart = GETPOST('search_'.$key.'_dtstart', 'alphanohtml'); + $postDateEnd = GETPOST('search_'.$key.'_dtend', 'alphanohtml'); + + $html .= '
    '; + $html .= $this->form->inputDate('search_'.$key.'_dtstart', $postDateStart ? $postDateStart : '', $langs->trans('From')); + $html .= '
    '; + $html .= '
    '; + $html .= $this->form->inputDate('search_'.$key.'_dtend', $postDateEnd ? $postDateEnd : '', $langs->trans('to')); + $html .= '
    '; + } else { + $html .= ''; + } + $html .= '
    '; + $html .= ''; + $html .= '
    '; + $html .= ''; + $html .= $langs->trans($arrayfields['t.'.$key]['label']); + $html .= ''; + $html .= ''; + $html .= $langs->trans($arrayfields['remain_to_pay']['label']);; + $html .= ''; + $html .= $langs->trans($arrayfields['download_link']['label']);; + $html .= '
    '; + $html .= ''; + if ($key == 'status' || $key == 'fk_statut') { + if ($elementEn == 'invoice') { + // specific to get invoice status (depends on payment) + $html .= $object->getLibStatut(5, $payment); + } else { + $html .= $object->getLibStatut(5); + } + } elseif ($key == 'rowid') { + $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->id, ''); + } else { + $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->$key, ''); + } + $html .= ''; + $html .= $this->form->showOutputFieldForObject($object, $arrayfields['remain_to_pay'], 'remain_to_pay', $remaintopay, ''); + //$html .= price($remaintopay); + $html .= ''; + $filename = dol_sanitizeFileName($obj->ref); + $filedir = $conf->{$element}->multidir_output[$obj->element_entity] . '/' . dol_sanitizeFileName($obj->ref); + $html .= $this->form->getDocumentsLink($element, $filename, $filedir); + $html .= '
    '; + $html .= price(!empty($totalarray['val'][$totalarray['pos'][$i]]) ? $totalarray['val'][$totalarray['pos'][$i]] : 0); + $html .= '' . $langs->trans("Total") . '
    '.$langs->trans("NoRecordFound").'
    '; + + $html .= '
    '; + + return $html; + } +} diff --git a/htdocs/webportal/public/class/html.formwebportal.class.php b/htdocs/webportal/public/class/html.formwebportal.class.php new file mode 100644 index 0000000000000..92f70fd184a56 --- /dev/null +++ b/htdocs/webportal/public/class/html.formwebportal.class.php @@ -0,0 +1,1318 @@ + + * Copyright (C) 2023 Lionel Vessiller + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/** + * \file htdocs/webportal/public/class/html.formwebportal.class.php + * \ingroup webportal + * \brief File of class with all html predefined components for WebPortal + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; + +/** + * Class to manage generation of HTML components + * Only common components for WebPortal must be here. + * + */ +class FormWebPortal extends Form +{ + /** + * @var DoliDB Database + */ + public $db; + + /** + * @var array $infofiles Array of file info + */ + public $infofiles; // Used to return informations by function getDocumentsLink + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + } + + /** + * Html for input with label + * + * @param string $type Type of input : button, checkbox, color, email, hidden, month, number, password, radio, range, tel, text, time, url, week + * @param string $name Name + * @param string $value [=''] Value + * @param string $id [=''] Id + * @param string $morecss [=''] Class + * @param string $moreparam [=''] Add attributes (checked, required, etc) + * @param string $label [=''] Label + * @param string $addInputLabel [=''] Add label for input + * @return string Html for input with label + */ + public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '') + { + $out = ''; + if ($label != '') { + $out .= '