diff --git a/README.md b/README.md index e1689d18..93b85540 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Informations - Numéro du module : 436301 -- Dernière mise à jour : 15/01/2023 +- Dernière mise à jour : 07/03/2023 - Éditeur : [Evarisk](https://evarisk.com) - Thème : Eldy Menu - Licence : GPLv3 @@ -11,10 +11,10 @@ ### Version -- Version : 1.10.0 +- Version : 1.11.0 - PHP : 7.4.33 -- Compatibilité : Dolibarr 17.0.0 - 18.0.4 -- Saturne Framework : 1.2.1 +- Compatibilité : Dolibarr 17.0.0 - 18.0.5 +- Saturne Framework : 1.3.0 ## Liens diff --git a/admin/control.php b/admin/control.php index f6b86024..960d4416 100644 --- a/admin/control.php +++ b/admin/control.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2022-2024 EVARISK * * 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 @@ -18,33 +18,31 @@ /** * \file admin/control.php * \ingroup digiquali - * \brief DigiQuali control config page. + * \brief DigiQuali control config page */ // Load DigiQuali environment if (file_exists('../digiquali.main.inc.php')) { - require_once __DIR__ . '/../digiquali.main.inc.php'; + require_once __DIR__ . '/../digiquali.main.inc.php'; } elseif (file_exists('../../digiquali.main.inc.php')) { - require_once __DIR__ . '/../../digiquali.main.inc.php'; + require_once __DIR__ . '/../../digiquali.main.inc.php'; } else { - die('Include of digiquali main fails'); + die('Include of digiquali main fails'); } -// Libraries +// Load Dolibarr libraries require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; -require_once __DIR__ . '/../lib/digiquali.lib.php'; +// Load DigiQuali libraries require_once __DIR__ . '/../class/control.class.php'; +require_once __DIR__ . '/../lib/digiquali.lib.php'; // Global variables definitions -global $conf, $db, $langs, $user; +global $conf, $db, $hookmanager, $langs, $moduleName, $moduleNameLowerCase, $user; // Load translation files required by the page saturne_load_langs(['admin']); -// Initialize view objects -$form = new Form($db); - // Get parameters $action = GETPOST('action', 'alpha'); $backtopage = GETPOST('backtopage', 'alpha'); @@ -55,19 +53,18 @@ $tmptype2label = ExtraFields::$type2label; $type2label = ['']; foreach ($tmptype2label as $key => $val) { - $type2label[$key] = $langs->transnoentitiesnoconv($val); + $type2label[$key] = $langs->transnoentitiesnoconv($val); } // Initialize objects -$object = new Control($db); -$elementType = $object->element; -$objectType = $object->element; -$elementtype = $moduleNameLowerCase . '_' . $objectType; // Must be the $table_element of the class that manage extrafield. +$object = new Control($db); + +$hookmanager->initHooks(['controladmin', 'globalcard']); // Note that conf->hooks_modules contains array -$error = 0; //Error counter +$elementtype = $moduleNameLowerCase . '_' . $object->element; // Must be the $table_element of the class that manage extrafield // Security check - Protection if external user -$permissiontoread = $user->rights->digiquali->adminpage->read; +$permissiontoread = $user->rights->$moduleNameLowerCase->adminpage->read; saturne_check_access($permissiontoread); /* @@ -77,18 +74,6 @@ //Extrafields actions require DOL_DOCUMENT_ROOT . '/core/actions_extrafields.inc.php'; -//Set numering modele for control object -if ($action == 'setmod') { - $constforval = 'DIGIQUALI_' . strtoupper('control') . '_ADDON'; - dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity); -} - -//Set numering modele for controldet object -if ($action == 'setmodControlDet') { - $constforval = 'DIGIQUALI_' . strtoupper('controldet') . '_ADDON'; - dolibarr_set_const($db, $constforval, $value, 'chaine', 0, '', $conf->entity); -} - if ($action == 'update_control_reminder') { $reminderFrequency = GETPOST('control_reminder_frequency'); $reminderType = GETPOST('control_reminder_type'); @@ -97,17 +82,10 @@ dolibarr_set_const($db, 'DIGIQUALI_CONTROL_REMINDER_TYPE', $reminderType, 'chaine', 0, '', $conf->entity); setEventMessage('SavedConfig'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; } -if ($action == 'update_public_survey_title') { - $publicSurveyTitle = GETPOST('public_survey_title'); - - dolibarr_set_const($db, 'DIGIQUALI_PUBLIC_SURVEY_TITLE', $publicSurveyTitle, 'chaine', 0, '', $conf->entity); - - setEventMessage('SavedConfig'); -} - - /* * View */ @@ -123,7 +101,7 @@ // Configuration header $head = digiquali_admin_prepare_head(); -print dol_get_fiche_head($head, 'control', $title, -1, 'digiquali_color@digiquali'); +print dol_get_fiche_head($head, $object->element, $title, -1, 'digiquali_color@digiquali'); /* * Numbering module @@ -133,158 +111,15 @@ require __DIR__ . '/../../saturne/core/tpl/admin/object/object_const_view.tpl.php'; -//Control data -print load_fiche_titre($langs->trans('ConfigData', $langs->transnoentities('ControlsMin')), '', ''); - -print ''; -print ''; -print ''; -print ''; -print ''; -print ''; - -//Display medias conf -print ''; - -print ''; -print ''; - -//Use large size media in gallery -print ''; - -print ''; -print ''; - -//Lock control if DMD/DLUO outdated -print ''; - -print ''; -print ''; -print '
' . $langs->trans('Name') . '' . $langs->trans('Description') . '' . $langs->trans('Status') . '
'; -print $langs->trans('DisplayMedias'); -print ''; -print $langs->trans('DisplayMediasDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_CONTROL_DISPLAY_MEDIAS'); -print '
'; -print $langs->trans('UseLargeSizeMedia'); -print ''; -print $langs->trans('UseLargeSizeMediaDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY'); -print '
'; -print $langs->trans('LockControlOutdatedEquipment'); -print ''; -print $langs->trans('LockControlOutdatedEquipmentDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_LOCK_CONTROL_OUTDATED_EQUIPMENT'); -print '
'; +/* + * Numbering module line + */ $object = new ControlLine($db); require __DIR__ . '/../../saturne/core/tpl/admin/object/object_numbering_module_view.tpl.php'; -require __DIR__ . '/../../saturne/core/tpl/admin/object/object_const_view.tpl.php'; - -//Control data -print load_fiche_titre($langs->trans('ConfigData', $langs->transnoentities('ControlsMin')), '', ''); - -print ''; -print ''; -print ''; -print ''; -print ''; -print ''; - -//Display medias conf -print ''; - -print ''; -print ''; - -//Use large size media in gallery -print ''; - -print ''; -print ''; - -// Auto-save action on question answer -print ''; - -print ''; -print ''; - -print ''; -print ''; -print ''; - -print ''; - -print ''; - -print ''; - -print ''; -print ''; - -print ''; - -print ''; -print ''; - -print ''; - -print ''; -print ''; - -print '
' . $langs->trans('Name') . '' . $langs->trans('Description') . '' . $langs->trans('Status') . '
'; -print $langs->trans('DisplayMedias'); -print ''; -print $langs->trans('DisplayMediasDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_CONTROL_DISPLAY_MEDIAS'); -print '
'; -print $langs->trans('UseLargeSizeMedia'); -print ''; -print $langs->trans('UseLargeSizeMediaDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY'); -print '
'; -print $langs->trans('AutoSaveActionQuestionAnswer'); -print ''; -print $langs->trans('AutoSaveActionQuestionAnswerDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_CONTROLDET_AUTO_SAVE_ACTION'); -print '
'; -print $langs->trans('PublicSurveyTitle'); -print ''; -print $langs->trans('PublicSurveyTitleDescription'); -print ''; -print ''; -print '
'; -print $langs->trans('EnablePublicControlHistory'); -print ''; -print $langs->trans('EnablePublicControlHistoryDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_ENABLE_PUBLIC_CONTROL_HISTORY'); -print '
'; -print $langs->trans('ShowQcFrequencyPublicInterface'); -print ''; -print $langs->trans('ShowQcFrequencyPublicInterfaceDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_SHOW_QC_FREQUENCY_PUBLIC_INTERFACE'); -print '
'; -print $langs->trans('ShowLastControlFirstOnPublicHistory'); -print ''; -print $langs->trans('ShowLastControlFirstOnPublicHistoryDescription'); -print ''; -print ajax_constantonoff('DIGIQUALI_SHOW_LAST_CONTROL_FIRST_ON_PUBLIC_HISTORY'); -print '
'; - -print '
'; - -print ''; - +// Control reminder print load_fiche_titre($langs->trans('ControlReminder'), '', ''); print '
'; @@ -297,6 +132,7 @@ print '' . $langs->trans('Value') . ''; print ''; +// Enable control reminder print ''; print $langs->trans('ControlReminder'); print ''; @@ -307,6 +143,7 @@ print ajax_constantonoff('DIGIQUALI_CONTROL_REMINDER_ENABLED'); print ''; +// Define control reminder frequency in days (ex: 30,60,90) print ''; print $langs->trans('ControlReminderFrequency'); print ''; @@ -317,6 +154,7 @@ print ''; print ''; +// Define control reminder type print ''; print $langs->trans('ControlReminderType'); print ''; @@ -333,32 +171,32 @@ print '
'; print ''; -//Extrafields control management +// Extrafields control management print load_fiche_titre($langs->trans('ExtrafieldsControlManagement'), '', ''); -require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; +$textobject = dol_strtolower($langs->transnoentities('Control')); +require DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_view.tpl.php'; // Buttons if ($action != 'create' && $action != 'edit') { - print '
'; - print ''; - print '
'; + print '
'; + print ''; + print '
'; } // Creation of an optional field if ($action == 'create') { - print load_fiche_titre($langs->trans('NewAttribute')); - require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_add.tpl.php'; + print load_fiche_titre($langs->trans('NewAttribute')); + require DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_add.tpl.php'; } // Edition of an optional field if ($action == 'edit' && !empty($attrname)) { - print load_fiche_titre($langs->trans('FieldEdition', $attrname)); - require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_edit.tpl.php'; + print load_fiche_titre($langs->trans('FieldEdition', $attrname)); + require DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_edit.tpl.php'; } // Page end print dol_get_fiche_end(); llxFooter(); $db->close(); - diff --git a/admin/publicinterface.php b/admin/publicinterface.php new file mode 100644 index 00000000..d093dbc1 --- /dev/null +++ b/admin/publicinterface.php @@ -0,0 +1,144 @@ + + * + * 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/publicinterface.php + * \ingroup digiquali + * \brief DigiQuali publicinterface config page + */ + +// Load DigiQuali environment +if (file_exists('../digiquali.main.inc.php')) { + require_once __DIR__ . '/../digiquali.main.inc.php'; +} elseif (file_exists('../../digiquali.main.inc.php')) { + require_once __DIR__ . '/../../digiquali.main.inc.php'; +} else { + die('Include of digiquali main fails'); +} + +// Load Dolibarr libraries +require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; + +// Load DigiQuali libraries +require_once __DIR__ . '/../lib/digiquali.lib.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $moduleName, $moduleNameLowerCase, $user; + +// Load translation files required by the page +saturne_load_langs(); + +// Get parameters +$action = GETPOST('action', 'alpha'); +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize objects + +// Initialize view objects +$form = new Form($db); + +$hookmanager->initHooks(['publicinterfaceadmin', 'globalcard']); // Note that conf->hooks_modules contains array + +// Security check - Protection if external user +$permissiontoread = $user->rights->$moduleNameLowerCase->adminpage->read; +saturne_check_access($permissiontoread); + +/* + * Actions + */ + +if ($action == 'set_public_interface_title') { + $answerPublicInterfaceTitle = GETPOST('DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE', 'none'); + dolibarr_set_const($db, 'DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE', $answerPublicInterfaceTitle, 'chaine', 0, '', $conf->entity); + + setEventMessage('SavedConfig'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; +} + +/* + * View + */ + +$title = $langs->trans('ModuleSetup', $moduleName); +$helpUrl = 'FR:Module_DigiQuali'; + +saturne_header(0,'', $title, $helpUrl); + +// Subheader +$linkBack = '' . $langs->trans('BackToModuleList') . ''; +print load_fiche_titre($title, $linkBack, 'title_setup'); + +// Configuration header +$head = digiquali_admin_prepare_head(); +print dol_get_fiche_head($head, 'publicinterface', $title, -1, 'digiquali_color@digiquali'); + +print load_fiche_titre($langs->trans('Config'), '', ''); + +print '
'; +print ''; +print ''; + +print ''; +print ''; +print ''; +print ''; +print ''; + +$substitutionArray = getCommonSubstitutionArray($langs); +complete_substitutions_array($substitutionArray, $langs); + +// Substitution array/string +$helpForSubstitution = ''; +if (is_array($substitutionArray) && count($substitutionArray)) { + $helpForSubstitution .= $langs->trans('AvailableVariables') . ' :
'; +} +foreach ($substitutionArray as $key => $val) { + if ($key != '__OBJECT_ELEMENT_REF__') { + $helpForSubstitution .= $key . ' -> '. $langs->trans(dol_string_nohtmltag(dolGetFirstLineOfText($val))) . '
'; + } else { + $helpForSubstitution .= $key . ' -> '. $langs->transnoentities('AnswerPublicInterfaceSubstitution') . '
'; + } +} + +// Public answer title +$answerPublicInterfaceTitle = $langs->transnoentities($conf->global->DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE) ?: $langs->transnoentities('AnswerPublicInterface'); +print ''; + +// Use signatory +print ''; +print ''; + +print '
' . $langs->trans('Parameters') . '' . $langs->trans('Value') . '
' . $form->textwithpicto($langs->transnoentities('AnswerPublicInterfaceTitle'), $helpForSubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody'); +print ''; +$dolEditor = new DolEditor('DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE', $answerPublicInterfaceTitle, '100%', 120, 'dolibarr_details', '', false, true, $conf->global->FCKEDITOR_ENABLE_MAIL, ROWS_2, 70); +$dolEditor->Create(); +print '
'; +print $langs->transnoentities('AnswerPublicInterfaceUseSignatory'); +print ''; +print $langs->transnoentities('AnswerPublicInterfaceUseSignatoryDescription'); +print ''; +print ajax_constantonoff('DIGIQUALI_ANSWER_PUBLIC_INTERFACE_USE_SIGNATORY'); +print '
'; +print $form->buttonsSaveCancel('Save', ''); +print '
'; + +// Page end +print dol_get_fiche_end(); +llxFooter(); +$db->close(); diff --git a/admin/sheet.php b/admin/sheet.php index 29042bee..c7fe2a4e 100644 --- a/admin/sheet.php +++ b/admin/sheet.php @@ -210,7 +210,7 @@ require_once __DIR__ . '/../../saturne/core/tpl/admin/object/object_const_view.tpl.php'; // Generate categories. -print load_fiche_titre($langs->trans('SheetCategories'), '', ''); +print load_fiche_titre($langs->trans('SheetCategories'), '', '', 0, 'sheetCategories'); print ''; print ''; diff --git a/admin/survey.php b/admin/survey.php new file mode 100644 index 00000000..d0e426fd --- /dev/null +++ b/admin/survey.php @@ -0,0 +1,138 @@ + + * + * 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/survey.php + * \ingroup digiquali + * \brief DigiQuali survey config page + */ + +// Load DigiQuali environment +if (file_exists('../digiquali.main.inc.php')) { + require_once __DIR__ . '/../digiquali.main.inc.php'; +} elseif (file_exists('../../digiquali.main.inc.php')) { + require_once __DIR__ . '/../../digiquali.main.inc.php'; +} else { + die('Include of digiquali main fails'); +} + +// Load Dolibarr libraries +require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; + +// Load DigiQuali libraries +require_once __DIR__ . '/../class/survey.class.php'; +require_once __DIR__ . '/../lib/digiquali.lib.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $moduleName, $moduleNameLowerCase, $user; + +// Load translation files required by the page +saturne_load_langs(['admin']); + +// Get parameters +$action = GETPOST('action', 'alpha'); +$backtopage = GETPOST('backtopage', 'alpha'); +$value = GETPOST('value', 'alpha'); +$attrname = GETPOST('attrname', 'alpha'); + +// List of supported format type extrafield label +$tmptype2label = ExtraFields::$type2label; +$type2label = ['']; +foreach ($tmptype2label as $key => $val) { + $type2label[$key] = $langs->transnoentitiesnoconv($val); +} + +// Initialize objects +$object = new Survey($db); + +$hookmanager->initHooks(['surveyadmin', 'globalcard']); // Note that conf->hooks_modules contains array + +$elementtype = $moduleNameLowerCase . '_' . $object->element; // Must be the $table_element of the class that manage extrafield + +// Security check - Protection if external user +$permissiontoread = $user->rights->$moduleNameLowerCase->adminpage->read; +saturne_check_access($permissiontoread); + +/* + * Actions + */ + +// Extrafields actions +require_once DOL_DOCUMENT_ROOT . '/core/actions_extrafields.inc.php'; + +/* + * View + */ + +$title = $langs->trans('ModuleSetup', $moduleName); +$helpUrl = 'FR:Module_DigiQuali'; + +saturne_header(0,'', $title, $helpUrl); + +// Subheader +$linkBack = '' . $langs->trans('BackToModuleList') . ''; +print load_fiche_titre($title, $linkBack, 'title_setup'); + +// Configuration header +$head = digiquali_admin_prepare_head(); +print dol_get_fiche_head($head, $object->element, $title, -1, 'digiquali_color@digiquali'); + +/* + * Numbering module + */ + +require __DIR__ . '/../../saturne/core/tpl/admin/object/object_numbering_module_view.tpl.php'; + +require __DIR__ . '/../../saturne/core/tpl/admin/object/object_const_view.tpl.php'; + +/* + * Numbering module line + */ + +$object = new SurveyLine($db); + +require __DIR__ . '/../../saturne/core/tpl/admin/object/object_numbering_module_view.tpl.php'; + +// Extrafields survey management +print load_fiche_titre($langs->trans('ExtrafieldsSurveyManagement'), '', ''); + +$textobject = dol_strtolower($langs->transnoentities('Survey')); +require_once DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_view.tpl.php'; + +// Buttons +if ($action != 'create' && $action != 'edit') { + print ''; +} + +// Creation of an optional field +if ($action == 'create') { + print load_fiche_titre($langs->trans('NewAttribute')); + require_once DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_add.tpl.php'; +} + +// Edition of an optional field +if ($action == 'edit' && !empty($attrname)) { + print load_fiche_titre($langs->trans('FieldEdition', $attrname)); + require_once DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_edit.tpl.php'; +} + +// Page end +print dol_get_fiche_end(); +llxFooter(); +$db->close(); diff --git a/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index 064acb81..14014f87 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -92,7 +92,13 @@ public function constructCategory($parameters, &$object) 'code' => 'control', 'obj_class' => 'Control', 'obj_table' => 'digiquali_control', - ] + ], + 'survey' => [ + 'id' => 436301004, + 'code' => 'survey', + 'obj_class' => 'Survey', + 'obj_table' => 'digiquali_survey', + ] ]; } @@ -123,6 +129,7 @@ public function doActions(array $parameters, $object, string $action): int require_once __DIR__ . '/../class/question.class.php'; require_once __DIR__ . '/../class/sheet.class.php'; require_once __DIR__ . '/../class/control.class.php'; + require_once __DIR__ . '/../class/survey.class.php'; } if (!$error) { @@ -234,11 +241,12 @@ public function printCommonFooter($parameters) /** * Overloading the formObjectOptions function : replacing the parent's function with the one below * - * @param array $parameters Hook metadata (context, etc...) - * @param object $object Object - * @return void + * @param array $parameters Hook metadatas (context, etc...) + * @param object|null $object Current object + * @return int 0 < on error, 0 on success, 1 to replace standard code */ - public function formObjectOptions(array $parameters, object $object) { + public function formObjectOptions(array $parameters, ?object $object): int + { if (strpos($parameters['context'], 'productlotcard') !== false) { $objectData = ['type' => $object->element, 'id' => $object->id]; @@ -250,6 +258,8 @@ public function formObjectOptions(array $parameters, object $object) { $object->updateExtrafield('control_history_link'); } } + + return 0; // or return 1 to replace standard code } /** @@ -291,9 +301,9 @@ public function saturneBannerTab(array $parameters, CommonObject $object): int global $conf, $langs; // Do something only for the current context. - if (strpos($parameters['context'], 'controlcard') !== false) { + if (preg_match('/controlcard|surveycard/', $parameters['context'])) { if ($conf->browser->layout == 'phone') { - $morehtmlref = '
' . img_picto('', 'fontawesome_fa-caret-square-down_far_#966EA2F2_fa-2em', 'class="toggleControlInfo pictofixedwidth valignmiddle" style="width: 35px;"') . $langs->trans('DisplayMoreInfo') . '
'; + $morehtmlref = '
' . img_picto('', 'fontawesome_fa-caret-square-down_far_#966EA2F2_fa-2em', 'class="toggle-object-infos pictofixedwidth valignmiddle" style="width: 35px;"') . $langs->trans('DisplayMoreInfo') . '
'; } else { $morehtmlref = ''; } @@ -304,42 +314,6 @@ public function saturneBannerTab(array $parameters, CommonObject $object): int return 0; // or return 1 to replace standard code. } - /** - * Overloading the printMainArea function : replacing the parent's function with the one below. - * - * @param array $parameters Hook metadatas (context, etc...). - * @return int 0 < on error, 0 on success, 1 to replace standard code. - */ - public function printMainArea(array $parameters): int - { - global $conf, $mysoc; - - // Do something only for the current context. - if (preg_match('/publiccontrol|publicsurvey|publiccontrolhistory/', $parameters['context'])) { - if (!empty($conf->global->SATURNE_SHOW_COMPANY_LOGO)) { - // Define logo and logoSmall. - $logoSmall = $mysoc->logo_small; - $logo = $mysoc->logo; - // Define urlLogo. - $urlLogo = ''; - if (!empty($logoSmall) && is_readable($conf->mycompany->dir_output . '/logos/thumbs/' . $logoSmall)) { - $urlLogo = DOL_URL_ROOT . '/viewimage.php?modulepart=mycompany&entity=' . $conf->entity . '&file=' . urlencode('logos/thumbs/' . $logoSmall); - } elseif (!empty($logo) && is_readable($conf->mycompany->dir_output . '/logos/' . $logo)) { - $urlLogo = DOL_URL_ROOT . '/viewimage.php?modulepart=mycompany&entity=' . $conf->entity . '&file=' . urlencode('logos/' . $logo); - } - // Output html code for logo. - if ($urlLogo) { - print ''; - } - print '
'; - } - } - - return 0; // or return 1 to replace standard code. - } - /** * Overloading the saturneAdminDocumentData function : replacing the parent's function with the one below. * @@ -354,6 +328,10 @@ public function saturneAdminDocumentData(array $parameters): int 'ControlDocument' => [ 'documentType' => 'controldocument', 'picto' => 'fontawesome_fa-tasks_fas_#d35968' + ], + 'SurveyDocument' => [ + 'documentType' => 'surveydocument', + 'picto' => 'fontawesome_fa-marker_fas_#d35968' ] ]; $this->results = $types; @@ -363,20 +341,71 @@ public function saturneAdminDocumentData(array $parameters): int } /** - * Overloading the saturneAdminObjectConst function : replacing the parent's function with the one below. + * Overloading the saturneAdminObjectConst function : replacing the parent's function with the one below * - * @param array $parameters Hook metadatas (context, etc...). - * @return int 0 < on error, 0 on success, 1 to replace standard code. + * @param array $parameters Hook metadatas (context, etc...) + * @return int 0 < on error, 0 on success, 1 to replace standard code */ public function saturneAdminObjectConst(array $parameters): int { - // Do something only for the current context. - if (strpos($parameters['context'], 'digiqualiadmindocuments') !== false) { + if (strpos($parameters['context'], 'surveyadmin') !== false) { + $constArray['digiquali'] = [ + 'DisplayMedias' => [ + 'name' => 'DisplayMediasSample', + 'description' => 'DisplaySurveyMediasSampleDescription', + 'code' => 'DIGIQUALI_SURVEY_DISPLAY_MEDIAS', + ], + 'UseLargeSizeMedia' => [ + 'name' => 'UseLargeSizeMedia', + 'description' => 'UseLargeSizeMediaDescription', + 'code' => 'DIGIQUALI_SURVEY_USE_LARGE_MEDIA_IN_GALLERY', + ], + 'AutoSaveActionQuestionAnswer' => [ + 'name' => 'AutoSaveActionQuestionAnswer', + 'description' => 'AutoSaveActionQuestionAnswerDescription', + 'code' => 'DIGIQUALI_SURVEYDET_AUTO_SAVE_ACTION', + ] + ]; + $this->results = $constArray; + return 1; + } + + if (strpos($parameters['context'], 'controladmin') !== false) { $constArray['digiquali'] = [ - 'controldocument' => [ - 'name' => 'ControlDocumentDisplayMedias', - 'description' => 'ControlDocumentDisplayMediasDescription', - 'code' => 'DIGIQUALI_CONTROLDOCUMENT_DISPLAY_MEDIAS' + 'DisplayMedias' => [ + 'name' => 'DisplayMediasSample', + 'description' => 'DisplayMediasSampleDescription', + 'code' => 'DIGIQUALI_CONTROL_DISPLAY_MEDIAS', + ], + 'UseLargeSizeMedia' => [ + 'name' => 'UseLargeSizeMedia', + 'description' => 'UseLargeSizeMediaDescription', + 'code' => 'DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY', + ], + 'LockControlOutdatedEquipment' => [ + 'name' => 'LockControlOutdatedEquipment', + 'description' => 'LockControlOutdatedEquipmentDescription', + 'code' => 'DIGIQUALI_LOCK_CONTROL_OUTDATED_EQUIPMENT', + ], + 'AutoSaveActionQuestionAnswer' => [ + 'name' => 'AutoSaveActionQuestionAnswer', + 'description' => 'AutoSaveActionQuestionAnswerDescription', + 'code' => 'DIGIQUALI_CONTROLDET_AUTO_SAVE_ACTION', + ], + 'EnablePublicControlHistory' => [ + 'name' => 'EnablePublicControlHistory', + 'description' => 'EnablePublicControlHistoryDescription', + 'code' => 'DIGIQUALI_ENABLE_PUBLIC_CONTROL_HISTORY', + ], + 'ShowQcFrequencyPublicInterface' => [ + 'name' => 'ShowQcFrequencyPublicInterface', + 'description' => 'ShowQcFrequencyPublicInterfaceDescription', + 'code' => 'DIGIQUALI_SHOW_QC_FREQUENCY_PUBLIC_INTERFACE', + ], + 'ShowLastControlFirstOnPublicHistory' => [ + 'name' => 'ShowLastControlFirstOnPublicHistory', + 'description' => 'ShowLastControlFirstOnPublicHistoryDescription', + 'code' => 'DIGIQUALI_SHOW_LAST_CONTROL_FIRST_ON_PUBLIC_HISTORY', ] ]; $this->results = $constArray; diff --git a/class/control.class.php b/class/control.class.php index 9d0cb757..b1ddb53f 100644 --- a/class/control.class.php +++ b/class/control.class.php @@ -126,7 +126,7 @@ class Control extends SaturneObject 'note_private' => ['type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 90, 'notnull' => 0, 'visible' => 0, 'showinpwa' => 0], 'verdict' => ['type' => 'smallint', 'label' => 'Verdict', 'enabled' => 1, 'position' => 110, 'notnull' => 0, 'visible' => 5, 'showinpwa' => 1, 'index' => 1, 'positioncard' => 20, 'arrayofkeyval' => [0 => '', 1 => 'OK', 2 => 'KO', 3 => 'N/A']], 'photo' => ['type' => 'text', 'label' => 'Photo', 'enabled' => 1, 'position' => 120, 'notnull' => 0, 'visible' => 0, 'showinpwa' => 0], - 'track_id' => ['type' => 'text', 'label' => 'TrackID', 'enabled' => 1, 'position' => 125, 'notnull' => 0, 'visible' => 0, 'showinpwa' => 0], + 'track_id' => ['type' => 'text', 'label' => 'TrackID', 'enabled' => 1, 'position' => 125, 'notnull' => 0, 'visible' => 2, 'showinpwa' => 0], 'fk_user_creat' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'picto' => 'user', 'enabled' => 1, 'position' => 130, 'notnull' => 1, 'visible' => 0, 'showinpwa' => 0, 'foreignkey' => 'user.rowid'], 'fk_user_modif' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'position' => 140, 'notnull' => 0, 'visible' => 0, 'showinpwa' => 0, 'foreignkey' => 'user.rowid'], 'fk_sheet' => ['type' => 'integer:Sheet:digiquali/class/sheet.class.php', 'label' => 'Sheet', 'picto' => 'fontawesome_fa-list_fas_#d35968', 'enabled' => 1, 'position' => 11, 'notnull' => 1, 'visible' => 5, 'showinpwa' => 0, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_sheet.rowid'], @@ -240,6 +240,16 @@ class Control extends SaturneObject */ public $projectid; + /** + * @var string Name of subtable line + */ + public $table_element_line = 'digiquali_controldet'; + + /** + * @var ControlLine[] Array of subtable lines + */ + public $lines = []; + /** * Constructor. * @@ -734,28 +744,6 @@ public function initAsSpecimen() $this->initAsSpecimenCommon(); } - /** - * Create an array of lines - * - * @return array|int array of lines if OK, <0 if KO - */ - public function getLinesArray() - { - $this->lines = []; - - $objectline = new ControlLine($this->db); - $result = $objectline->fetchAll('ASC', 'position', 0, 0, ['customsql' => 'fk_control = ' . $this->id]); - - if (is_numeric($result)) { - $this->error = $this->error; - $this->errors = $this->errors; - return $result; - } else { - $this->lines = $result; - return $this->lines; - } - } - /** * Load dashboard info. * @@ -907,22 +895,23 @@ public function getNbControlsByMonth(): array $array['picto'] = $this->picto; // Graph parameters. - $array['width'] = '100%'; - $array['height'] = 400; - $array['type'] = 'bars'; - $array['dataset'] = 3; + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'bars'; + $array['showlegend'] = 1; + $array['dataset'] = 3; $array['labels'] = [ 0 => [ - 'label' => $langs->trans("$years[0]"), + 'label' => $years[0], 'color' => '#9567AA' ], 1 => [ - 'label' => $langs->trans("$years[1]"), + 'label' => $years[1], 'color' => '#4F9EBE' ], 2 => [ - 'label' => $langs->trans("$years[2]"), + 'label' => $years[2], 'color' => '#FAC461' ] ]; @@ -1093,398 +1082,199 @@ public function getTriggerDescription(SaturneObject $object): string } } +/** + * Class for ControlLine + */ class ControlLine extends SaturneObject { /** - * @var string Module name. + * @var string Module name */ public $module = 'digiquali'; /** - * @var string ID to identify managed object - */ - public $element = 'controldet'; - - /** - * @var string Name of table without prefix where object is stored - */ - public $table_element = 'digiquali_controldet'; - - public $ref = ''; - - public $date_creation = ''; - - public $comment = ''; - - public $answer = ''; - - public $answer_photo = ''; - - public $fk_control = ''; - - public $fk_question = ''; - - /** - * @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, 'comment' => 'Id'), - 'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => '1', 'position' => 10, 'notnull' => 1, 'visible' => 1, 'noteditable' => '1', 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => '1', 'comment' => 'Reference of object'), - 'ref_ext' => array('type' => 'varchar(128)', 'label' => 'RefExt', 'enabled' => '1', 'position' => 20, 'notnull' => 0, 'visible' => 0,), - 'entity' => array('type' => 'integer', 'label' => 'Entity', 'enabled' => '1', 'position' => 30, 'notnull' => 1, 'visible' => 0,), - 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => '1', 'position' => 40, 'notnull' => 1, 'visible' => 0,), - 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => '1', 'position' => 50, 'notnull' => 0, 'visible' => 0,), - 'status' => array('type' => 'status', 'label' => 'Status', 'enabled' => '1', 'position' => 55, 'notnull' => 0, 'visible' => 0,), - 'answer' => array('type' => 'text', 'label' => 'Answer', 'enabled' => '1', 'position' => 60, 'notnull' => -1, 'visible' => 0,), - 'answer_photo' => array('type' => 'text', 'label' => 'AnswerPhoto', 'enabled' => '1', 'position' => 70, 'notnull' => -1, 'visible' => 0,), - 'comment' => array('type' => 'text', 'label' => 'Comment', 'enabled' => '1', 'position' => 80, 'notnull' => -1, 'visible' => 0,), - 'fk_question' => array('type' => 'integer', 'label' => 'FkQuestion', 'enabled' => '1', 'position' => 90, 'notnull' => 1, 'visible' => 0,), - 'fk_control' => array('type' => 'integer', 'label' => 'FkControl', 'enabled' => '1', 'position' => 100, 'notnull' => 1, 'visible' => 0,), - ); - - /** - * Constructor - * - * @param DoliDb $db Database handler - */ - public function __construct(DoliDB $db) - { - global $conf; - - $this->db = $db; - - if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible'] = 0; - if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled'] = 0; - } - - /** - * Load prevention plan line from database - * - * @param int $rowid id of invoice line to get - * @return int <0 if KO, >0 if OK - */ - public function fetch($id, ?string $ref = NULL, string $morewhere = ''): int - { - global $db; - - $sql = 'SELECT t.rowid, t.ref, t.date_creation, t.status, t.answer, t.answer_photo, t.comment, t.fk_question, t.fk_control '; - $sql .= ' FROM ' . MAIN_DB_PREFIX . 'digiquali_controldet as t'; - $sql .= ' WHERE t.rowid = ' . $id; - $sql .= ' AND entity IN (' . getEntity($this->table_element) . ')'; - - $result = $db->query($sql); - if ($result) { - $objp = $db->fetch_object($result); - - $this->id = $objp->rowid; - $this->ref = $objp->ref; - $this->date_creation = $objp->date_creation; - $this->status = $objp->status; - $this->answer = $objp->answer; - $this->answer_photo = $objp->answer_photo; - $this->comment = $objp->comment; - $this->fk_question = $objp->fk_question; - $this->fk_control = $objp->fk_control; - - $db->free($result); - - return $this->id; - } else { - $this->error = $db->lasterror(); - return -1; - } - } - - /** - * Load control line from database - * - * @param int $parent_id - * @param int $limit - * @return int <0 if KO, >0 if OK - */ - public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND') - { - global $db; - $sql = 'SELECT t.rowid, t.ref, t.date_creation, t.status, t.answer, t.answer_photo, t.comment, t.fk_question, t.fk_control '; - $sql .= ' FROM ' . MAIN_DB_PREFIX . 'digiquali_controldet as t'; - $sql .= ' WHERE entity IN (' . getEntity($this->table_element) . ')'; - - $result = $db->query($sql); - - if ($result) { - $num = $db->num_rows($result); - - $i = 0; - while ($i < ($limit ? min($limit, $num) : $num)) { - $obj = $db->fetch_object($result); - - $record = new self($db); - - $record->id = $obj->rowid; - $record->ref = $obj->ref; - $record->date_creation = $obj->date_creation; - $record->status = $obj->status; - $record->answer = $obj->answer; - $record->answer_photo = $obj->answer_photo; - $record->comment = $obj->comment; - $record->fk_question = $obj->fk_question; - $record->fk_control = $obj->fk_control; - - $records[$record->id] = $record; - - $i++; - } - - $db->free($result); - - return $records; - } else { - $this->error = $db->lasterror(); - return -1; - } - } - - /** - * Load control line from database and from parent - * - * @param int $parent_id - * @param int $limit - * @return int <0 if KO, >0 if OK - */ - public function fetchFromParent($control_id, $limit = 0) - { - global $db; - $sql = 'SELECT t.rowid, t.ref, t.date_creation, t.status, t.answer, t.answer_photo, t.comment, t.fk_question, t.fk_control '; - $sql .= ' FROM ' . MAIN_DB_PREFIX . 'digiquali_controldet as t'; - $sql .= ' WHERE entity IN (' . getEntity($this->table_element) . ')'; - $sql .= ' AND fk_control = ' . $control_id; - - $result = $db->query($sql); - - if ($result) { - $num = $db->num_rows($result); - - $i = 0; - while ($i < ($limit ? min($limit, $num) : $num)) { - $obj = $db->fetch_object($result); - - $record = new self($db); - - $record->id = $obj->rowid; - $record->ref = $obj->ref; - $record->date_creation = $obj->date_creation; - $record->status = $obj->status; - $record->answer = $obj->answer; - $record->answer_photo = $obj->answer_photo; - $record->comment = $obj->comment; - $record->fk_question = $obj->fk_question; - $record->fk_control = $obj->fk_control; - - $records[$record->id] = $record; - - $i++; - } - - $db->free($result); - - return $records; - } else { - $this->error = $db->lasterror(); - return -1; - } - } - - /** - * Load control line from database form parent with question - * - * @param int $control_id - * @param int $question_id - * @return int <0 if KO, >0 if OK - */ - public function fetchFromParentWithQuestion($control_id, $question_id, $limit = 0) - { - global $db; - $sql = 'SELECT t.rowid, t.ref, t.date_creation, t.status, t.answer, t.answer_photo, t.comment, t.fk_question, t.fk_control '; - $sql .= ' FROM ' . MAIN_DB_PREFIX . 'digiquali_controldet as t'; - $sql .= ' WHERE entity IN (' . getEntity($this->table_element) . ')'; - $sql .= ' AND fk_control = ' . $control_id .' AND fk_question ='. $question_id; - + * @var string Element type of object + */ + public $element = 'controldet'; - $result = $db->query($sql); + /** + * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management + */ + public $table_element = 'digiquali_controldet'; - if ($result) { - $num = $db->num_rows($result); + /** + * @var int Does this object support multicompany module ? + * 0 = No test on entity, 1 = Test with field entity, 'field@table' = Test with link by field@table + */ + public $ismultientitymanaged = 1; - $i = 0; - while ($i < ($limit ? min($limit, $num) : $num)) { - $obj = $db->fetch_object($result); + /** + * @var int Does object support extrafields ? 0 = No, 1 = Yes + */ + public int $isextrafieldmanaged = 1; - $record = new self($db); + /** + * '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 can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') 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 '$conf->global->MY_SETUP_PARAM' or '!empty($conf->multicurrency->enabled)' ...) + * '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) + * 'default' is a default value for creation (can still be overwroted 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' 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 you 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 + */ - $record->id = $obj->rowid; - $record->ref = $obj->ref; - $record->date_creation = $obj->date_creation; - $record->status = $obj->status; - $record->answer = $obj->answer; - $record->answer_photo = $obj->answer_photo; - $record->comment = $obj->comment; - $record->fk_question = $obj->fk_question; - $record->fk_control = $obj->fk_control; + /** + * @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 = [ + 'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'comment' => 'Id'], + 'ref' => ['type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'visible' => 1, 'noteditable' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'validate' => 1, 'comment' => 'Reference of object'], + 'ref_ext' => ['type' => 'varchar(128)', 'label' => 'RefExt', 'enabled' => 1, 'position' => 20, 'notnull' => 0, 'visible' => 0], + 'entity' => ['type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 0, 'index' => 1], + 'date_creation' => ['type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 40, 'notnull' => 1, 'visible' => 0], + 'tms' => ['type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => 0], + 'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 0, 'index' => 0], + 'status' => ['type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 70, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'default' => 1], + 'type' => ['type' => 'varchar(128)', 'label' => 'Type', 'enabled' => 0, 'position' => 80, 'notnull' => 0, 'visible' => 0], + 'answer' => ['type' => 'text', 'label' => 'Answer', 'enabled' => 1, 'position' => 90, 'notnull' => 0, 'visible' => 0], + 'answer_photo' => ['type' => 'text', 'label' => 'AnswerPhoto', 'enabled' => 0, 'position' => 100, 'notnull' => 0, 'visible' => 0], + 'comment' => ['type' => 'text', 'label' => 'Comment', 'enabled' => 1, 'position' => 110, 'notnull' => 0, 'visible' => 0], + 'fk_user_creat' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'picto' => 'user', 'enabled' => 1, 'position' => 120, 'notnull' => 1, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_user_modif' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'position' => 130, 'notnull' => 0, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_control' => ['type' => 'integer:Control:digiquali/class/survey.class.php', 'label' => 'Control', 'picto' => 'fontawesome_fa-tasks_fas_#d35968', 'enabled' => 1, 'position' => 140, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_survey.rowid'], + 'fk_question' => ['type' => 'integer:Question:digiquali/class/question.class.php', 'label' => 'Question', 'picto' => 'fontawesome_fa-question_fas_#d35968', 'enabled' => 1, 'position' => 150, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_question.rowid'], + ]; - $records[$record->id] = $record; + /** + * @var int ID + */ + public int $rowid; - $i++; - } + /** + * @var string Ref + */ + public $ref; - $db->free($result); + /** + * @var string Ref ext + */ + public $ref_ext; - return $records; - } else { - $this->error = $db->lasterror(); - return -1; - } + /** + * @var int Entity + */ + public $entity; - } + /** + * @var int|string Creation date + */ + public $date_creation; - /** - * Insert line into database - * - * @param User $user - * @param bool $notrigger 1 no triggers - * @return int <0 if KO, >0 if OK - * @throws Exception - */ - public function insert(User $user, $notrigger = false) - { - global $db, $user; - - // Clean parameters - $this->description = trim($this->description); - - $db->begin(); - $now = dol_now(); - - // Insertion dans base de la ligne - $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'digiquali_controldet'; - $sql .= ' ( ref, entity, status, date_creation, answer, answer_photo, comment, fk_question, fk_control, fk_user_creat'; - $sql .= ')'; - $sql .= ' VALUES ('; - $sql .= "'" . $db->escape($this->ref) . "'" . ', '; - $sql .= $this->entity . ', '; - $sql .= 1 . ', '; - $sql .= "'" . $db->escape($db->idate($now)) . "'" . ', '; - $sql .= "'" . $db->escape($this->answer) . "'" . ', '; - $sql .= "'" . $db->escape($this->answer_photo) . "'" . ', '; - $sql .= "'" . $db->escape($this->comment) . "'" . ', '; - $sql .= $this->fk_question . ', '; - $sql .= $this->fk_control . ', '; - $sql .= $user->id; - - $sql .= ')'; - - dol_syslog(get_class($this) . '::insert', LOG_DEBUG); - $resql = $db->query($sql); + /** + * @var int|string Timestamp + */ + public $tms; - if ($resql) { - $this->id = $db->last_insert_id(MAIN_DB_PREFIX . 'controldet'); - $this->rowid = $this->id; // For backward compatibility - - $db->commit(); - // Triggers - if ( ! $notrigger) { - // Call triggers - $this->call_trigger(strtoupper(get_class($this)) . '_CREATE', $user); - // End call triggers - } - return $this->id; - } else { - $this->error = $db->lasterror(); - $db->rollback(); - return -2; - } - } + /** + * @var string Import key + */ + public $import_key; - /** - * Update line into database - * - * @param User $user User object - * @param int $notrigger Disable triggers - * @return int <0 if KO, >0 if OK - * @throws Exception - */ - public function update(User $user, $notrigger = false): int - { - global $user, $db; + /** + * @var int Status + */ + public $status; - $error = 0; + /** + * @var string|null Type + */ + public ?string $type; - // Clean parameters - $this->description = trim($this->description); + /** + * @var string|null Answer + */ + public ?string $answer = ''; - $db->begin(); + /** + * @var string|null Answer photo + */ + public ?string $answer_photo; - // Mise a jour ligne en base - $sql = 'UPDATE ' . MAIN_DB_PREFIX . 'digiquali_controldet SET'; + /** + * @var string|null Comment + */ + public ?string $comment = ''; - $sql .= " ref='" . $db->escape($this->ref) . "',"; - $sql .= " status='" . $db->escape($this->status) . "',"; - $sql .= " answer='" . $db->escape($this->answer) . "',"; - $sql .= ' answer_photo=' . '"' . $db->escape($this->answer_photo) . '"' . ','; - $sql .= ' comment=' . '"' . $db->escape($this->comment) . '"' . ','; - $sql .= ' fk_question=' . $db->escape($this->fk_question). ','; - $sql .= ' fk_control=' . $db->escape($this->fk_control); + /** + * @var int User ID + */ + public int $fk_user_creat; - $sql .= ' WHERE rowid = ' . $this->id; + /** + * @var int|null User ID + */ + public ?int $fk_user_modif; - dol_syslog(get_class($this) . '::update', LOG_DEBUG); + /** + * @var int Control ID + */ + public int $fk_control; - $resql = $db->query($sql); + /** + * @var ?int|null Question ID + */ + public int $fk_question; - if ($resql) { - $db->commit(); - // Triggers - if ( ! $notrigger) { - // Call triggers - $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $user); - // End call triggers - } - return $this->id; - } else { - $this->error = $db->error(); - $db->rollback(); - return -2; - } - } + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->element); + } - /** - * Delete line in database - * - * @return int <0 if KO, >0 if OK - * @throws Exception - */ - public function delete(User $user, $notrigger = false, bool $softDelete = true): int - { - global $user, $db; - - $db->begin(); - - $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'digiquali_controldet WHERE rowid = ' . $this->id; - dol_syslog(get_class($this) . '::delete', LOG_DEBUG); - if ($db->query($sql)) { - $db->commit(); - // Triggers - if ( ! $notrigger) { - // Call trigger - $this->call_trigger(strtoupper(get_class($this)) . '_DELETE', $user); - // End call triggers - } - return 1; - } else { - $this->error = $db->error() . ' sql=' . $sql; - $db->rollback(); - return -1; - } - } + /** + * Load control line from database form parent with question + * + * @param int $controlID Control id + * @param int $questionID Question id + * @return array|int Int <0 if KO, array of pages if OK + * @throws Exception + */ + public function fetchFromParentWithQuestion(int $controlID, int $questionID) + { + return $this->fetchAll('', '', 1, 0, ['customsql' => 't.fk_control = ' . $controlID . ' AND t.fk_question = ' . $questionID . ' AND t.status > 0']); + } } class ControlEquipment extends SaturneObject diff --git a/class/digiqualidashboard.class.php b/class/digiqualidashboard.class.php index ee5605b1..8fc8f6d2 100644 --- a/class/digiqualidashboard.class.php +++ b/class/digiqualidashboard.class.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2021-2024 EVARISK * * 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 @@ -18,23 +18,23 @@ /** * \file class/digiqualidashboard.class.php * \ingroup digiquali - * \brief Class file for manage DigiqualiDashboard. + * \brief Class file for manage DigiqualiDashboard */ /** - * Class for DigiqualiDashboard. + * Class for DigiqualiDashboard */ class DigiqualiDashboard { /** - * @var DoliDB Database handler. + * @var DoliDB Database handler */ public DoliDB $db; /** - * Constructor. + * Constructor * - * @param DoliDB $db Database handler. + * @param DoliDB $db Database handler */ public function __construct(DoliDB $db) { @@ -42,7 +42,7 @@ public function __construct(DoliDB $db) } /** - * Load dashboard info. + * Load dashboard info * * @return array * @throws Exception @@ -50,10 +50,13 @@ public function __construct(DoliDB $db) public function load_dashboard(): array { require_once __DIR__ . '/control.class.php'; + require_once __DIR__ . '/survey.class.php'; $control = new Control($this->db); + $survey = new Survey($this->db); $array['control'] = $control->load_dashboard(); + $array['survey'] = $survey->load_dashboard(); return $array; } diff --git a/class/digiqualidocuments/surveydocument.class.php b/class/digiqualidocuments/surveydocument.class.php new file mode 100644 index 00000000..5eadb942 --- /dev/null +++ b/class/digiqualidocuments/surveydocument.class.php @@ -0,0 +1,51 @@ + + * + * 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/digiqualidocuments/surveydocument.class.php + * \ingroup digiquali + * \brief This file is a class file for SurveyDocument + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../../saturne/class/saturnedocuments.class.php'; + +/** + * Class for SurveyDocument + */ +class SurveyDocument extends SaturneDocuments +{ + /** + * @var string Module name + */ + public $module = 'digiquali'; + + /** + * @var string Element type of object + */ + public $element = 'surveydocument'; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->element); + } +} diff --git a/class/sheet.class.php b/class/sheet.class.php index 749ddd11..db658e5d 100644 --- a/class/sheet.class.php +++ b/class/sheet.class.php @@ -116,7 +116,7 @@ class Sheet extends SaturneObject 'tms' => ['type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 50, 'notnull' => 1, 'visible' => 0], 'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 0, 'index' => 0], 'status' => ['type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 70, 'notnull' => 1, 'visible' => 1, 'index' => 1, 'default' =>1, 'arrayofkeyval' => ['specialCase' => 'InProgressAndLocked', 1 => 'InProgress', 2 => 'Locked', 3 => 'Archived'], 'css' => 'minwidth200'], - 'type' => ['type' => 'varchar(128)', 'label' => 'Type', 'enabled' => 1, 'position' => 80, 'notnull' => 0, 'visible' => 0,], + 'type' => ['type' => 'select', 'label' => 'Type', 'enabled' => 1, 'position' => 65, 'notnull' => 1, 'visible' => 1, 'arrayofkeyval' => ['control' => 'Control', 'survey' => 'Survey']], 'label' => ['type' => 'varchar(255)', 'label' => 'Label', 'enabled' => 1, 'position' => 11, 'notnull' => 1, 'visible' => 1, 'searchall' => 1, 'css' => 'minwidth200'], 'description' => ['type' => 'html', 'label' => 'Description', 'enabled' => 1, 'position' => 15, 'notnull' => 0, 'visible' => 1, 'searchall' => 1, 'css' => 'minwidth200'], 'element_linked' => ['type' => 'text', 'label' => 'ElementLinked', 'enabled' => 1, 'position' => 90, 'notnull' => 0, 'visible' => 0], @@ -168,9 +168,9 @@ class Sheet extends SaturneObject public $status; /** - * @var string|null Type. + * @var string Type */ - public ?string $type = ''; + public string $type = 'control'; /** * @var string Label. @@ -304,7 +304,7 @@ public function createFromClone(User $user, $fromid) } // Create clone - $object->fetchQuestionsLinked($object->id, 'digiquali_' . $object->element); + $object->fetchObjectLinked($object->id, 'digiquali_' . $object->element); $object->context['createfromclone'] = 'createfromclone'; $object->ref = $object->getNextNumRef(); $object->status = 1; @@ -398,12 +398,6 @@ public function selectSheetList($selected = '', $htmlname = 'fk_sheet', $filter if ($selected === '') $selected = array(); elseif ( ! is_array($selected)) $selected = array($selected); - // Clean $filter that may contains sql conditions so sql code - if (function_exists('testSqlAndScriptInject')) { - if (testSqlAndScriptInject($filter, 3) > 0) { - $filter = ''; - } - } // On recherche les societes $sql = "SELECT *"; $sql .= " FROM " . MAIN_DB_PREFIX . "digiquali_sheet as s"; @@ -473,99 +467,6 @@ public function selectSheetList($selected = '', $htmlname = 'fk_sheet', $filter return $out; } - /** - * Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into - * this->linkedObjectsIds array + - * this->linkedObjects array if $loadalsoobjects = 1 - * Possible usage for parameters: - * - all parameters empty -> we look all link to current object (current object can be source or target) - * - source id+type -> will get target list linked to source - * - target id+type -> will get source list linked to target - * - source id+type + target type -> will get target list of the type - * - target id+type + target source -> will get source list of the type - * - * @param int $sourceid Object source id (if not defined, id of object) - * @param string $sourcetype Object source type (if not defined, element name of object) - * @param int $targetid Object target id (if not defined, id of object) - * @param string $targettype Object target type (if not defined, elemennt name of object) - * @param string $clause 'OR' or 'AND' clause used when both source id and target id are provided - * @param int $alsosametype 0=Return only links to object that differs from source type. 1=Include also link to objects of same type. - * @param string $orderby SQL 'ORDER BY' clause - * @param int $loadalsoobjects Load also array this->linkedObjects (Use 0 to increase performances) - * @return int <0 if KO, >0 if OK - * @see add_object_linked(), updateObjectLinked(), deleteObjectLinked() - */ - public function fetchQuestionsLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1) - { - $this->linkedObjectsIds = array(); - $this->linkedObjects = array(); - - $justsource = false; - $withsourcetype = false; - - $sourceid = (!empty($sourceid) ? $sourceid : $this->id); - $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element); - - // Links between objects are stored in table element_element - $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype, position'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'element_element'; - $sql .= " WHERE "; - $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')"; - $sql .= " AND targettype = 'digiquali_question'"; - - $sql .= ' ORDER BY '.$orderby; - - dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG); - $resql = $this->db->query($sql); - - if ($resql) { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) { - $maxPosition = $this->getMaxPosition(); - $obj = $this->db->fetch_object($resql); - $this->linkedObjectsIds[$obj->targettype][$obj->position ?: ($maxPosition+1)] = $obj->fk_target; - $i++; - } - if (!empty($this->linkedObjectsIds)) { - $tmparray = $this->linkedObjectsIds; - foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...) - // Here $module, $classfile and $classname are set - if ($loadalsoobjects) { - foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element - $object = new Question($this->db); - $ret = $object->fetch($objectid); - if ($ret >= 0) { - $this->linkedObjects[$objecttype][$i] = $object; - } - } - } - } - } - return 1; - } else { - dol_print_error($this->db); - return -1; - } - } - - /** - * Returns max position of questions in sheet - * - */ - public function getMaxPosition() { - $sql = "SELECT fk_source, sourcetype, targettype, position FROM ". MAIN_DB_PREFIX ."element_element WHERE fk_source = " . $this->id . " AND sourcetype = 'digiquali_sheet' ORDER BY position DESC LIMIT 1"; - $resql = $this->db->query($sql); - - if ($resql) { - $obj = $this->db->fetch_object($resql); - $positionField = 'position'; - return $obj->$positionField; - } else { - return 0; - } - } - /** * Update questions position in sheet * diff --git a/class/survey.class.php b/class/survey.class.php new file mode 100644 index 00000000..4d5281d5 --- /dev/null +++ b/class/survey.class.php @@ -0,0 +1,792 @@ + + * + * 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/survey.class.php + * \ingroup digiquali + * \brief This file is a CRUD class file for Survey (Create/Read/Update/Delete) + */ + +// Load Dolibarr libraries +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php'; + +// Load Saturne libraries +require_once __DIR__ . '/../../saturne/class/saturneobject.class.php'; + +/** + * Class for Survey + */ +class Survey extends SaturneObject +{ + /** + * @var string Module name + */ + public $module = 'digiquali'; + + /** + * @var string Element type of object + */ + public $element = 'survey'; + + /** + * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management + */ + public $table_element = 'digiquali_survey'; + + /** + * @var int Does this object support multicompany module ? + * 0 = No test on entity, 1 = Test with field entity, 'field@table' = Test with link by field@table + */ + public $ismultientitymanaged = 1; + + /** + * @var int Does object support extrafields ? 0 = No, 1 = Yes + */ + public int $isextrafieldmanaged = 1; + + /** + * @var string Name of icon for survey. Must be a 'fa-xxx' fontawesome code (or 'fa-xxx_fa_color_size') or 'survey@digiquali' if picto is file 'img/object_survey.png' + */ + public string $picto = 'fontawesome_fa-marker_fas_#d35968'; + + public const STATUS_DELETED = -1; + public const STATUS_DRAFT = 0; + public const STATUS_VALIDATED = 1; + public const STATUS_LOCKED = 2; + public const STATUS_ARCHIVED = 3; + + /** + * '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 can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') 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 '$conf->global->MY_SETUP_PARAM' or '!empty($conf->multicurrency->enabled)' ...) + * '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) + * 'default' is a default value for creation (can still be overwroted 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' 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 you 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 + */ + + /** + * @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 = [ + 'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'comment' => 'Id'], + 'ref' => ['type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'visible' => 4, 'noteditable' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'validate' => 1, 'comment' => 'Reference of object'], + 'ref_ext' => ['type' => 'varchar(128)', 'label' => 'RefExt', 'enabled' => 1, 'position' => 20, 'notnull' => 0, 'visible' => 0], + 'entity' => ['type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 0, 'index' => 1], + 'date_creation' => ['type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 40, 'notnull' => 1, 'visible' => 2, 'positioncard' => 10], + 'tms' => ['type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => 0], + 'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 0, 'index' => 0], + 'status' => ['type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 70, 'notnull' => 1, 'visible' => 5, 'index' => 1, 'default' => 0, 'arrayofkeyval' => [0 => 'StatusDraft', 1 => 'Validated', 2 => 'Locked', 3 => 'Archived']], + 'note_public' => ['type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'position' => 80, 'notnull' => 0, 'visible' => 0], + 'note_private' => ['type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'position' => 90, 'notnull' => 0, 'visible' => 0], + 'photo' => ['type' => 'text', 'label' => 'Photo', 'enabled' => 1, 'position' => 100, 'notnull' => 0, 'visible' => 0], + 'success_rate' => ['type' => 'real', 'label' => 'SuccessScore', 'enabled' => 1, 'position' => 35, 'notnull' => 0, 'visible' => 2, 'help' => 'PercentageValue'], + 'track_id' => ['type' => 'text', 'label' => 'TrackID', 'enabled' => 1, 'position' => 110, 'notnull' => 0, 'visible' => 2], + 'fk_user_creat' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'picto' => 'user', 'enabled' => 1, 'position' => 120, 'notnull' => 1, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_user_modif' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'position' => 130, 'notnull' => 0, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_sheet' => ['type' => 'integer:Sheet:digiquali/class/sheet.class.php', 'label' => 'Sheet', 'picto' => 'fontawesome_fa-list_fas_#d35968', 'enabled' => 1, 'position' => 11, 'notnull' => 1, 'visible' => 5, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_sheet.rowid'], + 'projectid' => ['type' => 'integer:Project:projet/class/project.class.php:1', 'label' => 'Project', 'picto' => 'project', 'enabled' => 1, 'position' => 13, 'notnull' => 0, 'visible' => 1, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'projet.rowid', 'positioncard' => 2] + ]; + + /** + * @var int ID + */ + public int $rowid; + + /** + * @var string Ref + */ + public $ref; + + /** + * @var string Ref ext + */ + public $ref_ext; + + /** + * @var int Entity + */ + public $entity; + + /** + * @var int|string Creation date + */ + public $date_creation; + + /** + * @var int|string Timestamp + */ + public $tms; + + /** + * @var string Import key + */ + public $import_key; + + /** + * @var int Status + */ + public $status; + + /** + * @var string Public note + */ + public $note_public; + + /** + * @var string Private note + */ + public $note_private; + + /** + * @var string|null Photo path + */ + public ?string $photo = ''; + + /** + * @var float|string|null Success rate + */ + public $success_rate; + + /** + * @var string|null TrackID + */ + public ?string $track_id; + + /** + * @var int User ID + */ + public int $fk_user_creat; + + /** + * @var int|null User ID + */ + public ?int $fk_user_modif; + + /** + * @var int Sheet ID + */ + public int $fk_sheet; + + /** + * @var int|string|null Project ID + */ + public $projectid; + + /** + * @var string Name of subtable line + */ + public $table_element_line = 'digiquali_surveydet'; + + /** + * @var SurveyLine[] Array of subtable lines + */ + public $lines = []; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->element); + } + + /** + * Create object into database + * + * @param User $user User that creates + * @param bool $notrigger false = launch triggers after, true = disable triggers + * @return int 0 < if KO, ID of created object if OK + */ + public function create(User $user, bool $notrigger = false): int + { + $this->track_id = generate_random_id(); + $result = parent::create($user, $notrigger); + + if ($result > 0) { + global $conf; + + require_once TCPDF_PATH . 'tcpdf_barcodes_2d.php'; + + $url = dol_buildpath('custom/digiquali/public/survey/public_survey.php?track_id=' . $this->track_id . '&entity=' . $conf->entity, 3); + + $barcode = new TCPDF2DBarcode($url, 'QRCODE,L'); + + dol_mkdir($conf->digiquali->multidir_output[$conf->entity] . '/survey/' . $this->ref . '/qrcode/'); + $file = $conf->digiquali->multidir_output[$conf->entity] . '/survey/' . $this->ref . '/qrcode/' . 'barcode_' . $this->track_id . '.png'; + + $imageData = $barcode->getBarcodePngData(); + $imageData = imagecreatefromstring($imageData); + imagepng($imageData, $file); + } + + return $result; + } + + /** + * Set draft status + * + * @param User $user Object user that modify + * @param int $notrigger 1 = Does not execute triggers, 0 = Execute triggers + * @return int 0 < if KO, > 0 if OK + * @throws Exception + */ + public function setDraft(User $user, int $notrigger = 0): int + { + $signatory = new SaturneSignature($this->db, $this->module, $this->element); + $signatory->deleteSignatoriesSignatures($this->id, $this->element); + return parent::setDraft($user, $notrigger); + } + + /** + * Return if a survey can be deleted + * + * @return int 0 < if KO, > 0 if OK + */ + public function isErasable(): int + { + return $this->isLinkedToOtherObjects(); + } + + /** + * Return if a survey is linked to another object + * + * @return int 0 < if KO, > 0 if OK + */ + public function isLinkedToOtherObjects(): int + { + // Links between objects are stored in table element_element + $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype'; + $sql .= ' FROM '.MAIN_DB_PREFIX . 'element_element'; + $sql .= ' WHERE fk_target = ' . $this->id; + $sql .= " AND targettype = '" . $this->table_element . "'"; + + $resql = $this->db->query($sql); + if ($resql) { + $nbObjectsLinked = 0; + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) { + $nbObjectsLinked++; + $i++; + } + if ($nbObjectsLinked > 0) { + return -1; + } else { + return 1; + } + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Clone an object into another one + * + * @param User $user User that creates + * @param int $fromID ID of object to clone + * @param array $options Options array + * @return int New object created, <0 if KO + * @throws Exception + */ + public function createFromClone(User $user, int $fromID, array $options): int + { + global $conf; + + dol_syslog(__METHOD__, LOG_DEBUG); + + $error = 0; + + $object = new self($this->db); + $this->db->begin(); + + // Load source object + $result = $object->fetchCommon($fromID); + if ($result > 0 && !empty($object->table_element_line)) { + $object->fetchLines(); + } + + $objectRef = $object->ref; + + // Reset some properties + unset($object->fk_user_creat); + unset($object->import_key); + + // Clear fields + if (property_exists($object, 'ref')) { + $object->ref = ''; + } + if (property_exists($object, 'date_creation')) { + $object->date_creation = dol_now(); + } + if (property_exists($object, 'status')) { + $object->status = 0; + } + if (empty($options['photos'])) { + $object->photo = ''; + } + + $object->context = 'createfromclone'; + + $object->fetchObjectLinked('','', $object->id, 'digiquali_' . $object->element, 'OR', 1, 'sourcetype', 0); + + $surveyID = $object->create($user); + if ($surveyID > 0) { + $objectFromClone = new self($this->db); + $objectFromClone->fetch($surveyID); + + // Categories + $cat = new Categorie($this->db); + $categories = $cat->containing($fromID, 'survey'); + if (is_array($categories) && !empty($categories)) { + foreach($categories as $cat) { + $categoryIds[] = $cat->id; + } + $object->setCategories($categoryIds); + } + + // Add objects linked + $linkableElements = get_sheet_linkable_objects(); + if (!empty($linkableElements)) { + foreach($linkableElements as $linkableElement) { + if ($linkableElement['conf'] > 0 && (!empty($object->linkedObjectsIds[$linkableElement['link_name']]))) { + foreach($object->linkedObjectsIds[$linkableElement['link_name']] as $linkedElementId) { + $objectFromClone->add_object_linked($linkableElement['link_name'], $linkedElementId); + } + } + } + } + + // Add Attendants + $signatory = new SaturneSignature($this->db); + if (!empty($options['attendants'])) { + // Load signatory from source object + $signatories = $signatory->fetchSignatory('', $fromID, $this->element); + if (is_array($signatories) && !empty($signatories)) { + foreach ($signatories as $arrayRole) { + foreach ($arrayRole as $signatoryRole) { + $signatory->createFromClone($user, $signatoryRole->id, $surveyID); + } + } + } + } + + // Add Photos + if (!empty($options['photos'])) { + $dir = $conf->digiquali->multidir_output[$conf->entity] . '/survey'; + $path = $dir . '/' . $objectRef . '/photos'; + dol_mkdir($dir . '/' . $objectFromClone->ref . '/photos'); + dolCopyDir($path,$dir . '/' . $objectFromClone->ref . '/photos', 0, 1); + } + } else { + $error++; + $this->error = $object->error; + $this->errors = $object->errors; + } + + // End + if (!$error) { + $this->db->commit(); + return $surveyID; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * 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(int $status, int $mode = 0): string + { + if (empty($this->labelStatus) || empty($this->labelStatusShort)) { + global $langs; + + $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusDraft'); + $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated'); + $this->labelStatus[self::STATUS_LOCKED] = $langs->transnoentitiesnoconv('Locked'); + $this->labelStatus[self::STATUS_ARCHIVED] = $langs->transnoentitiesnoconv('Archived'); + $this->labelStatus[self::STATUS_DELETED] = $langs->transnoentitiesnoconv('Deleted'); + + $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('StatusDraft'); + $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated'); + $this->labelStatusShort[self::STATUS_LOCKED] = $langs->transnoentitiesnoconv('Locked'); + $this->labelStatusShort[self::STATUS_ARCHIVED] = $langs->transnoentitiesnoconv('Archived'); + $this->labelStatusShort[self::STATUS_DELETED] = $langs->transnoentitiesnoconv('Deleted'); + } + + $statusType = 'status' . $status; + if ($status == self::STATUS_VALIDATED) { + $statusType = 'status4'; + } + if ($status == self::STATUS_LOCKED) { + $statusType = 'status6'; + } + if ($status == self::STATUS_ARCHIVED) { + $statusType = 'status8'; + } + if ($status == self::STATUS_DELETED) { + $statusType = 'status9'; + } + + return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); + } + + /** + * Initialise object with example values + * ID must be 0 if object instance is a specimen + * + * @return void + */ + public function initAsSpecimen() + { + $this->initAsSpecimenCommon(); + } + + /** + * Load dashboard info + * + * @return array + * @throws Exception + */ + public function load_dashboard(): array + { + $getNbSurveysByMonth = $this->getNbSurveysByMonth(); + + $array['graphs'] = [$getNbSurveysByMonth]; + + return $array; + } + + /** + * Get surveys by month + * + * @return array Graph datas (label/color/type/title/data etc..) + * @throws Exception + */ + public function getNbSurveysByMonth(): array + { + global $conf, $langs; + + $startMonth = $conf->global->SOCIETE_FISCAL_MONTH_START; + $currentYear = date('Y', dol_now()); + $years = [0 => $currentYear - 2, 1 => $currentYear - 1, 2 => $currentYear]; + + // Graph Title parameters + $array['title'] = $langs->transnoentities('SurveysByFiscalYear'); + $array['picto'] = $this->picto; + + // Graph parameters + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'bars'; + $array['showlegend'] = 1; + $array['dataset'] = 3; + + $array['labels'] = [ + 0 => [ + 'label' => $years[0], + 'color' => '#9567AA' + ], + 1 => [ + 'label' => $years[1], + 'color' => '#4F9EBE' + ], + 2 => [ + 'label' => $years[2], + 'color' => '#FAC461' + ] + ]; + + $arrayNbSurveys = []; + for ($i = 1; $i < 13; $i++) { + foreach ($years as $key => $year) { + $surveys = $this->fetchAll('', '', 0, 0, ['customsql' => 'MONTH (t.date_creation) = ' . $i . ' AND YEAR (t.date_creation) = ' . $year . ' AND t.status >= 0']); + if (is_array($surveys) && !empty($surveys)) { + $arrayNbSurveys[$key][$i] = count($surveys); + } + } + + $month = $langs->transnoentitiesnoconv('MonthShort' . sprintf('%02d', $i)); + $arrayKey = $i - $startMonth; + $arrayKey = $arrayKey >= 0 ? $arrayKey : $arrayKey + 12; + $array['data'][$arrayKey] = [$month, $arrayNbSurveys[0][$i], $arrayNbSurveys[1][$i], $arrayNbSurveys[2][$i]]; + } + ksort($array['data']); + + return $array; + } + + /** + * Write information of trigger description + * + * @param SaturneObject $object Object calling the trigger + * @return string Description to display in actioncomm->note_private + */ + public function getTriggerDescription(SaturneObject $object): string + { + global $langs; + + // Load DigiQuali libraries + require_once __DIR__ . '/../class/sheet.class.php'; + + $sheet = new Sheet($this->db); + $sheet->fetch($object->fk_sheet); + + $ret = parent::getTriggerDescription($object); + $ret .= $langs->transnoentities('Sheet') . ' : ' . $sheet->ref . ' - ' . $sheet->label . '
'; + if ($object->projectid > 0) { + require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; + $project = new Project($this->db); + $project->fetch($object->projectid); + $ret .= $langs->transnoentities('Project') . ' : ' . $project->ref . ' ' . $project->title . '
'; + } + $ret .= (!empty($object->photo) ? $langs->transnoentities('Photo') . ' : ' . $object->photo . '
' : ''); + + return $ret; + } +} + +/** + * Class for SurveyLine + */ +class SurveyLine extends SaturneObject +{ + /** + * @var string Module name + */ + public $module = 'digiquali'; + + /** + * @var string Element type of object + */ + public $element = 'surveydet'; + + /** + * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management + */ + public $table_element = 'digiquali_surveydet'; + + /** + * @var int Does this object support multicompany module ? + * 0 = No test on entity, 1 = Test with field entity, 'field@table' = Test with link by field@table + */ + public $ismultientitymanaged = 1; + + /** + * @var int Does object support extrafields ? 0 = No, 1 = Yes + */ + public int $isextrafieldmanaged = 1; + + /** + * '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 can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') 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 '$conf->global->MY_SETUP_PARAM' or '!empty($conf->multicurrency->enabled)' ...) + * '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) + * 'default' is a default value for creation (can still be overwroted 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' 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 you 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 + */ + + /** + * @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 = [ + 'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'comment' => 'Id'], + 'ref' => ['type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'visible' => 1, 'noteditable' => 1, 'default' => '(PROV)', 'index' => 1, 'searchall' => 1, 'showoncombobox' => 1, 'validate' => 1, 'comment' => 'Reference of object'], + 'ref_ext' => ['type' => 'varchar(128)', 'label' => 'RefExt', 'enabled' => 1, 'position' => 20, 'notnull' => 0, 'visible' => 0], + 'entity' => ['type' => 'integer', 'label' => 'Entity', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 0, 'index' => 1], + 'date_creation' => ['type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 40, 'notnull' => 1, 'visible' => 0], + 'tms' => ['type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 50, 'notnull' => 0, 'visible' => 0], + 'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 60, 'notnull' => 0, 'visible' => 0, 'index' => 0], + 'status' => ['type' => 'smallint', 'label' => 'Status', 'enabled' => 1, 'position' => 70, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'default' => 1], + 'type' => ['type' => 'varchar(128)', 'label' => 'Type', 'enabled' => 0, 'position' => 80, 'notnull' => 0, 'visible' => 0], + 'answer' => ['type' => 'text', 'label' => 'Answer', 'enabled' => 1, 'position' => 90, 'notnull' => 0, 'visible' => 0], + 'answer_photo' => ['type' => 'text', 'label' => 'AnswerPhoto', 'enabled' => 0, 'position' => 100, 'notnull' => 0, 'visible' => 0], + 'comment' => ['type' => 'text', 'label' => 'Comment', 'enabled' => 1, 'position' => 110, 'notnull' => 0, 'visible' => 0], + 'fk_user_creat' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserAuthor', 'picto' => 'user', 'enabled' => 1, 'position' => 120, 'notnull' => 1, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_user_modif' => ['type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'picto' => 'user', 'enabled' => 1, 'position' => 130, 'notnull' => 0, 'visible' => 0, 'foreignkey' => 'user.rowid'], + 'fk_survey' => ['type' => 'integer:Survey:digiquali/class/survey.class.php', 'label' => 'Survey', 'picto' => 'fontawesome_fa-marker_fas_#d35968', 'enabled' => 1, 'position' => 140, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_survey.rowid'], + 'fk_question' => ['type' => 'integer:Question:digiquali/class/question.class.php', 'label' => 'Question', 'picto' => 'fontawesome_fa-question_fas_#d35968', 'enabled' => 1, 'position' => 150, 'notnull' => 1, 'visible' => 0, 'index' => 1, 'css' => 'maxwidth500 widthcentpercentminusxx', 'foreignkey' => 'digiquali_question.rowid'], + ]; + + /** + * @var int ID + */ + public int $rowid; + + /** + * @var string Ref + */ + public $ref; + + /** + * @var string Ref ext + */ + public $ref_ext; + + /** + * @var int Entity + */ + public $entity; + + /** + * @var int|string Creation date + */ + public $date_creation; + + /** + * @var int|string Timestamp + */ + public $tms; + + /** + * @var string Import key + */ + public $import_key; + + /** + * @var int Status + */ + public $status; + + /** + * @var string|null Type + */ + public ?string $type; + + /** + * @var string|null Answer + */ + public ?string $answer = ''; + + /** + * @var string|null Answer photo + */ + public ?string $answer_photo; + + /** + * @var string|null Comment + */ + public ?string $comment = ''; + + /** + * @var int User ID + */ + public int $fk_user_creat; + + /** + * @var int|null User ID + */ + public ?int $fk_user_modif; + + /** + * @var int Survey ID + */ + public int $fk_survey; + + /** + * @var ?int|null Question ID + */ + public int $fk_question; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->element); + } + + /** + * Load survey line from database form parent with question + * + * @param int $surveyID Survey id + * @param int $questionID Question id + * @return array|int Int <0 if KO, array of pages if OK + * @throws Exception + */ + public function fetchFromParentWithQuestion(int $surveyID, int $questionID) + { + return $this->fetchAll('', '', 1, 0, ['customsql' => 't.fk_survey = ' . $surveyID . ' AND t.fk_question = ' . $questionID . ' AND t.status > 0']); + } +} diff --git a/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php index 4f8cedde..30fecb8f 100644 --- a/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php @@ -108,6 +108,12 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo // Replace tags of lines. try { + $moreParam['segmentName'] = 'controller'; + $moreParam['excludeAttendantsRole'] = ['attendant']; + $this->setAttendantsSegment($odfHandler, $outputLangs, $moreParam); + + $moreParam['segmentName'] = 'attendant'; + $moreParam['excludeAttendantsRole'] = ['controller']; $this->setAttendantsSegment($odfHandler, $outputLangs, $moreParam); // Get questions. @@ -127,7 +133,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo if (!empty($object)) { $sheet = new Sheet($this->db); - $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet','', '', 'OR', 1, 'sourcetype', 0); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet', null, '', 'OR', 1, 'position', 0); $questionIds = $sheet->linkedObjectsIds; if (is_array($questionIds['digiquali_question']) && !empty($questionIds['digiquali_question'])) { @@ -195,6 +201,10 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo $photoArray[$image] = $questionAnswerLine->ref; } } + } else { + $tmpArray['ref_answer'] = ''; + $tmpArray['comment'] = ''; + $tmpArray['answer'] = ' '; } $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); } @@ -397,8 +407,7 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $tmpArray['mycompany_mail'] = (!empty($conf->global->MAIN_INFO_SOCIETE_MAIL) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_MAIL : ''); $tmpArray['mycompany_phone'] = (!empty($conf->global->MAIN_INFO_SOCIETE_PHONE) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_PHONE : ''); - $moreParam['tmparray'] = $tmpArray; - $moreParam['multipleAttendantsSegment'] = ['controller', 'attendant']; + $moreParam['tmparray'] = $tmpArray; return parent::write_file($objectDocument, $outputLangs, $srcTemplatePath, $hideDetails, $hideDesc, $hideRef, $moreParam); } diff --git a/core/modules/digiquali/digiqualidocuments/controldocument/modules_controldocument.php b/core/modules/digiquali/digiqualidocuments/controldocument/modules_controldocument.php deleted file mode 100644 index 9d468927..00000000 --- a/core/modules/digiquali/digiqualidocuments/controldocument/modules_controldocument.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * 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 . - * or see https://www.gnu.org/ - */ - -/** - * \file core/modules/digiquali/digiqualidocuments/controldocument/modules_controldocument.php - * \ingroup digiquali - * \brief File that contains parent class for controldocuments document models. - */ - -// Load Saturne libraries -require_once __DIR__ . '/../../../../../../saturne/core/modules/saturne/modules_saturne.php'; - -/** - * Parent class for documents models. - */ -abstract class ModeleODTControlDocument extends SaturneDocumentModel -{ - /** - * Return list of active generation modules. - * - * @param DoliDB $db Database handler. - * @param string $type Document type. - * @param int $maxfilenamelength Max length of value to show. - * - * @return array List of templates. - * @throws Exception - */ - public static function liste_modeles(DoliDB $db, string $type, int $maxfilenamelength = 0): array - { - return parent::liste_modeles($db, 'controldocument', $maxfilenamelength); - } -} diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php new file mode 100644 index 00000000..af0f7ab0 --- /dev/null +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -0,0 +1,338 @@ + + * + * 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 . + * or see https://www.gnu.org/ + */ + +/** + * \file core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php + * \ingroup digiquali + * \brief File of class to build ODT survey document + */ + +// Load Dolibarr libraries +require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; +require_once DOL_DOCUMENT_ROOT . '/comm/action/class/actioncomm.class.php'; + +// Load DigiQuali libraries +require_once __DIR__ . '/mod_surveydocument_standard.php'; +require_once __DIR__ . '/../../../../../lib/digiquali_sheet.lib.php'; + +require_once __DIR__ . '/../../../../../class/question.class.php'; +require_once __DIR__ . '/../../../../../class/sheet.class.php'; +require_once __DIR__ . '/../../../../../class/answer.class.php'; + +/** + * Class to build documents using ODF templates generator + */ +class doc_surveydocument_odt extends SaturneDocumentModel +{ + /** + * @var array Minimum version of PHP required by module + * e.g.: PHP ≥ 5.5 = array(5, 5) + */ + public $phpmin = [7, 4]; + + /** + * @var string Dolibarr version of the loaded document + */ + public string $version = 'dolibarr'; + + /** + * @var string Module + */ + public string $module = 'digiquali'; + + /** + * @var string Document type + */ + public string $document_type = 'surveydocument'; + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->document_type); + } + + /** + * Return description of a module + * + * @param Translate $langs Lang object to use for output + * @return string Description + */ + public function info(Translate $langs): string + { + return parent::info($langs); + } + + /** + * Fill all odt tags for segments lines + * + * @param Odf $odfHandler Object builder odf library + * @param Translate $outputLangs Lang object to use for output + * @param array $moreParam More param (Object/user/etc) + * + * @return int 1 if OK, <=0 if KO + * @throws Exception + */ + public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $moreParam): int + { + global $conf; + + $object = $moreParam['object']; + + // Replace tags of lines + try { + $this->setAttendantsSegment($odfHandler, $outputLangs, $moreParam); + + // Get questions + $photoArray = []; + $foundTagForLines = 1; + try { + $listLines = $odfHandler->setSegment('questions'); + } catch (OdfException $e) { + // We may arrive here if tags for lines not present into template + $foundTagForLines = 0; + $listLines = ''; + dol_syslog($e->getMessage()); + } + + if ($foundTagForLines) { + if (!empty($object)) { + $sheet = new Sheet($this->db); + $answer = new Answer($this->db); + + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position'); + if (!empty($sheet->linkedObjects['digiquali_question'])) { + foreach ($sheet->linkedObjects['digiquali_question'] as $question) { + foreach ($object->lines as $line) { + if ($line->fk_question === $question->id) { + $tmpArray['ref'] = $question->ref; + $tmpArray['label'] = $question->label; + $tmpArray['description'] = strip_tags($question->description); + $tmpArray['ref_answer'] = $line->ref; + $tmpArray['comment'] = dol_htmlentitiesbr_decode(strip_tags($line->comment, '
')); + + $answersArray = []; + $answers = $answer->fetchAll('ASC', 'position', 0, 0, ['fk_question' => $line->fk_question]); + if (is_array($answers) && !empty($answers)) { + foreach ($answers as $answer) { + $answersArray[$answer->position] = $answer->value; + } + } + + switch ($question->type) { + case 'OkKo' : + case 'OkKoToFixNonApplicable' : + case 'UniqueChoice' : + $tmpArray['answer'] = $answersArray[$line->answer]; + break; + case 'Text' : + case 'Range' : + $tmpArray['answer'] = $line->answer; + break; + case 'Percentage' : + $tmpArray['answer'] = $line->answer . ' %'; + break; + case 'MultipleChoices' : + $tmpArray['answer'] = ''; + $answers = explode(',', $line->answer); + foreach ($answers as $answerValue) { + $tmpArray['answer'] .= $answersArray[$answerValue] . ', '; + } + $tmpArray['answer'] = rtrim($tmpArray['answer'], ', '); + break; + default: + $tmpArray['answer'] = ''; + } + + $path = $conf->digiquali->multidir_output[$conf->entity] . '/survey/' . $object->ref . '/answer_photo/' . $question->ref; + $fileList = dol_dir_list($path, 'files'); + // Fill an array with photo path and ref of the answer for next loop + if (is_array($fileList) && !empty($fileList)) { + foreach ($fileList as $singleFile) { + $fileSmall = saturne_get_thumb_name($singleFile['name']); + $image = $path . '/thumbs/' . $fileSmall; + $photoArray[$image] = $line->ref; + } + } + $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); + } + } + } + $odfHandler->mergeSegment($listLines); + } + } + } + + // Get answer photos + $foundTagForLines = 1; + try { + $listLines = $odfHandler->setSegment('photos'); + } catch (OdfException $e) { + // We may arrive here if tags for lines not present into template + $foundTagForLines = 0; + $listLines = ''; + dol_syslog($e->getMessage()); + } + + // Loop on previous photos array + if ($foundTagForLines) { + if (is_array($photoArray) && !empty($photoArray)) { + foreach ($photoArray as $photoPath => $answerRef) { + $fileInfo = preg_split('/thumbs\//', $photoPath); + $name = end($fileInfo); + + $tmpArray['answer_ref'] = ($previousRef == $answerRef) ? '' : $outputLangs->trans('Ref') . ' : ' . $answerRef; + $tmpArray['media_name'] = $name; + $tmpArray['photo'] = $photoPath; + + $previousRef = $answerRef; + + $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); + } + } else { + $tmpArray['answer_ref'] = ' '; + $tmpArray['media_name'] = ' '; + $tmpArray['photo'] = ' '; + $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); + } + $odfHandler->mergeSegment($listLines); + } + } catch (OdfException $e) { + $this->error = $e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + return 0; + } + + /** + * Function to build a document on disk + * + * @param SaturneDocuments $objectDocument Object source to build document + * @param Translate $outputLangs Lang object to use for output + * @param string $srcTemplatePath Full path of source filename for generator using a template file + * @param int $hideDetails Do not show line details + * @param int $hideDesc Do not show desc + * @param int $hideRef Do not show ref + * @param array $moreParam More param (Object/user/etc) + * @return int 1 if OK, <=0 if KO + * @throws Exception + */ + public function write_file(SaturneDocuments $objectDocument, Translate $outputLangs, string $srcTemplatePath, int $hideDetails = 0, int $hideDesc = 0, int $hideRef = 0, array $moreParam): int + { + global $conf; + + $object = $moreParam['object']; + + if (!empty($object->photo)) { + $path = $conf->digiquali->multidir_output[$conf->entity] . '/survey/' . $object->ref . '/photos'; + $fileSmall = saturne_get_thumb_name($object->photo); + $image = $path . '/thumbs/' . $fileSmall; + $tmpArray['photoDefault'] = $image; + } else { + $noPhoto = '/public/theme/common/nophoto.png'; + $tmpArray['photoDefault'] = DOL_DOCUMENT_ROOT . $noPhoto; + } + + $outputLangs->loadLangs(['products', 'bills', 'orders', 'contracts', 'projects', 'companies']); + + $sheet = new Sheet($this->db); + $project = new Project($this->db); + $actionComm = new ActionComm($this->db); + + $sheet->fetch($object->fk_sheet); + $project->fetch($object->projectid); + + $object->fetchObjectLinked('', '', $object->id, 'digiquali_survey', 'OR', 1, 'sourcetype', 0); + + $linkableElements = get_sheet_linkable_objects(); + if (!empty($linkableElements)) { + $nameField = []; + $objectInfo = []; + foreach ($linkableElements as $linkableElement) { + $nameField[$linkableElement['link_name']] = $linkableElement['name_field']; + $objectInfo[$linkableElement['link_name']] = ['title' => $linkableElement['langs'], 'className' => $linkableElement['className']]; + } + foreach ($object->linkedObjectsIds as $linkedObjectType => $linkedObjectsIds) { + $className = $objectInfo[$linkedObjectType]['className']; + $linkedObject = new $className($this->db); + $result = $linkedObject->fetch(array_shift($object->linkedObjectsIds[$linkedObjectType])); + if ($result > 0) { + $objectName = ''; + $objectNameField = $nameField[$linkedObjectType]; + if (strstr($objectNameField, ',')) { + $nameFields = explode(', ', $objectNameField); + if (is_array($nameFields) && !empty($nameFields)) { + foreach ($nameFields as $subnameField) { + $objectName .= $linkedObject->$subnameField . ' '; + } + } + } else { + $objectName = $linkedObject->$objectNameField; + } + $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; + $tmpArray['object_label_ref'] .= $objectName . chr(0x0A); + } + } + } + + $tmpArray['object_ref'] = $object->ref; + $tmpArray['object_label_ref'] = rtrim($tmpArray['object_label_ref'], chr(0x0A)); + + $actionComms = $actionComm->getActions(0, $object->id,$object->element . '@digiquali', " AND code = 'AC_SURVEY_SAVEANSWER'", 'id','DESC', 1); + if (is_array($actionComms) && !empty($actionComms)) { + $tmpArray['actioncomm_creation_date'] = dol_print_date($actionComms[0]->datec, 'dayhour', 'tzuser'); + } + + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + + $averagePercentageQuestions = 0; + $percentQuestionCounter = 0; + foreach ($sheet->linkedObjects['digiquali_question'] as $questionLinked) { + if ($questionLinked->type !== 'Percentage') { + continue; // Skip non-percentage questions + } + + $percentQuestionCounter++; + foreach ($object->lines as $line) { + if ($line->fk_question === $questionLinked->id) { + $averagePercentageQuestions += $line->answer; + } + } + } + + $averagePercentageQuestions = ($percentQuestionCounter > 0) ? ($averagePercentageQuestions / $percentQuestionCounter) : 0; + + if ($percentQuestionCounter > 0) { + $tmpArray['average'] = price2num($averagePercentageQuestions) . ' %'; + } else { + $tmpArray['average'] = ''; + } + $tmpArray['project_label'] = $project->ref . ' - ' . $project->title; + $tmpArray['sheet_ref'] = $sheet->ref; + $tmpArray['sheet_label'] = $sheet->label; + $tmpArray['object_note_public'] = $object->note_public; + + $moreParam['tmparray'] = $tmpArray; + + return parent::write_file($objectDocument, $outputLangs, $srcTemplatePath, $hideDetails, $hideDesc, $hideRef, $moreParam); + } +} diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/index.php b/core/modules/digiquali/digiqualidocuments/surveydocument/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/index.php @@ -0,0 +1,2 @@ + + * + * 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 . + * or see https://www.gnu.org/ + */ + +/** + * \file core/modules/digiquali/digiqualidocuments/surveydocument/mod_surveydocument_standard.php + * \ingroup digiquali + * \brief File of class to manage surveydocument numbering rules standard + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../../../../../saturne/core/modules/saturne/modules_saturne.php'; + +/** + * Class to manage surveydocument numbering rules standard + */ +class mod_surveydocument_standard extends ModeleNumRefSaturne +{ + /** + * @var string Numbering module ref prefix + */ + public string $prefix = 'SYD'; + + /** + * @var string Name + */ + public string $name = 'Rhéa'; +} diff --git a/core/modules/digiquali/survey/index.php b/core/modules/digiquali/survey/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/core/modules/digiquali/survey/index.php @@ -0,0 +1,2 @@ + + * + * 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 . + * or see https://www.gnu.org/ + */ + +/** + * \file core/modules/digiquali/survey/mod_survey_standard.php + * \ingroup digiquali + * \brief File of class to manage survey numbering rules standard + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../../../../saturne/core/modules/saturne/modules_saturne.php'; + +/** + * Class to manage survey numbering rules standard + */ +class mod_survey_standard extends ModeleNumRefSaturne +{ + /** + * @var string Numbering module ref prefix + */ + public string $prefix = 'SY'; + + /** + * @var string Name + */ + public string $name = 'Télesto'; +} diff --git a/core/modules/digiquali/surveydet/index.php b/core/modules/digiquali/surveydet/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/core/modules/digiquali/surveydet/index.php @@ -0,0 +1,2 @@ + + * + * 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 . + * or see https://www.gnu.org/ + */ + +/** + * \file core/modules/digiquali/surveydet/mod_surveydet_standard.php + * \ingroup digiquali + * \brief File of class to manage surveydet numbering rules standard + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../../../../saturne/core/modules/saturne/modules_saturne.php'; + +/** + * Class to manage surveydet numbering rules standard + */ +class mod_surveydet_standard extends ModeleNumRefSaturne +{ + /** + * @var string Numbering module ref prefix + */ + public string $prefix = 'SYR'; + + /** + * @var string Name + */ + public string $name = 'Paaliaq'; +} diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 9e428b25..4f4f6418 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -77,7 +77,7 @@ public function __construct($db) $this->editor_url = 'https://evarisk.com/'; // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated' or a version string like 'x.y.z' - $this->version = '1.10.0'; + $this->version = '1.11.0'; // Url to the file with your last numberversion of this module //$this->url_last_version = 'http://www.example.com/versionmodule.txt'; @@ -97,7 +97,7 @@ public function __construct($db) // 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, + 'substitutions' => 1, // 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) @@ -126,7 +126,9 @@ public function __construct($db) 'publicsurvey', 'digiqualiadmindocuments', 'projecttaskscard', - 'main' + 'main', + 'controladmin', + 'surveyadmin', ], // Set this to 1 if features of module are opened to external users 'moduleforexternal' => 0, @@ -139,7 +141,8 @@ public function __construct($db) '/digiquali/question', '/ecm/digiquali', '/ecm/digiquali/medias', - '/ecm/digiquali/controldocument' + '/ecm/digiquali/controldocument', + '/ecm/digiquali/surveydocument' ]; // Config pages. Put here list of php page, stored into digiquali/admin directory, to use to set up module. @@ -196,6 +199,7 @@ public function __construct($db) // $i++ => ['DIGIQUALI_SHEET_LINK_SUPPLIER_ORDER', 'integer', 0, '', 0, 'current'], // $i++ => ['DIGIQUALI_SHEET_LINK_SUPPLIER_INVOICE', 'integer', 0, '', 0, 'current'], $i++ => ['DIGIQUALI_SHEET_DEFAULT_TAG', 'integer', 0, '', 0, 'current'], + $i++ => ['DIGIQUALI_SHEET_BACKWARD_COMPATIBILITY', 'integer', 0, '', 0, 'current'], // CONST QUESTION $i++ => ['DIGIQUALI_QUESTION_ADDON', 'chaine', 'mod_question_standard', '', 0, 'current'], @@ -217,6 +221,10 @@ public function __construct($db) $i++ => ['DIGIQUALI_SHOW_QC_FREQUENCY_PUBLIC_INTERFACE', 'integer', 1, '', 0, 'current'], $i++ => ['DIGIQUALI_SHOW_LAST_CONTROL_FIRST_ON_PUBLIC_HISTORY', 'integer', 1, '', 0, 'current'], + // CONST SURVEY + $i++ => ['DIGIQUALI_SURVEY_ADDON', 'chaine', 'mod_survey_standard', '', 0, 'current'], + $i++ => ['DIGIQUALI_SURVEY_USE_LARGE_MEDIA_IN_GALLERY', 'integer', 1, '', 0, 'current'], + // CONST DIGIQUALI DOCUMENTS $i++ => ['DIGIQUALI_AUTOMATIC_PDF_GENERATION', 'integer', 0, '', 0, 'current'], $i++ => ['DIGIQUALI_MANUAL_PDF_GENERATION', 'integer', 0, '', 0, 'current'], @@ -227,9 +235,15 @@ public function __construct($db) $i++ => ['DIGIQUALI_CONTROLDOCUMENT_ADDON_ODT_PATH', 'chaine', 'DOL_DOCUMENT_ROOT/custom/digiquali/documents/doctemplates/controldocument/', '', 0, 'current'], $i++ => ['DIGIQUALI_CONTROLDOCUMENT_CUSTOM_ADDON_ODT_PATH', 'chaine', 'DOL_DATA_ROOT' . (($conf->entity == 1 ) ? '/' : '/' . $conf->entity . '/') . 'ecm/digiquali/controldocument/', '', 0, 'current'], $i++ => ['DIGIQUALI_CONTROLDOCUMENT_DEFAULT_MODEL', 'chaine', 'template_controldocument_photo' ,'', 0, 'current'], - $i++ => ['DIGIQUALI_CONTROLDOCUMENT_DISPLAY_MEDIAS', 'integer', 1,'', 0, 'current'], $i++ => ['DIGIQUALI_DOCUMENT_MEDIA_VIGNETTE_USED', 'chaine', 'small','', 0, 'current'], + //CONST SURVEY DOCUMENT + $i++ => ['DIGIQUALI_SURVEYDOCUMENT_ADDON', 'chaine', 'mod_surveydocument_standard', '', 0, 'current'], + $i++ => ['DIGIQUALI_SURVEYDOCUMENT_ADDON_ODT_PATH', 'chaine', 'DOL_DOCUMENT_ROOT/custom/digiquali/documents/doctemplates/surveydocument/', '', 0, 'current'], + $i++ => ['DIGIQUALI_SURVEYDOCUMENT_CUSTOM_ADDON_ODT_PATH', 'chaine', 'DOL_DATA_ROOT' . (($conf->entity == 1 ) ? '/' : '/' . $conf->entity . '/') . 'ecm/digiquali/surveydocument/', '', 0, 'current'], + $i++ => ['DIGIQUALI_SURVEYDOCUMENT_DEFAULT_MODEL', 'chaine', 'template_surveydocument_photo' ,'', 0, 'current'], + //$i++ => ['DIGIQUALI_SURVEYDOCUMENT_DISPLAY_MEDIAS', 'integer', 1,'', 0, 'current'], + // CONST CONTROL LINE $i++ => ['DIGIQUALI_CONTROLDET_ADDON', 'chaine', 'mod_controldet_standard', '', 0, 'current'], $i++ => ['DIGIQUALI_CONTROLDET_AUTO_SAVE_ACTION', 'integer', 1, '', 0, 'current'], @@ -237,6 +251,10 @@ public function __construct($db) // CONST CONTROL EQUIPMENT $i++ => ['DIGIQUALI_CONTROL_EQUIPMENT_ADDON', 'chaine', 'mod_control_equipment_standard', '', 0, 'current'], + // CONST SURVEY LINE + $i++ => ['DIGIQUALI_SURVEYDET_ADDON', 'chaine', 'mod_surveydet_standard', '', 0, 'current'], + $i++ => ['DIGIQUALI_SURVEYDET_AUTO_SAVE_ACTION', 'integer', 1, '', 0, 'current'], + // CONST MODULE $i++ => ['DIGIQUALI_VERSION','chaine', $this->version, '', 0, 'current'], $i++ => ['DIGIQUALI_DB_VERSION', 'chaine', $this->version, '', 0, 'current'], @@ -253,6 +271,7 @@ public function __construct($db) $i++ => ['DIGIQUALI_REDIRECT_AFTER_CONNECTION', 'integer', 0, '', 0, 'current'], $i++ => ['DIGIQUALI_ADVANCED_TRIGGER', 'integer', 1, '', 0, 'current'], $i++ => ['DIGIQUALI_DOCUMENT_DIRECTORIES_NAME_BACKWARD_COMPATIBILITY', 'integer', 0, '', 0, 'current'], + $i++ => ['DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE', 'chaine', $langs->trans('AnswerPublicInterface'), '', 0, 'current'], $i++ => ['AGENDA_REMINDER_BROWSER', 'integer', 1, '', 0, 'current'], $i++ => ['AGENDA_REMINDER_EMAIL', 'integer', 1, '', 0, 'current'], @@ -291,57 +310,67 @@ public function __construct($db) $objectType = $linkableElement['tab_type']; } $this->tabs[] = ['data' => $objectType . ':+control:' . $pictoDigiQuali . $langs->trans('Controls') . ':digiquali@digiquali:$user->rights->digiquali->control->read:/custom/digiquali/view/control/control_list.php?fromid=__ID__&fromtype=' . $linkableElement['link_name']]; + $this->tabs[] = ['data' => $objectType . ':+survey:' . $pictoDigiQuali . $langs->trans('Surveys') . ':digiquali@digiquali:$user->rights->digiquali->survey->read:/custom/digiquali/view/survey/survey_list.php?fromid=__ID__&fromtype=' . $linkableElement['link_name']]; $this->module_parts['hooks'][] = $linkableElement['hook_name_list']; $this->module_parts['hooks'][] = $linkableElement['hook_name_card']; } } - // Dictionaries. + // Dictionaries $this->dictionaries = [ 'langs' => 'digiquali@digiquali', - // List of tables we want to see into dictonnary editor. + // List of tables we want to see into dictonnary editor 'tabname' => [ MAIN_DB_PREFIX . 'c_question_type', - MAIN_DB_PREFIX . 'c_control_attendants_role' + MAIN_DB_PREFIX . 'c_control_attendants_role', + MAIN_DB_PREFIX . 'c_survey_attendants_role', ], - // Label of tables. + // Label of tables 'tablib' => [ 'Question', - 'Control' + 'Control', + 'Survey' ], - // Request to select fields. + // Request to select fields 'tabsql' => [ 'SELECT f.rowid as rowid, f.ref, f.label, f.description, f.position, f.active FROM ' . MAIN_DB_PREFIX . 'c_question_type as f', - 'SELECT f.rowid as rowid, f.ref, f.label, f.description, f.position, f.active FROM ' . MAIN_DB_PREFIX . 'c_control_attendants_role as f' + 'SELECT f.rowid as rowid, f.ref, f.label, f.description, f.position, f.active FROM ' . MAIN_DB_PREFIX . 'c_control_attendants_role as f', + 'SELECT f.rowid as rowid, f.ref, f.label, f.description, f.position, f.active FROM ' . MAIN_DB_PREFIX . 'c_survey_attendants_role as f' ], - // Sort order. + // Sort order 'tabsqlsort' => [ + 'label ASC', 'label ASC', 'label ASC' ], - // List of fields (result of select to show dictionary). + // List of fields (result of select to show dictionary) 'tabfield' => [ + 'ref,label,description,position', 'ref,label,description,position', 'ref,label,description,position' ], - // List of fields (list of fields to edit a record). + // List of fields (list of fields to edit a record) 'tabfieldvalue' => [ + 'ref,label,description,position', 'ref,label,description,position', 'ref,label,description,position' ], - // List of fields (list of fields for insert). + // List of fields (list of fields for insert) 'tabfieldinsert' => [ + 'ref,label,description,position', 'ref,label,description,position', 'ref,label,description,position' ], - // Name of columns with primary key (try to always name it 'rowid'). + // Name of columns with primary key (try to always name it 'rowid') 'tabrowid' => [ + 'rowid', 'rowid', 'rowid' ], - // Condition to show each dictionary. + // Condition to show each dictionary 'tabcond' => [ + $conf->digiquali->enabled, $conf->digiquali->enabled, $conf->digiquali->enabled ] @@ -426,6 +455,23 @@ public function __construct($db) $this->rights[$r][5] = 'delete'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) $r++; + /* SURVEY PERMISSSIONS */ + $this->rights[$r][0] = $this->numero . sprintf('%02d', $r + 1); // Permission id (must not be already used) + $this->rights[$r][1] = $langs->transnoentities('ReadObjects', dol_strtolower($langs->transnoentities('Surveys'))); // Permission label + $this->rights[$r][4] = 'survey'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $this->rights[$r][5] = 'read'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $r++; + $this->rights[$r][0] = $this->numero . sprintf('%02d', $r + 1); // Permission id (must not be already used) + $this->rights[$r][1] = $langs->transnoentities('CreateObjects', dol_strtolower($langs->transnoentities('Surveys'))); // Permission label + $this->rights[$r][4] = 'survey'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $this->rights[$r][5] = 'write'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $r++; + $this->rights[$r][0] = $this->numero . sprintf('%02d', $r + 1); // Permission id (must not be already used) + $this->rights[$r][1] = $langs->transnoentities('DeleteObjects', dol_strtolower($langs->transnoentities('Surveys'))); // Permission label + $this->rights[$r][4] = 'survey'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $this->rights[$r][5] = 'delete'; // In php code, permission will be checked by test if ($user->rights->digiquali->level1->level2) + $r++; + /* ADMINPAGE PANEL ACCESS PERMISSIONS */ $this->rights[$r][0] = $this->numero . sprintf('%02d', $r + 1); $this->rights[$r][1] = $langs->transnoentities('ReadAdminPage', 'DigiQuali'); @@ -551,6 +597,37 @@ public function __construct($db) 'user' => 0, ]; + $this->menu[$r++] = [ + 'fk_menu' => 'fk_mainmenu=digiquali', + 'type' => 'left', + 'titre' => $langs->transnoentities('Survey'), + 'prefix' => '', + 'mainmenu' => 'digiquali', + 'leftmenu' => 'digiquali_survey', + 'url' => '/digiquali/view/survey/survey_list.php', + 'langs' => 'digiquali@digiquali', + 'position' => 1000 + $r, + 'enabled' => '$conf->digiquali->enabled && $user->rights->digiquali->survey->read', + 'perms' => '$user->rights->digiquali->survey->read', + 'target' => '', + 'user' => 0, + ]; + + $this->menu[$r++] = [ + 'fk_menu' => 'fk_mainmenu=digiquali,fk_leftmenu=digiquali_survey', + 'type' => 'left', + 'titre' => '' . $langs->transnoentities('Categories'), + 'mainmenu' => 'digiquali', + 'leftmenu' => 'digiquali_surveytags', + 'url' => '/categories/index.php?type=survey', + 'langs' => 'digiquali@digiquali', + 'position' => 1000 + $r, + 'enabled' => '$conf->digiquali->enabled && $conf->categorie->enabled && $user->rights->digiquali->survey->read', + 'perms' => '$user->rights->digiquali->survey->read', + 'target' => '', + 'user' => 0, + ]; + $this->menu[$r++] = [ 'fk_menu' => 'fk_mainmenu=digiquali', 'type' => 'left', @@ -620,9 +697,11 @@ public function init($options = ''): int dolibarr_set_const($this->db, 'DIGIQUALI_DB_VERSION', $this->version, 'chaine', 0, '', $conf->entity); delDocumentModel('controldocument_odt', 'controldocument'); + delDocumentModel('surveydocument_odt', 'surveydocument'); delDocumentModel('calypso_controldocument', 'controldocument'); addDocumentModel('controldocument_odt', 'controldocument', 'ODT templates', 'DIGIQUALI_CONTROLDOCUMENT_ADDON_ODT_PATH'); + addDocumentModel('surveydocument_odt', 'surveydocument', 'ODT templates', 'DIGIQUALI_SURVEYDOCUMENT_ADDON_ODT_PATH'); if (!empty($conf->global->DIGIQUALI_SHEET_TAGS_SET) && empty($conf->global->DIGIQUALI_SHEET_DEFAULT_TAG)) { global $user, $langs; @@ -646,8 +725,10 @@ public function init($options = ''): int $linkableObject = new $className($this->db); $tableElement = $linkableObject->table_element; - $extraFields->addExtraField('qc_frequency', 'QcFrequency', 'int', 100, 10, $tableElement, 0, 0, '', 'a:1:{s:7:"options";a:1:{s:0:"";N;}}', 1, '', '1', '','',0, 'digiquali@digiquali', '$conf->digiquali->enabled'); - $extraFields->addExtraField('control_history_link', 'ControlHistoryLink', 'varchar', 100, 255, $tableElement, 0, 0, '', '', 0, '', '1', '','',0, 'digiquali@digiquali', '$conf->digiquali->enabled'); + $extraFields->addExtraField('qc_frequency', 'QcFrequency', 'int', 100, 10, $tableElement, 0, 0, '', 'a:1:{s:7:"options";a:1:{s:0:"";N;}}', 1, '', 1, '','',0, 'digiquali@digiquali', '$conf->digiquali->enabled'); + + $extraFields->update('control_history_link', 'ControlHistoryLink', 'varchar', 255, $tableElement, 0, 0, 110, '', 0, '', 5, '', '', '', 0, 'digiquali@digiquali', '$conf->digiquali->enabled'); + $extraFields->addExtraField('control_history_link', 'ControlHistoryLink', 'varchar', 110, 255, $tableElement, 0, 0, '', '', 0, '', 5, '','',0, 'digiquali@digiquali', '$conf->digiquali->enabled'); } } @@ -680,6 +761,20 @@ public function init($options = ''): int dolibarr_set_const($this->db, 'DIGIQUALI_CONTROL_BACKWARD_COMPATIBILITY', 1, 'integer', 0, '', $conf->entity); } + if (getDolGlobalInt('DIGIQUALI_SHEET_BACKWARD_COMPATIBILITY') == 0) { + require_once __DIR__ . '/../../class/sheet.class.php'; + $sheet = new Sheet($this->db); + $sheets = $sheet->fetchAll(); + if (is_array($sheets) && !empty($sheets)) { + foreach ($sheets as $sheet) { + $sheet->type = 'control'; + $sheet->setValueFrom('type', $sheet->type, '', '', 'text', '', $user, strtoupper($sheet->element) . '_MODIFY'); + } + } + + dolibarr_set_const($this->db, 'DIGIQUALI_SHEET_BACKWARD_COMPATIBILITY', 1, 'integer', 0, '', $conf->entity); + } + // Permissions $this->remove($options); diff --git a/core/substitutions/functions_digiquali.lib.php b/core/substitutions/functions_digiquali.lib.php new file mode 100644 index 00000000..a86c93d4 --- /dev/null +++ b/core/substitutions/functions_digiquali.lib.php @@ -0,0 +1,38 @@ + + * + * 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 . + * or see https://www.gnu.org/ + */ + +/** + * \file core/substitutions/functions_digiquali.lib.php + * \ingroup digiquali + * \brief File of functions to substitutions array + */ + +/** Function called to complete substitution array (before generating on ODT, or a personalized email) + * functions xxx_completesubstitutionarray are called by make_substitutions() if file + * is inside directory htdocs/core/substitutions + * + * @param array $substitutionarray Array with substitution key => val + * @param Translate $langs Output langs + * @param Object|string|null $object Object to use to get values + * @return void The entry parameter $substitutionarray is modified + * @throws Exception + */ +function digiquali_completesubstitutionarray(array &$substitutionarray, Translate $langs, $object) +{ + $substitutionarray['__OBJECT_ELEMENT_REF__'] = $langs->transnoentities('The' . ucfirst($object->element)) . ' ' . $object->ref; +} diff --git a/core/substitutions/index.php b/core/substitutions/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/core/substitutions/index.php @@ -0,0 +1,2 @@ +fetchFromParentWithQuestion($object->id, $questionId); - $questionAnswer = ''; - $comment = ''; - if ($result > 0 && is_array($result)) { - $itemControlDet = array_shift($result); - $questionAnswer = $itemControlDet->answer; - $comment = $itemControlDet->comment; - } +/* Copyright (C) 2022-2024 EVARISK + * + * 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 core/tpl/digiquali_answers.tpl.php + * \ingroup digiquali + * \brief Template page for answers lines + */ + +/** + * The following vars must be defined: + * Global : $conf, $langs, $user + * Parameters : + * Objects : $answer, $object, $objectLine, $sheet + * Variable : $publicInterface + */ + +if (is_array($sheet->linkedObjects['digiquali_question']) && !empty($sheet->linkedObjects['digiquali_question'])) { + foreach ($sheet->linkedObjects['digiquali_question'] as $question) { + $questionAnswer = ''; + $comment = ''; + $result = $objectLine->fetchFromParentWithQuestion($object->id, $question->id); + if (is_array($result) && !empty($result)) { + $objectLine = array_shift($result); + $questionAnswer = $objectLine->answer; + $comment = $objectLine->comment; + } if (!$user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER or empty($questionAnswer)) { - $item = $question; - $item->fetch($questionId); ?> -
+
-
getNomUrl(1, isset($publicInterface) ? 'nolink' : '', 1, '', -1, 1); ?>
-
description; ?>
+
getNomUrl(1, isset($publicInterface) ? 'nolink' : '', 1, '', -1, 1); ?>
+
description; ?>
ref ) ) { - print '' . $itemControlDet->ref . ' :'; + if (!empty($objectLine->ref) ) { + print '' . $objectLine->ref . ' :'; } ?>
- type == 'Text') : ?> + type == 'Text') : ?>
"> ' . $langs->trans('Answer') . ' : '; $object->status > $object::STATUS_DRAFT ? print $questionAnswer : - print 'status > $object::STATUS_DRAFT ? 'disabled' : '') .' name="answer'. $item->id .'" id="answer'. $item->id .'"class="question-textarea input-answer ' . ($object->status > 0 ? 'disable' : '') . '" value="'. $questionAnswer .'">'; - ?> + print 'status > $object::STATUS_DRAFT ? ' disabled' : '') . ' name="answer' . $question->id . '" id="answer' . $question->id . '"class="question-textarea input-answer ' . ($object->status > 0 ? 'disable' : '') . '" value="' . $questionAnswer . '">'; ?>
- enter_comment > 0) : ?> + enter_comment > 0) : ?> trans('Comment') . ' : '; ?> - enter_comment > 0) : ?> + enter_comment > 0) : ?> status > 0 ) : ?> - id .'" id="comment'. $item->id .'" value="'. $comment .'" '. ($object->status == 2 ? 'disabled' : '').'>'; ?> + id . '" id="comment' . $question->id . '" value="' . $comment . '" ' . ($object->status == 2 ? 'disabled' : '') . '>'; ?>
- show_photo > 0) : ?> + show_photo > 0) : ?>
global->DIGIQUALI_CONTROL_DISPLAY_MEDIAS)) : - print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/question/'. $item->ref . '/photo_ok', 'small', '', 0, 0, 0, 200, 200, 0, 0, 0, 'question/'. $item->ref . '/photo_ok', $item, 'photo_ok', 0, 0, 0,1, 'photo-ok', 0); - print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/question/'. $item->ref . '/photo_ko', 'small', '', 0, 0, 0, 200, 200, 0, 0, 0, 'question/'. $item->ref . '/photo_ko', $item, 'photo_ko', 0, 0, 0,1, 'photo-ko', 0); + if (getDolGlobalInt('DIGIQUALI_' . dol_strtoupper($object->element) . '_DISPLAY_MEDIAS')) : + print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/question/'. $question->ref . '/photo_ok', 'small', '', 0, 0, 0, 200, 200, 0, 0, 0, 'question/' . $question->ref . '/photo_ok', $question, 'photo_ok', 0, 0, 0,1, 'photo-ok', 0); + print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/question/'. $question->ref . '/photo_ko', 'small', '', 0, 0, 0, 200, 200, 0, 0, 0, 'question/' . $question->ref . '/photo_ko', $question, 'photo_ko', 0, 0, 0,1, 'photo-ko', 0); endif; ?>
-
+
- authorize_answer_photo > 0) : ?> -
+ authorize_answer_photo > 0) : ?> +
status == 0 ) : ?> - - -
diff --git a/core/tpl/digiquali_survey_list.tpl.php b/core/tpl/digiquali_survey_list.tpl.php new file mode 100644 index 00000000..7a15ec98 --- /dev/null +++ b/core/tpl/digiquali_survey_list.tpl.php @@ -0,0 +1,652 @@ +fields as $key => $val) { + if (!array_key_exists($key, $elementElementFields)) { + $sql .= 't.' . $key . ', '; + } +} + +// Add fields from extrafields +if (!empty($extrafields->attributes[$object->table_element]['label'])) { + foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql .= ($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.' as options_'.$key.', ' : ''); +} +// Add fields from hooks +$parameters = []; +$hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= preg_replace('/^,/', '', $hookmanager->resPrint); +$sql = preg_replace('/,\s*$/', '', $sql); +foreach($elementElementFields as $genericName => $elementElementName) { + if (GETPOST('search_' . $genericName) > 0 || $fromtype == $elementElementName) { + $id_tosearch = GETPOST('search' . $genericName) ?: $fromid; + $sql .= ',' . $elementElementName . '.fk_source, '; + } +} +$sql = rtrim($sql, ', '); +if (array_key_exists($sortfield, $elementElementFields) && !preg_match('/' . 'LEFT JOIN ' . MAIN_DB_PREFIX . 'element_element as ' . $elementElementFields[$sortfield] . '/', $sql)) { + $sql .= ',' . $elementElementFields[$sortfield] . '.fk_source'; +} +$sql .= ' FROM ' . MAIN_DB_PREFIX . $object->table_element . ' as t'; +if (!empty($conf->categorie->enabled)) { + $sql .= Categorie::getFilterJoinQuery('survey', "t.rowid"); +} +if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; + +foreach($elementElementFields as $genericName => $elementElementName) { + if (GETPOST('search_'.$genericName) > 0 || $fromtype == $elementElementName) { + $id_to_search = GETPOST('search_'.$genericName) ?: $fromid; + $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'element_element as '. $elementElementName .' on ('. $elementElementName .'.fk_source = ' . $id_to_search . ' AND '. $elementElementName .'.sourcetype="'. $elementElementName .'" AND '. $elementElementName .'.targettype = "digiquali_survey")'; + } +} + +if (array_key_exists($sortfield,$elementElementFields) && !preg_match('/' . 'LEFT JOIN ' . MAIN_DB_PREFIX . 'element_element as '. $elementElementFields[$sortfield] .'/', $sql)) { + $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'element_element as '. $elementElementFields[$sortfield] .' on ( '. $elementElementFields[$sortfield] .'.sourcetype="'. $elementElementFields[$sortfield] .'" AND '. $elementElementFields[$sortfield] .'.targettype = "digiquali_survey" AND '. $elementElementFields[$sortfield] .'.fk_target = t.rowid)'; +} + +// Add table from hooks +$parameters = []; +$hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // 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) . ')'; +} else { + $sql .= ' WHERE 1 = 1'; +} +$sql .= ' AND t.status >= ' . Survey::STATUS_DRAFT; + +foreach($elementElementFields as $genericName => $elementElementName) { + if (GETPOST('search_'.$genericName) > 0 || $fromtype == $elementElementName) { + $sql .= ' AND t.rowid = ' . $elementElementName . '.fk_target '; + } +} + +foreach ($search as $key => $val) { + if (!array_key_exists($key, $elementElementFields)) { + if (array_key_exists($key, $object->fields)) { + if ($key == 'status' && $val == -1) { + 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 ($val == '-1' || ($val === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) { + $val = ''; + } + $mode_search = 2; + } + if ($val != '') { + $sql .= natural_search('t.'. $db->escape($key), $val, (($key == 'status') ? 2 : $mode_search)); + } + } elseif (preg_match('/(_dtstart|_dtend)$/', $key) && $val != '') { + $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key); + if (preg_match('/^(date|timestamp|datetime)/', $object->fields[$columnName]['type'])) { + if (preg_match('/_dtstart$/', $key)) { + $sql .= ' AND t.' . $columnName . " >= '" . $db->idate($val) . "'"; + } + if (preg_match('/_dtend$/', $key)) { + $sql .= ' AND t.' . $columnName . " <= '" . $db->idate($val) . "'"; + } + } + } + } +} + +if ($search_all) { + $sql .= natural_search(array_keys($fieldstosearchall), $search_all); +} + +if (isModEnabled('categorie')) { + $sql .= Categorie::getFilterSelectQuery('survey', 't.rowid', $search_category_array); +} + +// Add where from extra fields +require DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_sql.tpl.php'; + +// Add where from hooks +$parameters = []; +$hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook +$sql .= $hookmanager->resPrint; + +// Count total nb of records +$nbTotalOfRecords = ''; +if (empty($conf->global->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('/^SELECT[a-zA-Z0-9\._\s\(\),=<>\:\-\']+\sFROM/', 'SELECT COUNT(*) as nbtotalofrecords FROM', $sql); + $resql = $db->query($sqlForCount); + if ($resql) { + $objForCount = $db->fetch_object($resql); + $nbTotalOfRecords = $objForCount->nbtotalofrecords; + } else { + dol_print_error($db); + } + + if (($page * $limit) > $nbTotalOfRecords) { // if total of record found is smaller than page * limit, goto and load page 0 + $page = 0; + $offset = 0; + } + $db->free($resql); +} + +// Complete request and execute it with limit +if (array_key_exists($sortfield, $elementElementFields)) { + $sql .= ' ORDER BY '. $elementElementFields[$sortfield] . '.fk_source ' . $sortorder; +} else { + $sql .= $db->order($sortfield, $sortorder); +} + +if ($limit) { + $sql .= $db->plimit($limit + 1, $offset); +} + +$resql = $db->query($sql); +if (!$resql) { + dol_print_error($db); + exit; +} + +$num = $db->num_rows($resql); + +// Direct jump if only one record found +if ($num == 1 && !empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all && !$page) { + $obj = $db->fetch_object($resql); + $id = $obj->rowid; + header('Location: ' . dol_buildpath('/digiquali/view/survey/survey_card.php', 1) . '?id=' . $id); + exit; +} + +// Output page +// -------------------------------------------------------------------- + +$arrayofselected = is_array($toselect) ? $toselect : []; +$extraparams = $fromtype && $fromid ? '?fromtype=' . $fromtype . '&fromid=' . $fromid : ''; + +$param = $extraparams; +if (!empty($mode)) { + $param .= '&mode=' . urlencode($mode); +} +if (!empty($contextpage) && $contextpage != $_SERVER['PHP_SELF']) { + $param .= '&contextpage=' . urlencode($contextpage); +} +if ($limit > 0 && $limit != $conf->liste_limit) { + $param .= '&limit=' . urlencode($limit); +} +foreach ($search as $key => $val) { + if (is_array($val) && count($val)) { + foreach ($val as $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 ($val != '') { + $param .= '&search_' . $key . '=' . urlencode($val); + } +} +if ($optioncss != '') { + $param .= '&optioncss='.urlencode($optioncss); +} +// Add $param from extra fields +require DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_param.tpl.php'; + +// Add $param from hooks +$parameters = []; +$hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object); // Note that $action and $object may have been modified by hook +$param .= $hookmanager->resPrint; + +// List of mass actions available +$arrayofmassactions = ['prearchive' => '' . $langs->trans('Archive')]; +if ($permissiontodelete) { + $arrayofmassactions['predelete'] = img_picto('', 'delete', 'class="pictofixedwidth"') . $langs->trans('Delete'); +} +if (GETPOST('nomassaction', 'int') || in_array($massaction, ['presend', 'predelete'])) { + $arrayofmassactions = []; +} +$massactionbutton = $form->selectMassAction('', $arrayofmassactions); + +print ''; +if ($optioncss != '') { + print ''; +} +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''; +if (GETPOSTISSET('id')) { + print ''; +} +if (!empty($fromtype)) { + $fromUrl = '&fromtype=' . $fromtype . '&fromid=' . $fromid; +} + +$newcardbutton = ''; +$newcardbutton .= dolGetButtonTitle($langs->trans('ViewList'), '', 'fa fa-bars imgforviewmode', $_SERVER['PHP_SELF'] . '?mode=common' . preg_replace('/([&?])*mode=[^&]+/', '', $param), '', ((empty($mode) || $mode == 'common') ? 2 : 1), ['morecss' => 'reposition']); +$newcardbutton .= dolGetButtonTitle($langs->trans('ViewKanban'), '', 'fa fa-th-list imgforviewmode', $_SERVER['PHP_SELF'] . '?mode=kanban' . preg_replace('/([&?])*mode=[^&]+/', '', $param), '', ($mode == 'kanban' ? 2 : 1), ['morecss' => 'reposition']); +$newcardbutton .= dolGetButtonTitleSeparator(); +$newcardbutton .= dolGetButtonTitle($langs->trans('New'), '', 'fa fa-plus-circle', dol_buildpath('/digiquali/view/survey/survey_card.php', 1) . '?action=create' . $fromUrl, '', $permissiontoadd); + +print_barre_liste($title, $page, $_SERVER['PHP_SELF'], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbTotalOfRecords, 'object_' . $object->picto, 0, $newcardbutton, '', $limit, 0, 0, 1); + +if ($massaction == 'prearchive') { + print $form->formconfirm($_SERVER['PHP_SELF'], $langs->trans('ConfirmMassArchive'), $langs->trans('ConfirmMassArchivingQuestion', count($toselect)), 'archive', null, '', 0, 200, 500, 1); +} + +// Add code for pre mass action (confirmation or email presend form) +$topicmail = 'SendSurveyRef'; +$modelmail = 'survey'; +$objecttmp = new Survey($db); +$trackid = 'xxxx'.$object->id; +require DOL_DOCUMENT_ROOT . '/core/tpl/massactions_pre.tpl.php'; + +if ($search_all) { + $setupstring = ''; + foreach ($fieldstosearchall as $key => $val) { + $fieldstosearchall[$key] = $langs->trans($val); + $setupstring .= $key . '=' . $val . ';'; + } + print ''; + print '
' . $langs->trans('FilterOnInto', $search_all) . join(', ', $fieldstosearchall) . '
'; +} + +$moreforfilter = ''; + +// Filter on categories +if (isModEnabled('categorie') && $user->rights->categorie->lire) { + $formCategory = new FormCategory($db); + $moreforfilter .= $formCategory->getFilterBox('survey', $search_category_array); +} + +$parameters = []; +$resHook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +if (empty($resHook)) { + $moreforfilter .= $hookmanager->resPrint; +} else { + $moreforfilter = $hookmanager->resPrint; +} + +if (!empty($moreforfilter)) { + print '
'; + print $moreforfilter; + print '
'; +} + +$varpage = empty($contextpage) ? $_SERVER['PHP_SELF'] : $contextpage; + +$signatoriesInDictionary = saturne_fetch_dictionary('c_' . $object->element . '_attendants_role'); +if (is_array($signatoriesInDictionary) && !empty($signatoriesInDictionary)) { + $customFieldsPosition = 111; + foreach ($signatoriesInDictionary as $signatoryInDictionary) { + $arrayfields[$signatoryInDictionary->ref] = ['label' => $signatoryInDictionary->ref, 'checked' => 1, 'position' => $customFieldsPosition++, 'css' => 'minwidth300 maxwidth500 widthcentpercentminusxx']; + } +} + +$arrayfields['SocietyAttendants'] = ['label' => 'SocietyAttendants', 'checked' => 1, 'position' => 115, 'css' => 'minwidth300 maxwidth500 widthcentpercentminusxx']; + +$selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage, getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')); // This also change content of $arrayfields +$selectedfields .= (count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : ''); + +$signatoriesInDictionary = saturne_fetch_dictionary('c_' . $object->element . '_attendants_role'); +if (is_array($signatoriesInDictionary) && !empty($signatoriesInDictionary)) { + foreach ($signatoriesInDictionary as $signatoryInDictionary) { + $object->fields['Custom'][$signatoryInDictionary->ref] = $arrayfields[$signatoryInDictionary->ref]; + } +} + +$object->fields['Custom']['SocietyAttendants'] = $arrayfields['SocietyAttendants']; + +print '
'; // You can use div-table-responsive-no-min if you don't need reserved height for your table +print '
- getNomUrl(1, 'nolink', 1) . '
'; + trans('BasedOnModel') . ' :
' . $sheet->getNomUrl(1, '', 0, '', -1, 1) . '
'; print ' ' . $langs->trans('Verdict') . '
'; print '
' . saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/qrcode/', 'small', 1, 0, 0, 0, 70, 70, 0, 0, 1, 'control/'. $object->ref . '/qrcode/', $object, '', 0, 0) . '
'; ?>
'; + +// Fields title search +// -------------------------------------------------------------------- +print ''; +// Action column +if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print ''; +} +foreach ($object->fields as $key => $val) +{ + $cssforfield = (empty($val['css']) ? '' : $val['css']); + if ($key == 'status') $cssforfield .= ($cssforfield ? ' ' : '').'center'; + elseif (in_array($val['type'], array('date', 'datetime', 'timestamp'))) $cssforfield .= ($cssforfield ? ' ' : '').'center'; + elseif (in_array($val['type'], array('timestamp'))) $cssforfield .= ($cssforfield ? ' ' : '').'nowrap'; + elseif (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && $val['label'] != 'TechnicalID') $cssforfield .= ($cssforfield ? ' ' : '').'right'; + if (!empty($arrayfields['t.'.$key]['checked'])) + { + print ''; + } elseif ($key == 'Custom') { + foreach ($val as $resource) { + if ($resource['checked']) { + if ($resource['label'] == 'SocietyAttendants') { + print ''; + } else { + print ''; + } + } + } + } +} + +// Extra fields +require DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_input.tpl.php'; + +// Fields from hook +$parameters = ['arrayfields' => $arrayfields]; +$hookmanager->executeHooks('printFieldListOption', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print ''; +} +print ''; + +$totalarray = []; +$totalarray['nbfield'] = 0; + +// Fields title label +// -------------------------------------------------------------------- +print ''; +if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print getTitleFieldOfList(($mode != 'kanban' ? $selectedfields : ''), 0, $_SERVER['PHP_SELF'], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); +} + +$invertedElementElementFields = array_flip($elementElementFields); +foreach ($object->fields as $key => $val) { + $disableSortField = dol_strlen($fromtype) > 0 ? preg_match('/'. $invertedElementElementFields[$fromtype] .'/',$key) : 0; + + $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']); + if ($key == 'status') { + $cssforfield .= ($cssforfield ? ' ' : '') . 'center'; + } elseif (in_array($val['type'], ['date', 'datetime', 'timestamp'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'center'; + } elseif (in_array($val['type'], ['timestamp'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap'; + } elseif (in_array($val['type'], ['double(24,8)', 'double(6,3)', 'integer', 'real', 'price']) && $val['label'] != 'TechnicalID' && empty($val['arrayofkeyval'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'right'; + } + $cssforfield = preg_replace('/small\s*/', '', $cssforfield); // the 'small' css must not be used for the title label + if (!empty($arrayfields['t.'.$key]['checked'])) { + print getTitleFieldOfList($arrayfields['t.' . $key]['label'], 0, $_SERVER['PHP_SELF'], 't.' . $key, '', $param, ($cssforfield ? 'class="' . $cssforfield . '"' : ''), $sortfield, $sortorder, ($cssforfield ? $cssforfield . ' ' : ''), $disableSortField); + $totalarray['nbfield']++; + } elseif ($key == 'Custom') { + foreach ($val as $resource) { + if ($resource['checked']) { + print ''; + } + } + } +} + +// Extra fields +require DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_search_title.tpl.php'; + +// Hook fields +$parameters = ['arrayfields' => $arrayfields, 'param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder, 'totalarray' => &$totalarray]; +$hookmanager->executeHooks('printFieldListTitle', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; +// Action column +if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print getTitleFieldOfList(($mode != 'kanban' ? $selectedfields : ''), 0, $_SERVER['PHP_SELF'], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch '); +} +print ''; + +// Detect if we need a fetch on each output line +$needToFetchEachLine = 0; +if (isset($extrafields->attributes[$object->table_element]['computed']) && is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0) { + foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val) { + if (preg_match('/\$object/', $val)) { + $needToFetchEachLine++; // There is at least one compute field that use $object + } + } +} + +// Loop on record +// -------------------------------------------------------------------- +$i = 0; +$savnbfield = $totalarray['nbfield'] + 1; +$totalarray = []; +$totalarray['nbfield'] = 0; +$imaxinloop = ($limit ? min($num, $limit) : $num); + +$revertedElementFields = array_flip($elementElementFields); +$linkedObjects = $object->fetchAllLinksForObjectType(); + +while ($i < $imaxinloop) { + $obj = $db->fetch_object($resql); + if (empty($obj)) { + break; // Should not happen + } + + // Store properties in $object + $object->setVarsFromFetchObj($obj); + + $filter = ['customsql' => 'fk_object = ' . $object->id . ' AND status > 0 AND object_type = "' . $object->element . '"']; + $signatories = $signatory->fetchAll('', 'role', 0, 0, $filter); + + if ($mode == 'kanban') { + if ($i == 0) { + print ''; + } + } else { + // Show here line of result + print ''; + // Action column + if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print ''; + } + foreach ($object->fields as $key => $val) { + $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']); + if (in_array($val['type'], ['date', 'datetime', 'timestamp'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'center'; + } elseif ($key == 'status') { + $cssforfield .= ($cssforfield ? ' ' : '') . 'center'; + } + + if (in_array($val['type'], ['timestamp'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap'; + } elseif ($key == 'ref') { + $cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap'; + } + + if (in_array($val['type'], ['double(24,8)', 'double(6,3)', 'integer', 'real', 'price']) && !in_array($key, ['rowid', 'status']) && empty($val['arrayofkeyval'])) { + $cssforfield .= ($cssforfield ? ' ' : '') . 'right'; + } + if (!empty($arrayfields['t.' . $key]['checked'])) { + print ''; + if ($key == 'status') { + print $object->getLibStatut(5); + } elseif ($key == 'fk_sheet') { + $sheet->fetch($object->fk_sheet); + print $sheet->getNomUrl(1); + } elseif (in_array($key, $revertedElementFields)) { + $linkedElement = $linkNameElementCorrespondence[$elementElementFields[$key]]; + + if (is_array($linkedObjects[$obj->rowid]) && !empty($linkedElement['conf']) && (!empty($linkedObjects[$obj->rowid][$linkedElement['link_name']]))) { + $className = $linkedElement['className']; + $linkedObject = new $className($db); + + $linkedObjectType = $linkedElement['link_name']; + $linkedObjectId = $linkedObjects[$obj->rowid][$linkedElement['link_name']]; + + if (!is_object($alreadyFetchedObjects[$linkedObjectType][$linkedObjectId])) { + $result = $linkedObject->fetch($linkedObjectId); + } else { + $linkedObject = $alreadyFetchedObjects[$linkedObjectType][$linkedObjectId]; + $result = $linkedObjects[$obj->rowid][$linkedElement['link_name']]; + } + if ($result > 0) { + $alreadyFetchedObjects[$linkedObjectType][$linkedObjectId] = $linkedObject; + print $linkedObject->getNomUrl(1); + } + } + } + else print $object->showOutputField($val, $key, $object->$key, ''); + print ''; + if (!$i) $totalarray['nbfield']++; + if (!empty($val['isameasure'])) + { + if (!$i) $totalarray['pos'][$totalarray['nbfield']] = 't.'.$key; + $totalarray['val']['t.'.$key] += $object->$key; + } + } elseif ($key == 'Custom') { + foreach ($val as $resource) { + if ($resource['checked']) { + if ($resource['label'] == 'SocietyAttendants') { + print ''; + } else { + print ''; + } + } + } + } + } + + // Extra fields + require DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_print_fields.tpl.php'; + + // Fields from hook + $parameters = ['arrayfields' => $arrayfields, 'object' => $object, 'obj' => $obj, 'i' => $i, 'totalarray' => &$totalarray]; + $hookmanager->executeHooks('printFieldListValue', $parameters, $object); // Note that $action and $object may have been modified by hook + print $hookmanager->resPrint; + // Action column + if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) { + print ''; + } + if (!$i) { + $totalarray['nbfield']++; + } + print ''; + } + $i++; +} + +// If no record found +if ($num == 0) { + $colspan = 1; + foreach ($arrayfields as $key => $val) { + if (!empty($val['checked'])) { + $colspan++; + } + } + print ''; +} + +$db->free($resql); + +$parameters = ['arrayfields' => $arrayfields, 'sql' => $sql]; +$hookmanager->executeHooks('printFieldListFooter', $parameters, $object); // Note that $action and $object may have been modified by hook +print $hookmanager->resPrint; + +print '
'; + $searchpicto = $form->showFilterButtons('left'); + print $searchpicto; + print ''; + if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { + print $form->selectarray('search_' . $key, $val['arrayofkeyval'], $search[$key], $val['notnull'], 0, 0, '', 1, 0, 0, '', 'minwidth200', 1); + } + elseif ($key == 'fk_sheet') { + print $sheet->selectSheetList(GETPOST('fromtype') == 'fk_sheet' ? GETPOST('fromid') : ($search['fk_sheet'] ?: 0), 'search_fk_sheet', 's.type = ' . '"' . $object->element . '"'); + } + elseif (strpos($val['type'], 'integer:') === 0) { + print $object->showInputField($val, $key, $search[$key], '', '', 'search_', 'minwidth100 maxwidth125 widthcentpercentminusxx', 1); + } elseif (!preg_match('/^(date|timestamp)/', $val['type'])) { + print ''; + } + print ''; + //print $form->select_company($searchSocietyAttendants, 'search_society_attendants', '', 1); + print ''; + $searchpicto = $form->showFilterButtons(); + print $searchpicto; + print '
'; + print $langs->trans($resource['label']); + print '
'; + print '
'; + } + // Output Kanban + print $object->getKanbanView(); + if ($i == ($imaxinloop - 1)) { + print '
'; + print '
'; + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($object->id, $arrayofselected)) { + $selected = 1; + } + print ''; + } + print ''; + if (is_array($signatories) && !empty($signatories)) { + $alreadyAddedThirdParties = []; + foreach ($signatories as $objectSignatory) { + if ($objectSignatory->element_type == 'socpeople') { + $contact->fetch($objectSignatory->element_id); + $thirdparty->fetch($contact->fk_soc); + if (!in_array($thirdparty->id, $alreadyAddedThirdParties)) { + print $thirdparty->getNomUrl(1); + print '
'; + } + } else { + $userTmp->fetch($objectSignatory->element_id); + if ($userTmp->contact_id > 0) { + $contact->fetch($userTmp->contact_id); + $thirdparty->fetch($contact->fk_soc); + if (!in_array($thirdparty->id, $alreadyAddedThirdParties)) { + print $thirdparty->getNomUrl(1); + print '
'; + } + } + } + $alreadyAddedThirdParties[] = $thirdparty->id; + } + } + print '
'; + if (is_array($signatories) && !empty($signatories) && $signatories > 0) { + foreach ($signatories as $objectSignatory) { + switch ($objectSignatory->attendance) { + case 1: + $cssButton = '#0d8aff'; + $userIcon = 'fa-user-clock'; + break; + case 2: + $cssButton = '#e05353'; + $userIcon = 'fa-user-slash'; + break; + default: + $cssButton = '#47e58e'; + $userIcon = 'fa-user'; + break; + } + if ($objectSignatory->element_type == 'user' && $objectSignatory->role == $resource['label']) { + $userTmp = $user; + $userTmp->fetch($objectSignatory->element_id); + print $userTmp->getNomUrl(1, '', 0, 0, 24, 1) . ' - ' . $objectSignatory->getLibStatut(3); + print ' - '; + print '
'; + } elseif ($objectSignatory->element_type == 'socpeople' && $objectSignatory->role == $resource['label']) { + $contact->fetch($objectSignatory->element_id); + print $contact->getNomUrl(1) . ' - ' . $objectSignatory->getLibStatut(3); + print ' - '; + print '
'; + } + } + } + print '
'; + if ($massactionbutton || $massaction) { // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + $selected = 0; + if (in_array($object->id, $arrayofselected)) { + $selected = 1; + } + print ''; + } + print '
' . $langs->trans('NoRecordFound') . '
'; +print ''; +print ''; diff --git a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php index c539cf99..5c63c671 100644 --- a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php +++ b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php @@ -49,7 +49,7 @@ public function __construct($db) $this->name = preg_replace('/^Interface/i', '', get_class($this)); $this->family = 'demo'; $this->description = 'DigiQuali triggers.'; - $this->version = '1.10.0'; + $this->version = '1.11.0'; $this->picto = 'digiquali@digiquali'; } @@ -163,9 +163,38 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf $actioncomm->create($user); break; + case 'SURVEY_CREATE' : + // Load Digiquali libraries + require_once __DIR__ . '/../../class/sheet.class.php'; + + $sheet = new Sheet($this->db); + + $sheet->fetch($object->fk_sheet); + if ($sheet->success_rate > 0) { + $object->success_rate = $sheet->success_rate; + $object->setValueFrom('success_rate', $object->success_rate, '', '', 'text', '', $user); + } + + if ($object->context != 'createfromclone') { + $elementArray = get_sheet_linkable_objects(); + if (!empty($elementArray)) { + foreach ($elementArray as $linkableElement) { + if (!empty(GETPOST($linkableElement['post_name'])) && GETPOST($linkableElement['post_name']) > 0) { + $object->add_object_linked($linkableElement['link_name'], GETPOST($linkableElement['post_name'])); + } + } + } + } + + $actioncomm->code = 'AC_' . strtoupper($object->element) . '_CREATE'; + $actioncomm->label = $langs->transnoentities('ObjectCreateTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); + $actioncomm->create($user); + break; + case 'QUESTION_MODIFY' : case 'SHEET_MODIFY' : case 'CONTROL_MODIFY' : + case 'SURVEY_MODIFY' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_MODIFY'; $actioncomm->label = $langs->transnoentities('ObjectModifyTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->create($user); @@ -182,6 +211,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf case 'QUESTION_DELETE' : case 'SHEET_DELETE' : case 'CONTROL_DELETE' : + case 'SURVEY_DELETE' : $actioncomm->code = 'AC_ ' . strtoupper($object->element) . '_DELETE'; $actioncomm->label = $langs->transnoentities('ObjectDeleteTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->create($user); @@ -198,12 +228,14 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf case 'QUESTION_VALIDATE' : case 'SHEET_VALIDATE' : case 'CONTROL_VALIDATE' : + case 'SURVEY_VALIDATE' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_VALIDATE'; $actioncomm->label = $langs->transnoentities('ObjectValidateTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->create($user); break; case 'CONTROL_UNVALIDATE' : + case 'SURVEY_UNVALIDATE' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_UNVALIDATE'; $actioncomm->label = $langs->transnoentities('ObjectUnValidateTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->create($user); @@ -211,6 +243,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf case 'QUESTION_LOCK' : case 'SHEET_LOCK' : + case 'SURVEY_LOCK' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_LOCK'; $actioncomm->label = $langs->transnoentities('ObjectLockedTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->note_private .= $langs->trans('Status') . ' : ' . $langs->trans('Locked') . '
'; @@ -296,6 +329,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf case 'QUESTION_ARCHIVE' : case 'SHEET_ARCHIVE' : case 'CONTROL_ARCHIVE' : + case 'SURVEY_ARCHIVE' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_ARCHIVE'; $actioncomm->label = $langs->transnoentities('ObjectArchivedTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->note_private .= $langs->trans('Status') . ' : ' . $langs->trans('Archived') . '
'; @@ -308,8 +342,8 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf $actioncomm->create($user); break; - case 'CONTROL_SAVEANSWER' : - $actioncomm->code = 'AC_' . strtoupper($object->element) . 'SAVEANSWER'; + case 'OBJECT_SAVEANSWER' : + $actioncomm->code = 'AC_' . strtoupper($object->element) . '_SAVEANSWER'; $actioncomm->label = $langs->transnoentities('AnswerSaveTrigger'); $actioncomm->create($user); break; @@ -321,12 +355,14 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf break; case 'CONTROL_SENTBYMAIL' : + case 'SURVEY_SENTBYMAIL' : $actioncomm->code = 'AC_' . strtoupper($object->element) . '_SENTBYMAIL'; $actioncomm->label = $langs->transnoentities('ObjectSentByMailTrigger', $langs->transnoentities(ucfirst($object->element)), $object->ref); $actioncomm->create($user); break; case 'CONTROLDOCUMENT_GENERATE' : + case 'SURVEYDOCUMENT_GENERATE' : $actioncomm->elementtype = $object->parent_type . '@digiquali'; $actioncomm->fk_element = $object->parent_id; $actioncomm->code = 'AC_' . strtoupper($object->element) . '_GENERATE'; diff --git a/css/digiquali.min.css b/css/digiquali.min.css index 5678cc82..33acca0a 100644 --- a/css/digiquali.min.css +++ b/css/digiquali.min.css @@ -1 +1 @@ -.control-audit *{box-sizing:border-box}.control-audit.multiselect{margin-top:-60px}.control-audit.multiselect>.wpeo-table{background:none !important}.control-audit.multiselect .table-cell{padding-top:0}.control-audit .wpeo-table.table-flex .table-row:not(.table-header):nth-of-type(odd){background:none}.control-audit .table-cell{margin-top:0 !important}.control-audit>.wpeo-table{border-bottom:1px solid rgba(0,0,0,.2)}.control-audit>.wpeo-table:nth-of-type(odd){background:rgba(38,60,92,.15)}.control-audit>.wpeo-table .cell-photo-check{text-align:right}@media(max-width: 600px){.control-audit>.wpeo-table .cell-photo-check{text-align:center}}.control-audit>.wpeo-table .question-photo-check{margin:0 4px;display:inline-block;position:relative}.control-audit>.wpeo-table .question-photo-check img{display:block;margin:0;width:200px;height:200px;background-size:cover}.control-audit>.wpeo-table .question-photo-check i{position:absolute;bottom:10px;right:10px;font-size:35px}.control-audit>.wpeo-table .question-photo-check.ko i{color:#e05353}.control-audit>.wpeo-table .question-photo-check.ok i{color:#47e58e}.control-audit>.wpeo-table .photo{margin:0 4px}.control-audit>.wpeo-table .photo.photo-ok{border:5px solid #47e58e}.control-audit>.wpeo-table .photo.photo-ko{border:5px solid #e05353}.control-audit>.wpeo-table .linked-medias{display:flex;gap:0 10px;flex-wrap:wrap}.control-audit>.wpeo-table .answer{display:inline-block;width:50px;height:50px;line-height:50px;font-size:18px;margin:0 4px;text-align:center;border-radius:50%;background:#fff;border:1px solid rgba(0,0,0,.4);transition:all .2s ease-out}@media(max-width: 600px){.control-audit>.wpeo-table .answer{width:60px;height:60px;line-height:60px;font-size:25px}}.control-audit>.wpeo-table .answer.square{border-radius:10%}.control-audit>.wpeo-table .answer:hover{cursor:pointer}.control-audit>.wpeo-table .answer.active{color:#fff !important}.control-audit>.wpeo-table .question-comment-container{margin-top:10px}.control-audit>.wpeo-table .question-comment-container .question-ref{font-size:13px;font-weight:700}.control-audit>.wpeo-table .question-comment-container .question-textarea{width:100%;background:#fff;border:1px solid rgba(0,0,0,.2);padding:1em 1.4em}.confirmquestions .answer{display:inline-block;width:30px;height:30px;line-height:30px;margin:0 4px;text-align:center;border-radius:50%;background:#fff;border:1px solid rgba(0,0,0,.4);-webkit-transition:all .2s ease-out;transition:all .2s ease-out}.confirmquestions .answer:hover{cursor:pointer}.confirmquestions .answer[value="1"]{color:#47e58e}.confirmquestions .answer[value="2"]{color:#e05353}.confirmquestions .answer[value="3"]{color:#e9ad4f}.confirmquestions .answer[value="4"]{color:rgba(0,0,0,.7);font-weight:700}.confirmquestions input[readonly]{border:0;width:100%;pointer-events:none}.confirmquestions input[readonly]:hover{cursor:default}.control-list-medias .question-section{display:block;margin-bottom:20px}.control-list-medias .question-section::after{display:block;content:"";clear:both}.control-list-medias .question-ref{font-weight:800;display:block;clear:both}.control-list-medias .media-container{display:block;float:left;margin-right:10px;margin-bottom:10px}.control-list-medias .media-container a{transition:all .2s ease-out}.control-list-medias .media-container a:hover{opacity:.8}.control-list-medias .media-container .photo{width:100%;height:100%;object-fit:cover}.question-table .linked-medias-list{display:flex;gap:10px;height:auto !important}@media(max-width: 500px){.question-table .linked-medias-list{flex-wrap:wrap}}@media(max-width: 500px){div.tabBar table.border.question-table tr.linked-medias,div.tabBar table.border.question-table tr.linked-medias .linked-medias-list{height:auto !important}}div.mainmenu.digiquali{background-image:none}div.mainmenu.digiquali::before{content:""}@media(max-width: 600px){div.tabsAction>span.butAction,div.tabsAction>span.butActionRefused,div.tabsAction>a.butAction,div.tabsAction>a.butActionDelete{padding:14px}}.dashboard-control{width:40px;height:40px;border-radius:6px;text-align:center;color:#fff;font-weight:900;font-size:14px;line-height:.9;padding:7px 2px;pointer-events:none}.progress-info{display:flex;align-items:center}.progress-info .progress-bar{width:100%;height:20px;background-color:#ddd;border-radius:5px}.progress-info .progress{width:50%;height:100%;border-radius:5px;transition:width .3s}.sheet-images-container .sheet-grid-images{display:flex;flex-wrap:wrap;gap:.8em}.sheet-images-container .sheet-grid-images img{object-fit:cover;border:3px solid #fff;transition:all .2s ease-out}.sheet-images-container .sheet-grid-images img:hover{cursor:pointer;opacity:.6}.preview-photo{z-index:2100 !important}.dropdown-toggle::after{display:none}.favorite-photo{border:5px solid #0d8aff} \ No newline at end of file +.question-answer-container *{box-sizing:border-box}.question-answer-container.multiselect{margin-top:-60px}.question-answer-container.multiselect>.wpeo-table{background:none !important}.question-answer-container.multiselect .table-cell{padding-top:0}.question-answer-container .wpeo-table.table-flex .table-row:not(.table-header):nth-of-type(odd){background:none}.question-answer-container .table-cell{margin-top:0 !important}.question-answer-container>.wpeo-table{border-bottom:1px solid rgba(0,0,0,.2)}.question-answer-container>.wpeo-table:nth-of-type(odd){background:rgba(38,60,92,.15)}.question-answer-container>.wpeo-table .cell-photo-check{text-align:right}@media(max-width: 600px){.question-answer-container>.wpeo-table .cell-photo-check{text-align:center}}.question-answer-container>.wpeo-table .question-photo-check{margin:0 4px;display:inline-block;position:relative}.question-answer-container>.wpeo-table .question-photo-check img{display:block;margin:0;width:200px;height:200px;background-size:cover}.question-answer-container>.wpeo-table .question-photo-check i{position:absolute;bottom:10px;right:10px;font-size:35px}.question-answer-container>.wpeo-table .question-photo-check.ko i{color:#e05353}.question-answer-container>.wpeo-table .question-photo-check.ok i{color:#47e58e}.question-answer-container>.wpeo-table .photo{margin:0 4px}.question-answer-container>.wpeo-table .photo.photo-ok{border:5px solid #47e58e}.question-answer-container>.wpeo-table .photo.photo-ko{border:5px solid #e05353}.question-answer-container>.wpeo-table .linked-medias{display:flex;gap:0 10px;flex-wrap:wrap}.question-answer-container>.wpeo-table .answer{display:inline-block;width:50px;height:50px;line-height:50px;font-size:18px;margin:0 4px;text-align:center;border-radius:50%;background:#fff;border:1px solid rgba(0,0,0,.4);transition:all .2s ease-out}@media(max-width: 600px){.question-answer-container>.wpeo-table .answer{width:60px;height:60px;line-height:60px;font-size:25px}}.question-answer-container>.wpeo-table .answer.square{border-radius:10%}.question-answer-container>.wpeo-table .answer:hover{cursor:pointer}.question-answer-container>.wpeo-table .answer.active{color:#fff !important}.question-answer-container>.wpeo-table .question-comment-container{margin-top:10px}.question-answer-container>.wpeo-table .question-comment-container .question-ref{font-size:13px;font-weight:700}.question-answer-container>.wpeo-table .question-comment-container .question-textarea{width:100%;background:#fff;border:1px solid rgba(0,0,0,.2);padding:1em 1.4em}.confirmquestions .answer{display:inline-block;width:30px;height:30px;line-height:30px;margin:0 4px;text-align:center;border-radius:50%;background:#fff;border:1px solid rgba(0,0,0,.4);-webkit-transition:all .2s ease-out;transition:all .2s ease-out}.confirmquestions .answer:hover{cursor:pointer}.confirmquestions .answer[value="1"]{color:#47e58e}.confirmquestions .answer[value="2"]{color:#e05353}.confirmquestions .answer[value="3"]{color:#e9ad4f}.confirmquestions .answer[value="4"]{color:rgba(0,0,0,.7);font-weight:700}.confirmquestions input[readonly]{border:0;width:100%;pointer-events:none}.confirmquestions input[readonly]:hover{cursor:default}.element-list-medias .question-section{display:block;margin-bottom:20px}.element-list-medias .question-section::after{display:block;content:"";clear:both}.element-list-medias .question-ref{font-weight:800;display:block;clear:both}.element-list-medias .media-container{display:block;float:left;margin-right:10px;margin-bottom:10px}.element-list-medias .media-container a{transition:all .2s ease-out}.element-list-medias .media-container a:hover{opacity:.8}.element-list-medias .media-container .photo{width:100%;height:100%;object-fit:cover}.question-table .linked-medias-list{display:flex;gap:10px;height:auto !important}@media(max-width: 500px){.question-table .linked-medias-list{flex-wrap:wrap}}@media(max-width: 500px){div.tabBar table.border.question-table tr.linked-medias,div.tabBar table.border.question-table tr.linked-medias .linked-medias-list{height:auto !important}}div.mainmenu.digiquali{background-image:none}div.mainmenu.digiquali::before{content:""}@media(max-width: 600px){div.tabsAction>span.butAction,div.tabsAction>span.butActionRefused,div.tabsAction>a.butAction,div.tabsAction>a.butActionDelete{padding:14px}}.dashboard-control{width:40px;height:40px;border-radius:6px;text-align:center;color:#fff;font-weight:900;font-size:14px;line-height:.9;padding:7px 2px;pointer-events:none}.progress-info{display:flex;align-items:center}.progress-info .progress-bar{width:100%;height:20px;background-color:#ddd;border-radius:5px}.progress-info .progress{width:50%;height:100%;border-radius:5px;transition:width .3s}.sheet-images-container .sheet-grid-images{display:flex;flex-wrap:wrap;gap:.8em}.sheet-images-container .sheet-grid-images img{object-fit:cover;border:3px solid #fff;transition:all .2s ease-out}.sheet-images-container .sheet-grid-images img:hover{cursor:pointer;opacity:.6}.preview-photo{z-index:2100 !important}.dropdown-toggle::after{display:none}.favorite-photo{border:5px solid #0d8aff} \ No newline at end of file diff --git a/css/scss/page/_control.scss b/css/scss/page/_control.scss index d9480b82..60143cfa 100644 --- a/css/scss/page/_control.scss +++ b/css/scss/page/_control.scss @@ -1,4 +1,4 @@ -.control-audit { +.question-answer-container { * { box-sizing: border-box; } diff --git a/css/scss/page/_control-medias.scss b/css/scss/page/_element-medias.scss similarity index 95% rename from css/scss/page/_control-medias.scss rename to css/scss/page/_element-medias.scss index c4bf4b27..9d3883c6 100644 --- a/css/scss/page/_control-medias.scss +++ b/css/scss/page/_element-medias.scss @@ -1,4 +1,4 @@ -.control-list-medias { +.element-list-medias { .question-section { display: block; margin-bottom: 20px; diff --git a/css/scss/page/_page.scss b/css/scss/page/_page.scss index d9a3aa1d..d3c89ef6 100644 --- a/css/scss/page/_page.scss +++ b/css/scss/page/_page.scss @@ -1,3 +1,3 @@ @import "control"; -@import "control-medias"; +@import "element-medias"; @import "question-add"; diff --git a/documents/doctemplates/surveydocument/index.php b/documents/doctemplates/surveydocument/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/documents/doctemplates/surveydocument/index.php @@ -0,0 +1,2 @@ +Commentaire non enregistré

'),$(this).addClass("show-comment-unsaved-message")),window.digiquali.control.updateButtonsStatus()},window.digiquali.control.updateButtonsStatus=function(){$("#saveButton").removeClass("butActionRefused"),$("#saveButton").addClass("butAction"),$("#saveButton").css("background","#0d8aff"),$(".fa-circle").css("display","inline"),$("#saveButton").attr("onclick",'$("#saveControl").submit()'),$(".validateButton").removeClass("butAction"),$("#dialog-confirm-actionButtonValidate").removeAttr("id"),$(".validateButton").addClass("butActionRefused")},window.digiquali.control.getAnswerCounter=function(t){let o=0;jQuery("#tablelines").children().each(function(){0<$(this).find(".answer.active").length&&(o+=1)}),document.cookie="answerCounter="+o},window.digiquali.control.showSelectObjectLinked=function(){var t=$(this).val(),o=window.saturne.toolbox.getToken(),e=window.saturne.toolbox.getQuerySeparator(document.URL),e=document.URL+e+"fk_sheet="+t+"&token="+o;window.saturne.loader.display($(".linked-objects")),$.ajax({url:e,type:"POST",processData:!1,contentType:!1,success:function(t){$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.toggleControlInfo=function(t){$(this).hasClass("fa-minus-square")?($(this).removeClass("fa-minus-square").addClass("fa-caret-square-down"),$(this).closest(".fiche").find(".fichecenter.controlInfo").addClass("hidden")):($(this).removeClass("fa-caret-square-down").addClass("fa-minus-square"),$(this).closest(".fiche").find(".fichecenter.controlInfo").removeClass("hidden"))},window.digiquali.control.copyToClipboard=function(t){var o=$(".copy-to-clipboard").attr("value");navigator.clipboard.writeText(o).then(()=>{$(".clipboard-copy").animate({backgroundColor:"#59ed9c"},200,()=>{$(".clipboard-copy").attr("class","fas fa-check clipboard-copy"),$(this).tooltip({items:".clipboard-copy",content:$("#copyToClipboardTooltip").val()}),$(this).tooltip("open"),$(".clipboard-copy").attr("style","")})})},window.digiquali.control.refreshLotSelector=function(t){var o=document.getElementById("add_control_equipment"),o=new FormData(o),e=window.saturne.toolbox.getToken(),o=o.get("productId"),e=document.URL+"&token="+e;e+="&fk_product="+o,window.saturne.loader.display($(".product-lot")),$.ajax({url:e,type:"POST",processData:!1,contentType:!1,success:function(t){$(".product-lot").replaceWith($(t).find(".product-lot"))},error:function(){}})},window.digiquali.control.switchPublicControlView=function(t){var o=$(this).find(".public-control-view").val(),e=window.saturne.toolbox.getToken();let i=document.URL+"&token="+e;i+=0==o?"&show_control_list=1":"&show_last_control=1",window.saturne.loader.display($(".signature-container")),$.ajax({url:i,type:"POST",processData:!1,contentType:!1,success:function(t){$("#publicControlHistory").replaceWith($(t).find("#publicControlHistory"))},error:function(){}})},window.digiquali.control.showOnlyQuestionsWithNoAnswer=function(){var t=window.saturne.toolbox.getQuerySeparator(document.URL),o=window.saturne.toolbox.getToken();let e;e=$(this).hasClass("fa-toggle-off")?1:0,window.saturne.loader.display($(this)),$.ajax({url:document.URL+t+"action=show_only_questions_with_no_answer&token="+o,type:"POST",processData:!1,data:JSON.stringify({showOnlyQuestionsWithNoAnswer:e}),contentType:!1,success:function(t){$(".questionLines").replaceWith($(t).find(".questionLines"))},error:function(){}})},window.digiquali.control.saveAnswer=function(t,o,e){var i=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".table-id-"+t)),$.ajax({url:document.URL+n+"action=save&token="+i,type:"POST",data:JSON.stringify({autoSave:!0,questionId:t,answer:o,comment:e}),processData:!1,contentType:!1,success:function(t){$(".fiche").replaceWith($(t).find(".fiche"))},error:function(){}})},window.digiquali.control.getSheetCategoryID=function(){let o=$(this).attr("value");var t=window.saturne.toolbox.getToken(),e=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-images-container")),$.ajax({url:document.URL+e+"sheetCategoryID="+o+"&token="+t,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-images-container").replaceWith($(t).find(".sheet-images-container")),$(".photo-sheet-category[value="+o+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-category[value="+o+"]").addClass("photo-sheet-category-active"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.getSheetSubCategoryID=function(){let o=$(".photo-sheet-category-active").attr("value"),e=$(this).attr("value");var t=window.saturne.toolbox.getToken(),i=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-images-container")),$.ajax({url:document.URL+i+"sheetCategoryID="+o+"&sheetSubCategoryID="+e+"&token="+t,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-images-container").replaceWith($(t).find(".sheet-images-container")),$(".photo-sheet-category[value="+o+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-category[value="+o+"]").addClass("photo-sheet-category-active"),$(".photo-sheet-sub-category[value="+e+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-sub-category[value="+e+"]").addClass("photo-sheet-sub-category-active"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.getSheetID=function(){let o=$(this).attr("data-object-id");var t=$(".photo-sheet-category-active").attr("value"),e=$(".photo-sheet-sub-category-active").attr("value"),i=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-elements")),window.saturne.loader.display($(".linked-objects")),$.ajax({url:document.URL+n+"fk_sheet="+o+"&sheetCategoryID="+t+"&sheetSubCategoryID="+e+"&token="+i,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-elements").replaceWith($(t).find(".sheet-elements")),$(".photo-sheet[data-object-id="+o+"]").css("border","3px solid #0d8aff"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.question={},window.digiquali.question.init=function(){window.digiquali.question.event()},window.digiquali.question.event=function(){$(document).on("click",".clicked-photo-preview",window.digiquali.question.previewPhoto),$(document).on("click",".ui-dialog-titlebar-close",window.digiquali.question.closePreviewPhoto),$(document).on("click","#show_photo",window.digiquali.question.showPhoto),$(document).on("click",".answer-picto .item, .wpeo-table .item",window.digiquali.question.selectAnswerPicto)},window.digiquali.question.previewPhoto=function(t){$(this).hasClass("photo-ok")?$("#dialogforpopup").attr("style","border: 10px solid #47e58e"):$(this).hasClass("photo-ko")&&$("#dialogforpopup").attr("style","border: 10px solid #e05353")},window.digiquali.question.closePreviewPhoto=function(t){$("#dialogforpopup").attr("style","border:")},window.digiquali.question.showPhoto=function(){var t=$(this).closest(".question-table").find(".linked-medias");t.hasClass("hidden")?(t.attr("style",""),t.removeClass("hidden")):(t.attr("style","display:none"),t.addClass("hidden"))},window.digiquali.question.selectAnswerPicto=function(t){var o=$(this).closest(".wpeo-dropdown");$(this).closest(".content").removeClass("active"),o.find(".dropdown-toggle span").hide(),o.find(".dropdown-toggle.button-picto").html($(this).closest(".wpeo-tooltip-event").html()),o.find(".input-hidden-picto").val($(this).data("label"))},window.digiquali.sheet={},window.digiquali.sheet.init=function(){window.digiquali.sheet.event()},window.digiquali.sheet.event=function(){}; \ No newline at end of file +window.digiquali||(window.digiquali={},window.digiquali.scriptsLoaded=!1),window.digiquali.scriptsLoaded||(window.digiquali.init=function(){window.digiquali.load_list_script()},window.digiquali.load_list_script=function(){if(!window.digiquali.scriptsLoaded){var t=void 0,e=void 0;for(t in window.digiquali)for(e in window.digiquali[t].init&&window.digiquali[t].init(),window.digiquali[t])window.digiquali[t]&&window.digiquali[t][e]&&window.digiquali[t][e].init&&window.digiquali[t][e].init();window.digiquali.scriptsLoaded=!0}},window.digiquali.refresh=function(){var t=void 0,e=void 0;for(t in window.digiquali)for(e in window.digiquali[t].refresh&&window.digiquali[t].refresh(),window.digiquali[t])window.digiquali[t]&&window.digiquali[t][e]&&window.digiquali[t][e].refresh&&window.digiquali[t][e].refresh()},$(document).ready(window.digiquali.init)),window.digiquali.control={},window.digiquali.control.init=function(){window.digiquali.control.event()},window.digiquali.control.event=function(){$(document).on("click",".validateButton",window.digiquali.control.getAnswerCounter),$(document).on("change","#fk_sheet",window.digiquali.control.showSelectObjectLinked),$(document).on("click",".clipboard-copy",window.digiquali.control.copyToClipboard),$(document).on("change","#productId",window.digiquali.control.refreshLotSelector),$(document).on("click",".switch-public-control-view",window.digiquali.control.switchPublicControlView),$(document).on("click",".show-only-questions-with-no-answer",window.digiquali.control.showOnlyQuestionsWithNoAnswer),$(document).on("click",".photo-sheet-category",window.digiquali.control.getSheetCategoryID),$(document).on("click",".photo-sheet-sub-category",window.digiquali.control.getSheetSubCategoryID),$(document).on("click",".photo-sheet",window.digiquali.control.getSheetID)},window.digiquali.control.getAnswerCounter=function(t){let e=0;jQuery("#tablelines").children().each(function(){0<$(this).find(".answer.active").length&&(e+=1)}),document.cookie="answerCounter="+e},window.digiquali.control.showSelectObjectLinked=function(){var t=$(this).val(),e=window.saturne.toolbox.getToken(),i=window.saturne.toolbox.getQuerySeparator(document.URL),i=document.URL+i+"fk_sheet="+t+"&token="+e;window.saturne.loader.display($(".linked-objects")),$.ajax({url:i,type:"POST",processData:!1,contentType:!1,success:function(t){$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.copyToClipboard=function(t){var e=$(".copy-to-clipboard").attr("value");navigator.clipboard.writeText(e).then(()=>{$(".clipboard-copy").animate({backgroundColor:"#59ed9c"},200,()=>{$(".clipboard-copy").attr("class","fas fa-check clipboard-copy"),$(this).tooltip({items:".clipboard-copy",content:$("#copyToClipboardTooltip").val()}),$(this).tooltip("open"),$(".clipboard-copy").attr("style","")})})},window.digiquali.control.refreshLotSelector=function(t){var e=document.getElementById("add_control_equipment"),e=new FormData(e),i=window.saturne.toolbox.getToken(),e=e.get("productId"),i=document.URL+"&token="+i;i+="&fk_product="+e,window.saturne.loader.display($(".product-lot")),$.ajax({url:i,type:"POST",processData:!1,contentType:!1,success:function(t){$(".product-lot").replaceWith($(t).find(".product-lot"))},error:function(){}})},window.digiquali.control.switchPublicControlView=function(t){var e=$(this).find(".public-control-view").val(),i=window.saturne.toolbox.getToken();let o=document.URL+"&token="+i;o+=0==e?"&show_control_list=1":"&show_last_control=1",window.saturne.loader.display($(".signature-container")),$.ajax({url:o,type:"POST",processData:!1,contentType:!1,success:function(t){$("#publicControlHistory").replaceWith($(t).find("#publicControlHistory"))},error:function(){}})},window.digiquali.control.showOnlyQuestionsWithNoAnswer=function(){var t=window.saturne.toolbox.getQuerySeparator(document.URL),e=window.saturne.toolbox.getToken();let i;i=$(this).hasClass("fa-toggle-off")?1:0,window.saturne.loader.display($(this)),$.ajax({url:document.URL+t+"action=show_only_questions_with_no_answer&token="+e,type:"POST",processData:!1,data:JSON.stringify({showOnlyQuestionsWithNoAnswer:i}),contentType:!1,success:function(t){$(".progress-info").replaceWith($(t).find(".progress-info")),$(".question-answer-container").replaceWith($(t).find(".question-answer-container"))},error:function(){}})},window.digiquali.control.getSheetCategoryID=function(){let e=$(this).attr("value");var t=window.saturne.toolbox.getToken(),i=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-images-container")),$.ajax({url:document.URL+i+"sheetCategoryID="+e+"&token="+t,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-images-container").replaceWith($(t).find(".sheet-images-container")),$(".photo-sheet-category[value="+e+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-category[value="+e+"]").addClass("photo-sheet-category-active"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.getSheetSubCategoryID=function(){let e=$(".photo-sheet-category-active").attr("value"),i=$(this).attr("value");var t=window.saturne.toolbox.getToken(),o=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-images-container")),$.ajax({url:document.URL+o+"sheetCategoryID="+e+"&sheetSubCategoryID="+i+"&token="+t,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-images-container").replaceWith($(t).find(".sheet-images-container")),$(".photo-sheet-category[value="+e+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-category[value="+e+"]").addClass("photo-sheet-category-active"),$(".photo-sheet-sub-category[value="+i+"]").css("border","3px solid #0d8aff"),$(".photo-sheet-sub-category[value="+i+"]").addClass("photo-sheet-sub-category-active"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.control.getSheetID=function(){let e=$(this).attr("data-object-id");var t=$(".photo-sheet-category-active").attr("value"),i=$(".photo-sheet-sub-category-active").attr("value"),o=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".sheet-elements")),window.saturne.loader.display($(".linked-objects")),$.ajax({url:document.URL+n+"fk_sheet="+e+"&sheetCategoryID="+t+"&sheetSubCategoryID="+i+"&token="+o,type:"POST",processData:!1,contentType:!1,success:function(t){$(".sheet-elements").replaceWith($(t).find(".sheet-elements")),$(".photo-sheet[data-object-id="+e+"]").css("border","3px solid #0d8aff"),$(".linked-objects").replaceWith($(t).find(".linked-objects"))},error:function(){}})},window.digiquali.object={},window.digiquali.object.init=function(){window.digiquali.object.event()},window.digiquali.object.event=function(){$(document).on("change",".object-table.linked-objects select",window.digiquali.object.disableOtherSelectors),$(document).on("click",".answer:not(.disable)",window.digiquali.object.selectAnswer),$(document).on("input",".input-answer:not(.disable)",window.digiquali.object.selectAnswer),$(document).on("keyup",".question-comment",window.digiquali.object.showCommentUnsaved)},window.digiquali.object.disableOtherSelectors=function(){var t=document.getElementById("createObjectForm"),t=new FormData(t),e=$(this).attr("id");0<=t.get(e)?$(".object-table.linked-objects").find("select").not("#"+e).attr("disabled",1):$(".object-table.linked-objects").find("select").not("#"+e).removeAttr("disabled")},window.digiquali.object.selectAnswer=function(){var e=$(this).closest(".select-answer.answer-cell"),t=e.attr("data-questionId"),i=$(this).closest(".table-id-"+t).attr("data-publicInterface"),o=$(this).closest(".table-id-"+t).attr("data-autoSave");let n="";var a=$(this).hasClass("answer")?$(this).attr("value"):$(this).val(),s=$(this).closest(".table-id-"+t).find("#comment"+t).val();if($(this).closest(".table-cell").hasClass("select-answer")){if($(this).hasClass("multiple-answers")){$(this).closest("span").toggleClass("active");let t=[];e.find(".multiple-answers.active").each(function(){t.push($(this).attr("value"))}),n=t}else $(this).closest(".table-cell").find(".answer.active").css("background-color","#fff"),$(this).closest(".table-cell").find("span").removeClass("active"),$(this).closest("span").addClass("active"),n=a;$(this).hasClass("active")?(e=$(this).closest(".answer-cell").find(".answer-color-"+$(this).attr("value")).val(),$(this).attr("style",$(this).attr("style")+" background:"+e+";")):$(this).attr("style",$(this).attr("style")+" background:#fff;"),$(this).closest(".answer-cell").find(".question-answer").val(n)}i||1!=o||$(this).hasClass("multiple-answers")?window.digiquali.object.updateButtonsStatus():window.digiquali.object.saveAnswer(t,n,s)},window.digiquali.object.showCommentUnsaved=function(){$(this).hasClass("show-comment-unsaved-message")||($(this).after('

Commentaire non enregistré

'),$(this).addClass("show-comment-unsaved-message")),window.digiquali.object.updateButtonsStatus()},window.digiquali.object.updateButtonsStatus=function(){$("#saveButton").removeClass("butActionRefused"),$("#saveButton").addClass("butAction"),$("#saveButton").css("background","#0d8aff"),$(".fa-circle").css("display","inline"),$("#saveButton").attr("onclick",'$("#saveObject").submit()'),$(".validateButton").removeClass("butAction"),$("#dialog-confirm-actionButtonValidate").removeAttr("id"),$(".validateButton").addClass("butActionRefused")},window.digiquali.object.saveAnswer=function(t,e,i){var o=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".table-id-"+t)),$.ajax({url:document.URL+n+"action=save&token="+o,type:"POST",data:JSON.stringify({autoSave:!0,questionId:t,answer:e,comment:i}),processData:!1,contentType:!1,success:function(t){$(".fiche").replaceWith($(t).find(".fiche"))},error:function(){}})},window.digiquali.question={},window.digiquali.question.init=function(){window.digiquali.question.event()},window.digiquali.question.event=function(){$(document).on("click",".clicked-photo-preview",window.digiquali.question.previewPhoto),$(document).on("click",".ui-dialog-titlebar-close",window.digiquali.question.closePreviewPhoto),$(document).on("click","#show_photo",window.digiquali.question.showPhoto),$(document).on("click",".answer-picto .item, .wpeo-table .item",window.digiquali.question.selectAnswerPicto)},window.digiquali.question.previewPhoto=function(t){$(this).hasClass("photo-ok")?$("#dialogforpopup").attr("style","border: 10px solid #47e58e"):$(this).hasClass("photo-ko")&&$("#dialogforpopup").attr("style","border: 10px solid #e05353")},window.digiquali.question.closePreviewPhoto=function(t){$("#dialogforpopup").attr("style","border:")},window.digiquali.question.showPhoto=function(){var t=$(this).closest(".question-table").find(".linked-medias");t.hasClass("hidden")?(t.attr("style",""),t.removeClass("hidden")):(t.attr("style","display:none"),t.addClass("hidden"))},window.digiquali.question.selectAnswerPicto=function(t){var e=$(this).closest(".wpeo-dropdown");$(this).closest(".content").removeClass("active"),e.find(".dropdown-toggle span").hide(),e.find(".dropdown-toggle.button-picto").html($(this).closest(".wpeo-tooltip-event").html()),e.find(".input-hidden-picto").val($(this).data("label"))},window.digiquali.sheet={},window.digiquali.sheet.init=function(){window.digiquali.sheet.event()},window.digiquali.sheet.event=function(){}; \ No newline at end of file diff --git a/js/modules/control.js b/js/modules/control.js index dc392190..9d23e407 100644 --- a/js/modules/control.js +++ b/js/modules/control.js @@ -28,13 +28,8 @@ window.digiquali.control.init = function() { * @return {void} */ window.digiquali.control.event = function() { - $( document ).on( 'click', '.answer:not(.disable)', window.digiquali.control.selectAnswer ); - $( document ).on( 'input', '.input-answer:not(.disable)', window.digiquali.control.selectAnswer ); - $( document ).on( 'change', '.control-table.linked-objects select', window.digiquali.control.disableOtherSelectors ); - $( document ).on( 'keyup', '.question-comment', window.digiquali.control.showCommentUnsaved ); $( document ).on( 'click', '.validateButton', window.digiquali.control.getAnswerCounter); $( document ).on( 'change', '#fk_sheet', window.digiquali.control.showSelectObjectLinked); - $( document ).on( 'click', '.toggleControlInfo', window.digiquali.control.toggleControlInfo ); $( document ).on( 'click', '.clipboard-copy', window.digiquali.control.copyToClipboard ); $( document ).on( 'change', '#productId', window.digiquali.control.refreshLotSelector ); $( document ).on( 'click', '.switch-public-control-view', window.digiquali.control.switchPublicControlView ); @@ -44,116 +39,6 @@ window.digiquali.control.event = function() { $(document).on('click', '.photo-sheet', window.digiquali.control.getSheetID); }; -/** - * Select an answer for a control question. - * - * @since 1.0.0 - * @version 1.0.0 - * - * @param {MouseEvent} event Les attributs lors du clic. - * @return {void} - */ -window.digiquali.control.selectAnswer = function ( event ) { - let questionElement = $(this).closest('.select-answer.answer-cell'); - let questionId = questionElement.attr('data-questionId'); - let publicInterface = $(this).closest('.table-id-' + questionId).attr('data-publicInterface'); - let autoSave = $(this).closest('.table-id-' + questionId).attr('data-autoSave'); - let answer = ''; - let answerValue = $(this).hasClass('answer') ? $(this).attr('value') : $(this).val(); - let comment = $(this).closest('.table-id-' + questionId).find('#comment' + questionId).val(); - if ($(this).closest('.table-cell').hasClass('select-answer')) { - if ($(this).hasClass('multiple-answers')) { - $(this).closest('span').toggleClass( 'active' ); - let selectedValues = [] - questionElement.find('.multiple-answers.active').each(function() { - selectedValues.push($(this).attr('value')) - }) - answer = selectedValues - } else { - $(this).closest('.table-cell').find('.answer.active').css( 'background-color', '#fff' ); - - $(this).closest('.table-cell').find('span').removeClass( 'active' ); - $(this).closest('span').addClass( 'active' ); - answer = answerValue - } - if ($(this).hasClass('active')) { - let answerColor = $(this).closest('.answer-cell').find('.answer-color-' + $(this).attr('value')).val() - $(this).attr('style', $(this).attr('style') + ' background:'+answerColor+';') - } else { - $(this).attr('style', $(this).attr('style') + ' background:#fff;') - } - $(this).closest('.answer-cell').find('.question-answer').val(answer) - } - - if (!publicInterface && autoSave == 1 && !$(this).hasClass('multiple-answers')) { - window.digiquali.control.saveAnswer(questionId, answer, comment); - } else { - window.digiquali.control.updateButtonsStatus() - } -}; - -/** - * Disable selectors on control object selection. - * - * @since 1.0.0 - * @version 1.0.0 - * - * @param {MouseEvent} event Les attributs lors du clic. - * @return {void} - */ -window.digiquali.control.disableOtherSelectors = function ( event ) { - var controlForm = document.getElementById('createControlForm'); - var formData = new FormData(controlForm); - - let selectorId = $(this).attr('id'); - let selectorData = formData.get(selectorId) - - if (selectorData >= 0) { - $('.control-table.linked-objects').find('select').not('#' + selectorId).attr('disabled', 1); - } else { - $('.control-table.linked-objects').find('select').not('#' + selectorId).removeAttr('disabled'); - } -}; - - -/** - * Show a comment for a control question if focus out. - * - * @since 1.1.0 - * @version 1.1.0 - * - * @param {MouseEvent} event Les attributs lors du clic. - * @return {void} - */ -window.digiquali.control.showCommentUnsaved = function ( event ) { - if (!$(this).hasClass('show-comment-unsaved-message')) { - $(this).after('

Commentaire non enregistré

'); - $(this).addClass('show-comment-unsaved-message'); - } - window.digiquali.control.updateButtonsStatus() -}; - -/** - * Change buttons status - * - * @since 1.1.0 - * @version 1.1.0 - * - * @param {MouseEvent} event Les attributs lors du clic. - * @return {void} - */ -window.digiquali.control.updateButtonsStatus = function ( ) { - $('#saveButton').removeClass('butActionRefused') - $('#saveButton').addClass('butAction') - $('#saveButton').css('background', '#0d8aff') - $('.fa-circle').css('display', 'inline') - $('#saveButton').attr('onclick','$("#saveControl").submit()'); - - $('.validateButton').removeClass('butAction') - $('#dialog-confirm-actionButtonValidate').removeAttr('id'); - $('.validateButton').addClass('butActionRefused') -}; - /** * Get answered questions counter * @@ -202,25 +87,6 @@ window.digiquali.control.showSelectObjectLinked = function() { }); }; -/** - * Show control info if toggle control info is on. - * - * @since 1.4.0 - * @version 1.4.0 - * - * @param {MouseEvent} event Les attributs lors du clic. - * @return {void} - */ -window.digiquali.control.toggleControlInfo = function ( event ) { - if ($(this).hasClass('fa-minus-square')) { - $(this).removeClass('fa-minus-square').addClass('fa-caret-square-down') - $(this).closest('.fiche').find('.fichecenter.controlInfo').addClass('hidden') - } else { - $(this).removeClass('fa-caret-square-down').addClass('fa-minus-square') - $(this).closest('.fiche').find('.fichecenter.controlInfo').removeClass('hidden') - } -} - /** * Copy current link to clipboard * @@ -346,41 +212,8 @@ window.digiquali.control.showOnlyQuestionsWithNoAnswer = function() { }), contentType: false, success: function(resp) { - $('.questionLines').replaceWith($(resp).find('.questionLines')) - }, - error: function() {} - }); -}; - -/** - * Save answer after click event - * - * @since 1.9.0 - * @version 1.9.0 - * - * @param {int} questionId Question ID - * @param {string} answer Answer value - * @param {string} comment Comment value - * @return {void} - */ -window.digiquali.control.saveAnswer = function(questionId, answer, comment) { - let token = window.saturne.toolbox.getToken(); - let querySeparator = window.saturne.toolbox.getQuerySeparator(document.URL); - window.saturne.loader.display($('.table-id-' + questionId)); - - $.ajax({ - url: document.URL + querySeparator + 'action=save&token=' + token, - type: "POST", - data: JSON.stringify({ - autoSave: true, - questionId: questionId, - answer: answer, - comment: comment - }), - processData: false, - contentType: false, - success: function(resp) { - $('.fiche').replaceWith($(resp).find('.fiche')); + $('.progress-info').replaceWith($(resp).find('.progress-info')); + $('.question-answer-container').replaceWith($(resp).find('.question-answer-container')); }, error: function() {} }); diff --git a/js/modules/object.js b/js/modules/object.js new file mode 100644 index 00000000..433acd99 --- /dev/null +++ b/js/modules/object.js @@ -0,0 +1,205 @@ +/* Copyright (C) 2024 EVARISK + * + * 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 . + * + * Library javascript to enable Browser notifications + */ + +/** + * \file js/modules/object.js + * \ingroup digiquali + * \brief JavaScript object file for module DigiQuali + */ + +/** + * Init object JS + * + * @memberof DigiQuali_Object + * + * @since 1.11.0 + * @version 1.11.0 + * + * @type {Object} + */ +window.digiquali.object = {}; + +/** + * Object init + * + * @memberof DigiQuali_Object + * + * @since 1.11.0 + * @version 1.11.0 + * + * @returns {void} + */ +window.digiquali.object.init = function() { + window.digiquali.object.event(); +}; + +/** + * Object event + * + * @memberof DigiQuali_Object + * + * @since 1.11.0 + * @version 1.11.0 + * + * @returns {void} + */ +window.digiquali.object.event = function() { + $(document).on( 'change', '.object-table.linked-objects select', window.digiquali.object.disableOtherSelectors); + $(document).on( 'click', '.answer:not(.disable)', window.digiquali.object.selectAnswer); + $(document).on( 'input', '.input-answer:not(.disable)', window.digiquali.object.selectAnswer); + $(document).on( 'keyup', '.question-comment', window.digiquali.object.showCommentUnsaved); +}; + +/** + * Disable selectors on object selection + * + * @since 1.8.0 + * @version 1.11.0 + * + * @return {void} + */ +window.digiquali.object.disableOtherSelectors = function() { + var objectForm = document.getElementById('createObjectForm'); + var formData = new FormData(objectForm); + + let selectorId = $(this).attr('id'); + let selectorData = formData.get(selectorId); + + if (selectorData >= 0) { + $('.object-table.linked-objects').find('select').not('#' + selectorId).attr('disabled', 1); + } else { + $('.object-table.linked-objects').find('select').not('#' + selectorId).removeAttr('disabled'); + } +}; + +/** + * Select an answer on question + * + * @since 1.0.0 + * @version 1.11.0 + * + * @return {void} + */ +window.digiquali.object.selectAnswer = function() { + let questionElement = $(this).closest('.select-answer.answer-cell'); + let questionId = questionElement.attr('data-questionId'); + let publicInterface = $(this).closest('.table-id-' + questionId).attr('data-publicInterface'); + let autoSave = $(this).closest('.table-id-' + questionId).attr('data-autoSave'); + let answer = ''; + let answerValue = $(this).hasClass('answer') ? $(this).attr('value') : $(this).val(); + let comment = $(this).closest('.table-id-' + questionId).find('#comment' + questionId).val(); + if ($(this).closest('.table-cell').hasClass('select-answer')) { + if ($(this).hasClass('multiple-answers')) { + $(this).closest('span').toggleClass('active'); + let selectedValues = []; + questionElement.find('.multiple-answers.active').each(function() { + selectedValues.push($(this).attr('value')); + }); + answer = selectedValues; + } else { + $(this).closest('.table-cell').find('.answer.active').css( 'background-color', '#fff' ); + + $(this).closest('.table-cell').find('span').removeClass('active'); + $(this).closest('span').addClass('active'); + answer = answerValue; + } + if ($(this).hasClass('active')) { + let answerColor = $(this).closest('.answer-cell').find('.answer-color-' + $(this).attr('value')).val(); + $(this).attr('style', $(this).attr('style') + ' background:' + answerColor + ';'); + } else { + $(this).attr('style', $(this).attr('style') + ' background:#fff;'); + } + $(this).closest('.answer-cell').find('.question-answer').val(answer); + } + + if (!publicInterface && autoSave == 1 && !$(this).hasClass('multiple-answers')) { + window.digiquali.object.saveAnswer(questionId, answer, comment); + } else { + window.digiquali.object.updateButtonsStatus(); + } +}; + +/** + * Show a comment for a question answer if focus out + * + * @since 1.1.0 + * @version 1.11.0 + * + * @return {void} + */ +window.digiquali.object.showCommentUnsaved = function() { + if (!$(this).hasClass('show-comment-unsaved-message')) { + $(this).after('

Commentaire non enregistré

'); + $(this).addClass('show-comment-unsaved-message'); + } + window.digiquali.object.updateButtonsStatus(); +}; + +/** + * Change buttons status + * + * @since 1.1.0 + * @version 1.11.0 + * + * @return {void} + */ +window.digiquali.object.updateButtonsStatus = function() { + $('#saveButton').removeClass('butActionRefused'); + $('#saveButton').addClass('butAction'); + $('#saveButton').css('background', '#0d8aff'); + $('.fa-circle').css('display', 'inline'); + $('#saveButton').attr('onclick','$("#saveObject").submit()'); + + $('.validateButton').removeClass('butAction'); + $('#dialog-confirm-actionButtonValidate').removeAttr('id'); + $('.validateButton').addClass('butActionRefused'); +}; + +/** + * Save answer after click event + * + * @since 1.9.0 + * @version 1.11.0 + * + * @param {int} questionId Question ID + * @param {string} answer Answer value + * @param {string} comment Comment value + * @return {void} + */ +window.digiquali.object.saveAnswer = function(questionId, answer, comment) { + let token = window.saturne.toolbox.getToken(); + let querySeparator = window.saturne.toolbox.getQuerySeparator(document.URL); + window.saturne.loader.display($('.table-id-' + questionId)); + + $.ajax({ + url: document.URL + querySeparator + 'action=save&token=' + token, + type: 'POST', + data: JSON.stringify({ + autoSave: true, + questionId: questionId, + answer: answer, + comment: comment + }), + processData: false, + contentType: false, + success: function(resp) { + $('.fiche').replaceWith($(resp).find('.fiche')); + }, + error: function() {} + }); +}; diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 8b038c24..c2403bcb 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -41,6 +41,22 @@ MainSheetCategoriesDescription = Cette option permet la génération des catégo SheetMainCategory = Choix de la catégorie principale du modèle de contrôle SheetMainCategoryDescription = Cette option permet de choisir quelle catégorie sera utilisée lors du choix de catégorie pendant la création d'un contrôle +# Control - Contrôle +DisplayMediasSample = Afficher les médias d'exemple des questions +DisplayControlMediasSampleDescription = Afficher les médias d'exemple des questions dans le contrôle +ExtrafieldsControlManagement = Gestion des attributs supplémentaires des contrôles + +# Survey - Questionnaire +DisplaySurveyMediasSampleDescription = Afficher les médias d'exemple des questions dans le questionnaire +ExtrafieldsSurveyManagement = Gestion des attributs supplémentaires des questionnaires + +# Public interface - Interface publique +AnswerPublicInterface = Interface publique de réponse aux questions pour __OBJECT_ELEMENT_REF__ +AnswerPublicInterfaceTitle = Titre de l'interface publique de réponse aux questions +AnswerPublicInterfaceSubstitution = Traduction générique "TheObject" __REF__ + + + # # Questions # @@ -53,8 +69,8 @@ QuestionList = Liste des questions NewQuestion = Nouvelle question ModifyQuestion = Modifier une question LinkedQuestionsList = Liste des questions liées -addQuestionLink = Question ajoutée au modèle de contrôle : -removeQuestionLink = Question retirée de le modèle de contrôle : +addQuestionLink = Question ajoutée au modèle : +removeQuestionLink = Question retirée du modèle : LockQuestion = La question %s est passée au statut verrouillé LockQuestions = Toutes les questions sélectionnées sont passées au statut verrouillé ThereAre = Il y a @@ -72,7 +88,7 @@ AuthorizeAnswerPhotoTooltip = Autorise l'envoi de photos sur les réponses de NoteControl = Note publique EnterComment = Saisir les commentaires EnterCommentTooltip = Saisit le commentaire sur les réponses de question -ErrorQuestionUsedInSheet = La question %s ne peut pas être supprimée, elle est utilisée dans une ou plusieurs modèles de contrôle. +ErrorQuestionUsedInSheet = La question %s ne peut pas être supprimée, elle est utilisée dans un ou plusieurs modèles. ErrorNoQuestionSelected = Aucune question sélectionnée AddQuestionIntoCategory = Assigner cette catégorie à la question QuestionType = Type de question @@ -107,37 +123,37 @@ AveragePercentageQuestions = Moyenne des questions SheetAddQuestionTrigger = Question ajoutée # Data - Donnée -Sheet = Modèle de contrôle -Sheets = modèles de contrôle -TheSheet = le modèle de contrôle -SheetList = Liste des modèles de contrôle -NewSheet = Nouveau modèle de contrôle -ModifySheet = Modifier un modèle de contrôle -AddSheet = Ajouter un modèle de contrôle -AllQuestionsMustHaveLocked = Tous les questions doivent être verrouillées pour verrouiller le modèle de contrôle +Sheet = Modèle +Sheets = modèles +TheSheet = le modèle +SheetList = Liste des modèles +NewSheet = Nouveau modèle +ModifySheet = Modifier un modèle +AddSheet = Ajouter un modèle +AllQuestionsMustHaveLocked = Toutes les questions doivent être verrouillées pour verrouiller le modèle NoLotForThisProduct = Pas de lot défini pour ce produit ModalAddPhoto = Bibliothèque de médias -SheetManagement = Gestion des modèles de contrôle -ActionsOnSheet = Événements liés au modèle de contrôle -SheetsCategoriesArea = Espace des tags/catégories des modèles de contrôle -ExtrafieldsSheetManagement = Gestion des attributs supplémentaires des modèles de contrôle -SheetCategories = Catégories des modèles de contrôle -SheetSubCategories = Sous-catégories des modèles de contrôle -GenerateCategories = Génération des catégories de modèle de contrôle par défaut +SheetManagement = Gestion des modèles +ActionsOnSheet = Événements liés au modèle +SheetsCategoriesArea = Espace des tags/catégories des modèles +ExtrafieldsSheetManagement = Gestion des attributs supplémentaires des modèles +SheetCategories = Catégories des modèles +SheetSubCategories = Sous-catégories des modèles +GenerateCategories = Génération des catégories de modèle par défaut AlreadyGenerated = Déjà généré(e)s NotCreated = Pas encore créé(e)s -CategoriesGeneration = Cette option permet la génération des catégories par défaut des des modèles de contrôle. Les catégories créées seront :
- Qualité
- Hygiène
- Sécurité
- Environnement
- Sureté
- Réglementaire
- Bureau d'Etude
- Fournisseurs
- Commercial
- Production
- Méthodes
- Comptabilité
- Logistique
- Informatique
+CategoriesGeneration = Cette option permet la génération des catégories par défaut des modèles. Les catégories créées seront :
- Qualité
- Hygiène
- Sécurité
- Environnement
- Sureté
- Réglementaire
- Bureau d'Etude
- Fournisseurs
- Commercial
- Production
- Méthodes
- Comptabilité
- Logistique
- Informatique
UniqueLinkedElement = Liaison unique sur les éléments de Dolibarr -UniqueLinkedElementDescription = Lier un seul élément de Dolibarr à un modèle de contrôle -ErrorSheetUsedInControl = Le modèle de contrôle %s ne peut pas être supprimé, il est utilisé dans un ou plusieurs contrôles. +UniqueLinkedElementDescription = Lier un seul élément de Dolibarr à un modèle +ErrorSheetUsedInControl = Le modèle %s ne peut pas être supprimé, il est utilisé dans un ou plusieurs contrôles/questionnaires. ConfigElementLinked = Vous n'avez pas configuré d'éléments liés, veuillez cliquer ici pour vous rendre sur la page de configuration -ConfigSheet = Page de configuration des modèles de contrôle -AddSheetIntoCategory = Assigner cette catégorie au modèle de contrôle +ConfigSheet = Page de configuration des modèles +AddSheetIntoCategory = Assigner cette catégorie au modèle QuestionMandatorized = La question %s a été rendue obligatoire QuestionUnMandatorized = La question %s n'est plus obligatoire NoLinkedObjectSelected = Veuillez sélectionner un type d'objet à contrôler -ExportSheetData = Export des données du modèle de contrôle -ExportSheetDataDescription = Export des données du modèle de contrôle et des questions associées +ExportSheetData = Export des données du modèle +ExportSheetDataDescription = Export des données du modèle et des questions associées ExportWellDone = L'export a été réalisé avec succès ToImportDataGoToImportPage = Pour importer des données, veuillez vous rendre sur la page d'import : %s NbQuestions = Nombre de questions @@ -223,38 +239,37 @@ OrderLinked = Commande contrôlée ContractLinked = Contrat contrôlé TicketLinked = Ticket contrôlé LinkProduct = Activer le lien avec les produits -LinkProductDescription = Permet la liaison entre les produits et les modèles de contrôle +LinkProductDescription = Permet la liaison entre les produits et les modèles LinkProductlot = Activer le lien avec les numéros de lots/série -LinkProductlotDescription = Permet la liaison entre les numéros de lots/série et les modèles de contrôle +LinkProductlotDescription = Permet la liaison entre les numéros de lots/série et les modèles LinkUser = Activer le lien avec les utilisateurs -LinkUserDescription = Permet la liaison entre les utilisateurs et les modèles de contrôle +LinkUserDescription = Permet la liaison entre les utilisateurs et les modèles LinkThirdparty = Activer le lien avec les tiers -LinkThirdpartyDescription = Permet la liaison entre les tiers et les modèles de contrôle +LinkThirdpartyDescription = Permet la liaison entre les tiers et les modèles LinkContact = Activer le lien avec les contacts/adresses -LinkContactDescription = Permet la liaison entre les contacts/adresses et les modèles de contrôle +LinkContactDescription = Permet la liaison entre les contacts/adresses et les modèles LinkProject = Activer le lien avec les projets -LinkProjectDescription = Permet la liaison entre les projets et les modèles de contrôle +LinkProjectDescription = Permet la liaison entre les projets et les modèles LinkTask = Activer le lien avec les tâches -LinkTaskDescription = Permet la liaison entre les tâches et les modèles de contrôle +LinkTaskDescription = Permet la liaison entre les tâches et les modèles LinkInvoice = Activer le lien avec les factures -LinkInvoiceDescription = Permet la liaison entre les factures et les modèles de contrôle +LinkInvoiceDescription = Permet la liaison entre les factures et les modèles LinkOrder = Activer le lien avec les commandes -LinkOrderDescription = Permet la liaison entre les commandes et les modèles de contrôle +LinkOrderDescription = Permet la liaison entre les commandes et les modèles LinkContract = Activer le lien avec les contrats -LinkContractDescription = Permet la liaison entre les contrats et les modèles de contrôle +LinkContractDescription = Permet la liaison entre les contrats et les modèles LinkTicket = Activer le lien avec les tickets -LinkTicketDescription = Permet la liaison entre les tickets et les modèles de contrôle +LinkTicketDescription = Permet la liaison entre les tickets et les modèles LinkEntrepot = Activer le lien avec les entrepôts -LinkEntrepotDescription = Permet la liaison entre les entrepôts et les modèles de contrôle +LinkEntrepotDescription = Permet la liaison entre les entrepôts et les modèles LinkExpedition = Activer le lien avec les expéditions -LinkExpeditionDescription = Permet la liaison entre les expéditions et les modèles de contrôle +LinkExpeditionDescription = Permet la liaison entre les expéditions et les modèles LinkPropal = Activer le lien avec les propositions commerciales -LinkPropalDescription = Permet la liaison entre les propositions commerciales et les modèles de contrôle +LinkPropalDescription = Permet la liaison entre les propositions commerciales et les modèles NoLotDefined = Pas de numéro de lot/série trouvé SelectAProductFirst = Sélectionnez d'abord un produit ActionsOnControl = Événements liés au contrôle RegulatoryScore = Score réglementaire -ExtrafieldsControlManagement = Gestion des attributs supplémentaires des contrôles WithLastControl = Avec le dernier contrôle ControlMustBeLockedToSendEmail = Le contrôle doit être verrouillé pour pouvoir envoyer par e-mail SelectUser = Sélectionner un utilisateur @@ -281,14 +296,13 @@ ObjectLinked = Objet lié(s) NextControlDate = Prochaine date de contrôle NextControl = Prochain contrôle QuestionMustBeAnswered = Vous devez encore répondre à %s question(s) obligatoire(s) -NoControlAnswersPhoto = Pas de photos sur les réponses du contrôle ControlEquipment = Moyens de contrôle ControlEquipmentList = Liste des moyens de contrôle utilisés AddEquipmentLink = Moyen de contrôle ajouté : UnlinkEquipmentLink = Moyen de contrôle retiré : ErrorNoEquipmentSelected = Aucun moyen de contrôle sélectionné OptimalExpirationDate = DLUO/DDM -OptimalExpirationDateDescription = Date Limite d'Utilisation Optimale / Date de Durabilité Minimale +OptimalExpirationDateDescription = Date Limite d'Utilisation Optimale/Date de Durabilité Minimale LockControlOutdatedEquipment = Permet de verrouiller le contrôle même si la DLUO/DDM est dépassée LockControlOutdatedEquipmentDescription = Le contrôle pourra être verouillé si la DLUO/DDM d'un ou plusieurs moyens de contrôle est dépassée ControlListsByNextControl = Prochains contrôles à réaliser @@ -297,9 +311,8 @@ GoToEquipmentHours = Aller sur la page des moyens de ExpiredSince = Expiré depuis RemainingDays = Jours avant expiration NoEquipmentLinked = Aucun moyen de contrôle lié -NoControlAnswersPhoto = Pas de photos sur les réponses du contrôle NeedObjectToControl = Vous devez sélectionner un objet à contrôler -NeedFkSheet = Vous devez sélectionner un modèle de contrôle +NeedFkSheet = Vous devez sélectionner un modèle ControlReminder = Rappel d'événement de contrôle ControlReminderDescription = Activer le rappel d'événement de contrôle ControlReminderFrequency = Fréquence de rappel d'évènement de contrôle @@ -307,9 +320,7 @@ ControlReminderFrequencyDescription = Choisir la fréquence de rappel ControlReminderType = Type de rappel d'évènement de contrôle ControlReminderTypeDescription = Choisir le type de rappel d'évènement de contrôle
(par défaut : navigateur) controlled = contrôlé(e) -PublicSurvey = Interface publique de réponse au contrôle -PublicSurveyTitle = Titre de l'interface publique de réponse au contrôle -PublicSurveyTitleDescription = Définir le titre de l'interface publique de réponse au contrôle +PublicAnswer = Interface publique de réponse YourAnswersHaveBeenSaved = Vos réponses ont bien été sauvegardées DaysBeforeNextControl = Jours avant prochain contrôle SelectProductLots = Sélectionner un numéro de lot/série @@ -323,11 +334,12 @@ EnablePublicControlHistory = Afficher l'historique public de EnablePublicControlHistoryDescription = Permet l'affichage de l'historique de contrôle sur l'interface publique de contrôle ShowLastControlFirstOnPublicHistory = Afficher le dernier contrôle en priorité sur l'interface publique de contrôle ShowLastControlFirstOnPublicHistoryDescription = Force l'affichage du dernier contrôle à la place de la liste des contrôles de l'objet sur l'interface publique de contrôle -NonFinalVerdict = Le contrôle n'étant pas verrouillé, le verdict peut encore changer -GoToPublicSurveyPage = Voir l'interface publique de réponse +NonFinalVerdict = Le contrôle n'est pas verrouillé,
le verdict peut encore changer +GoToPublicAnswerPage = Voir l'interface publique de réponse BeCarefullVerdictKO = Attention si le statut de votre contrôle est KO. Il faudra refaire votre contrôle. ShowQcFrequencyPublicInterface = Afficher la période de validé (en jours) sur l'interface publique de contrôle ShowQcFrequencyPublicInterfaceDescription = Option permettant l'affichage la période de validé (en jours) sur l'interface publique de contrôle +GenerateSheetTags = Vous n'avez pas généré de catégories principales, veuillez cliquer ici pour vous rendre sur la page de configuration @@ -342,6 +354,7 @@ NotApplicable = NA AnswerPhoto = Photo AutoSaveActionQuestionAnswer = Sauvegarde automatique des réponses aux questions AutoSaveActionQuestionAnswerDescription = Sauvegarde automatiquement les réponses aux questions lors du choix de la réponse +NoObjectLineAnswersPhoto = Pas de photos sur les réponses du %s # # ControlDocument - Fiche de Contrôle @@ -360,19 +373,52 @@ controldocument_photo.odt = Fiche de Contrôle avec photos controldocument_photo = fiche de controle avec photos + +# +# Survey - Questionnaire +# + +# Data - Donnée +Survey = Questionnaire +Surveys = Questionnaires +SurveyList = Liste des questionnaires +NewSurvey = Nouveau questionnaire +TheSurvey = le questionnaire +SurveysCategoriesArea = Espace des tags/catégories des questionnaires +SurveysByFiscalYear = Rapport des questionnaires sur l'exercice fiscal +NeedObjectToLink = Vous devez sélectionner un objet à lier +ConfirmValidateSurvey = Êtes-vous sûr de vouloir valider le questionnaire ? +PublicSurvey = Interface publique de questionnaire +PublicSurveyToCome = Interface publique de questionnaire à venir + + +# +# SurveyDocument - Fiche du Questionnaire +# + +# Data - Donnée +SurveyDocument = Fiche du Questionnaire +Surveydocument = Fiche du questionnaire +surveydocument = fiche du questionnaire +surveydocument.odt = Fiche du Questionnaire +surveydocument_photo = fiche du questionnaire avec photos +surveydocument_photo.odt = Fiche du Questionnaire avec photos + + + # # Tools - Outils # # Data - Donnée DataMigrationDigiQualiToFile = Export des données de DigiQuali -DataMigrationExportSQA = Export Modèle de contrôle/Question/Réponse vers ZIP -DataMigrationExportSQADescription = Export des données des modèles de contrôle, questions et réponses en fichier ZIP +DataMigrationExportSQA = Export Modèle/Question/Réponse vers ZIP +DataMigrationExportSQADescription = Export des données des modèles, questions et réponses en fichier ZIP DataMigrationExportQA = Export Question/Réponse vers ZIP DataMigrationExportQADescription = Export des données des questions et réponses en fichier ZIP DataMigrationFileToDolibarr = Import des données vers DigiQuali DataMigrationImportZIP = Import fichier ZIP vers Dolibarr -DataMigrationImportZIPDescription = Import des données des modèles de contrôle, des questions et des réponses d'un fichier ZIP +DataMigrationImportZIPDescription = Import des données des modèles, des questions et des réponses d'un fichier ZIP ErrorArchiveNotWellFormattedZIP = L'archive n'est pas pris en charge ou est vide. Vous devez joindre un archive d'import au format .zip ImportFinishWith = L'import des %s a été terminé avec %s erreur(s) sur %s importation(s) @@ -382,8 +428,9 @@ ImportFinishWith = L'import des %s a été terminé avec %s err # Data - Donnée SelectProducts = Sélectionner un produit -SelectProductsOrServices = Sélectionner un produit / service +SelectProductsOrServices = Sélectionner un produit/service Contact = Contact/Adresse Remain = reste -InProgressAndLocked = -- (En cours + vérrouillé) -- +InProgressAndLocked = -- (En cours + verrouillé) -- NoObservations = Pas d'observations +BasedOnModel = Basé sur le modèle diff --git a/lib/digiquali.lib.php b/lib/digiquali.lib.php index b71bb93d..76795eb8 100644 --- a/lib/digiquali.lib.php +++ b/lib/digiquali.lib.php @@ -58,6 +58,16 @@ function digiquali_admin_prepare_head(): array $head[$h][2] = 'control'; $h++; + $head[$h][0] = dol_buildpath('/digiquali/admin/survey.php', 1); + $head[$h][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('Survey') : ''; + $head[$h][2] = 'survey'; + $h++; + + $head[$h][0] = dol_buildpath('/digiquali/admin/publicinterface.php', 1); + $head[$h][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('PublicInterface') : ''; + $head[$h][2] = 'publicinterface'; + $h++; + $head[$h][0] = dol_buildpath('/saturne/admin/documents.php?module_name=DigiQuali', 1); $head[$h][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('YourDocuments') : ''; $head[$h][2] = 'documents'; diff --git a/lib/digiquali_answer.lib.php b/lib/digiquali_answer.lib.php index f57cbe32..fe665b48 100644 --- a/lib/digiquali_answer.lib.php +++ b/lib/digiquali_answer.lib.php @@ -45,7 +45,7 @@ function answer_pictos_dropdown($selected = ''): string $out .= ''; } $out .= ''; - $out .= '