From f887ce486017ac6e8c950a9c1449f0b52f663537 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Mon, 8 Jan 2024 15:15:58 +0100 Subject: [PATCH 01/64] #1657 [SQL] add: data model survey --- sql/llx_c_survey_attendants_role.sql | 24 +++++++++++++ sql/survey/index.php | 2 ++ sql/survey/llx_categorie_survey.key.sql | 20 +++++++++++ sql/survey/llx_categorie_survey.sql | 20 +++++++++++ sql/survey/llx_digiquali_survey.key.sql | 22 ++++++++++++ sql/survey/llx_digiquali_survey.sql | 34 +++++++++++++++++++ .../llx_digiquali_survey_extrafields.key.sql | 17 ++++++++++ .../llx_digiquali_survey_extrafields.sql | 21 ++++++++++++ sql/survey/llx_digiquali_surveydet.key.sql | 22 ++++++++++++ sql/survey/llx_digiquali_surveydet.sql | 33 ++++++++++++++++++ ...lx_digiquali_surveydet_extrafields.key.sql | 17 ++++++++++ .../llx_digiquali_surveydet_extrafields.sql | 21 ++++++++++++ 12 files changed, 253 insertions(+) create mode 100644 sql/llx_c_survey_attendants_role.sql create mode 100644 sql/survey/index.php create mode 100644 sql/survey/llx_categorie_survey.key.sql create mode 100644 sql/survey/llx_categorie_survey.sql create mode 100644 sql/survey/llx_digiquali_survey.key.sql create mode 100644 sql/survey/llx_digiquali_survey.sql create mode 100644 sql/survey/llx_digiquali_survey_extrafields.key.sql create mode 100644 sql/survey/llx_digiquali_survey_extrafields.sql create mode 100644 sql/survey/llx_digiquali_surveydet.key.sql create mode 100644 sql/survey/llx_digiquali_surveydet.sql create mode 100644 sql/survey/llx_digiquali_surveydet_extrafields.key.sql create mode 100644 sql/survey/llx_digiquali_surveydet_extrafields.sql diff --git a/sql/llx_c_survey_attendants_role.sql b/sql/llx_c_survey_attendants_role.sql new file mode 100644 index 00000000..874040e3 --- /dev/null +++ b/sql/llx_c_survey_attendants_role.sql @@ -0,0 +1,24 @@ +-- 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 https://www.gnu.org/licenses/. + +CREATE TABLE llx_c_survey_attendants_role( + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + entity integer default 1, + ref varchar(128), + label varchar(255), + description text, + active tinyint(4) DEFAULT 1, + position integer DEFAULT 0 +) ENGINE=innodb; diff --git a/sql/survey/index.php b/sql/survey/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/sql/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 https://www.gnu.org/licenses/. + +ALTER TABLE llx_categorie_survey ADD PRIMARY KEY pk_categorie_survey (fk_categorie, fk_survey); +ALTER TABLE llx_categorie_survey ADD INDEX idx_categorie_survey_fk_categorie (fk_categorie); +ALTER TABLE llx_categorie_survey ADD INDEX idx_categorie_survey_fk_survey (fk_survey); +ALTER TABLE llx_categorie_survey ADD CONSTRAINT fk_categorie_survey_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); +ALTER TABLE llx_categorie_survey ADD CONSTRAINT fk_categorie_survey_digiquali_survey_rowid FOREIGN KEY (fk_survey) REFERENCES llx_digiquali_survey (rowid); diff --git a/sql/survey/llx_categorie_survey.sql b/sql/survey/llx_categorie_survey.sql new file mode 100644 index 00000000..47f7bd76 --- /dev/null +++ b/sql/survey/llx_categorie_survey.sql @@ -0,0 +1,20 @@ +-- 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 https://www.gnu.org/licenses/. + +CREATE TABLE llx_categorie_survey( + fk_categorie integer NOT NULL, + fk_survey integer NOT NULL, + import_key varchar(14) +) ENGINE=innodb; diff --git a/sql/survey/llx_digiquali_survey.key.sql b/sql/survey/llx_digiquali_survey.key.sql new file mode 100644 index 00000000..3a3787c5 --- /dev/null +++ b/sql/survey/llx_digiquali_survey.key.sql @@ -0,0 +1,22 @@ +-- 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 https://www.gnu.org/licenses/. + +ALTER TABLE llx_digiquali_survey ADD INDEX idx_digiquali_survey_rowid (rowid); +ALTER TABLE llx_digiquali_survey ADD INDEX idx_digiquali_survey_ref (ref); +ALTER TABLE llx_digiquali_survey ADD INDEX idx_digiquali_survey_status (status); +ALTER TABLE llx_digiquali_survey ADD INDEX idx_digiquali_survey_fk_sheet (fk_sheet); +ALTER TABLE llx_digiquali_survey ADD INDEX idx_digiquali_survey_fk_projectid (projectid); +ALTER TABLE llx_digiquali_survey ADD UNIQUE INDEX uk_digiquali_survey_ref (ref, entity); +ALTER TABLE llx_digiquali_survey ADD CONSTRAINT llx_digiquali_survey_fk_user_creat FOREIGN KEY (fk_user_creat) REFERENCES llx_user(rowid); diff --git a/sql/survey/llx_digiquali_survey.sql b/sql/survey/llx_digiquali_survey.sql new file mode 100644 index 00000000..2eec6816 --- /dev/null +++ b/sql/survey/llx_digiquali_survey.sql @@ -0,0 +1,34 @@ +-- 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 https://www.gnu.org/licenses/. + +CREATE TABLE llx_digiquali_survey( + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + ref varchar(128) DEFAULT '(PROV)' NOT NULL, + ref_ext varchar(128), + entity integer DEFAULT 1 NOT NULL, + date_creation datetime NOT NULL, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + import_key varchar(14), + status integer DEFAULT 1 NOT NULL, + note_public text, + note_private text, + type varchar(128), + photo text, + track_id varchar(128) NOT NULL, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + fk_sheet integer NOT NULL, + projectid integer +) ENGINE=innodb; diff --git a/sql/survey/llx_digiquali_survey_extrafields.key.sql b/sql/survey/llx_digiquali_survey_extrafields.key.sql new file mode 100644 index 00000000..579639fb --- /dev/null +++ b/sql/survey/llx_digiquali_survey_extrafields.key.sql @@ -0,0 +1,17 @@ +-- 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 https://www.gnu.org/licenses/. + +ALTER TABLE llx_digiquali_survey_extrafields ADD INDEX idx_digiquali_survey_extrafields_rowid (rowid); +ALTER TABLE llx_digiquali_survey_extrafields ADD INDEX idx_digiquali_survey_extrafields_fk_object (fk_object); diff --git a/sql/survey/llx_digiquali_survey_extrafields.sql b/sql/survey/llx_digiquali_survey_extrafields.sql new file mode 100644 index 00000000..9f7d70ca --- /dev/null +++ b/sql/survey/llx_digiquali_survey_extrafields.sql @@ -0,0 +1,21 @@ +-- 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 https://www.gnu.org/licenses/. + +create table llx_digiquali_survey_extrafields( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + import_key varchar(14), + fk_object integer NOT NULL +) ENGINE=innodb; diff --git a/sql/survey/llx_digiquali_surveydet.key.sql b/sql/survey/llx_digiquali_surveydet.key.sql new file mode 100644 index 00000000..099db53a --- /dev/null +++ b/sql/survey/llx_digiquali_surveydet.key.sql @@ -0,0 +1,22 @@ +-- 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 https://www.gnu.org/licenses/. + +ALTER TABLE llx_digiquali_surveydet ADD INDEX idx_digiquali_surveydet_rowid (rowid); +ALTER TABLE llx_digiquali_surveydet ADD INDEX idx_digiquali_surveydet_ref (ref); +ALTER TABLE llx_digiquali_surveydet ADD INDEX idx_digiquali_surveydet_status (status); +ALTER TABLE llx_digiquali_surveydet ADD INDEX idx_digiquali_surveydet_fk_survey (fk_survey); +ALTER TABLE llx_digiquali_surveydet ADD INDEX idx_digiquali_surveydet_fk_question (fk_question); +ALTER TABLE llx_digiquali_surveydet ADD UNIQUE INDEX uk_digiquali_surveydet_ref (ref, entity); +ALTER TABLE llx_digiquali_surveydet ADD CONSTRAINT llx_digiquali_surveydet_fk_user_creat FOREIGN KEY (fk_user_creat) REFERENCES llx_user(rowid); diff --git a/sql/survey/llx_digiquali_surveydet.sql b/sql/survey/llx_digiquali_surveydet.sql new file mode 100644 index 00000000..e4c89240 --- /dev/null +++ b/sql/survey/llx_digiquali_surveydet.sql @@ -0,0 +1,33 @@ +-- 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 https://www.gnu.org/licenses/. + +CREATE TABLE llx_digiquali_surveydet( + rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL, + ref varchar(128) DEFAULT '(PROV)' NOT NULL, + ref_ext varchar(128), + entity integer DEFAULT 1 NOT NULL, + date_creation datetime NOT NULL, + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + import_key varchar(14), + status integer DEFAULT 1 NOT NULL, + type varchar(128), + answer text, + answer_photo text, + comment text, + fk_user_creat integer NOT NULL, + fk_user_modif integer, + fk_survey integer NOT NULL, + fk_question integer +) ENGINE=innodb; diff --git a/sql/survey/llx_digiquali_surveydet_extrafields.key.sql b/sql/survey/llx_digiquali_surveydet_extrafields.key.sql new file mode 100644 index 00000000..31928d9a --- /dev/null +++ b/sql/survey/llx_digiquali_surveydet_extrafields.key.sql @@ -0,0 +1,17 @@ +-- 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 https://www.gnu.org/licenses/. + +ALTER TABLE llx_digiquali_surveydet_extrafields ADD INDEX idx_digiquali_surveydet_extrafields_rowid (rowid); +ALTER TABLE llx_digiquali_surveydet_extrafields ADD INDEX idx_digiquali_surveydet_extrafields_fk_object (fk_object); diff --git a/sql/survey/llx_digiquali_surveydet_extrafields.sql b/sql/survey/llx_digiquali_surveydet_extrafields.sql new file mode 100644 index 00000000..49be83c9 --- /dev/null +++ b/sql/survey/llx_digiquali_surveydet_extrafields.sql @@ -0,0 +1,21 @@ +-- 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 https://www.gnu.org/licenses/. + +create table llx_digiquali_surveydet_extrafields( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + import_key varchar(14), + fk_object integer NOT NULL +) ENGINE=innodb; From e2b7b80497a0690dfbdd85aef6c444c76714a76a Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 18:17:10 +0100 Subject: [PATCH 02/64] #1657 [SQL] add: insert dictionary value --- sql/data.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/data.sql b/sql/data.sql index bd08bd02..65a90c30 100644 --- a/sql/data.sql +++ b/sql/data.sql @@ -24,4 +24,7 @@ INSERT INTO `llx_c_question_type` (`rowid`, `entity`, `ref`, `label`, `descripti INSERT INTO `llx_c_question_type` (`rowid`, `entity`, `ref`, `label`, `description`, `active`, `position`) VALUES(7, 0, 'OkKoToFixNonApplicable', 'OkKoToFixNonApplicable', '', 1, 60); INSERT INTO `llx_c_control_attendants_role` (`rowid`, `entity`, `ref`, `label`, `description`, `active`, `position`) VALUES(1, 0, 'Controller', 'Controller', '', 1, 1); -INSERT INTO `llx_c_control_attendants_role` (`rowid`, `entity`, `ref`, `label`, `description`, `active`, `position`) VALUES(2, 0, 'Attendant', 'Attendant', '', 1, 20); \ No newline at end of file +INSERT INTO `llx_c_control_attendants_role` (`rowid`, `entity`, `ref`, `label`, `description`, `active`, `position`) VALUES(2, 0, 'Attendant', 'Attendant', '', 1, 20); + +-- 1.11.0 +INSERT INTO `llx_c_survey_attendants_role` (`rowid`, `entity`, `ref`, `label`, `description`, `active`, `position`) VALUES(1, 0, 'Attendant', 'Attendant', '', 1, 1); From b1ea72199e8b3375ad9665c0c8e27bef6f269561 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Mon, 8 Jan 2024 15:53:00 +0100 Subject: [PATCH 03/64] #1660 [ODT] add: survey document --- .../surveydocument.class.php | 51 +++ .../doc_surveydocument_odt.modules.php | 352 ++++++++++++++++++ .../surveydocument/index.php | 2 + .../mod_surveydocument_standard.php | 42 +++ .../surveydocument/modules_surveydocument.php | 47 +++ .../doctemplates/surveydocument/index.php | 2 + .../template_surveydocument.odt | Bin 0 -> 22080 bytes .../template_surveydocument_photo.odt | Bin 0 -> 21967 bytes 8 files changed, 496 insertions(+) create mode 100644 class/digiqualidocuments/surveydocument.class.php create mode 100644 core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php create mode 100644 core/modules/digiquali/digiqualidocuments/surveydocument/index.php create mode 100644 core/modules/digiquali/digiqualidocuments/surveydocument/mod_surveydocument_standard.php create mode 100644 core/modules/digiquali/digiqualidocuments/surveydocument/modules_surveydocument.php create mode 100644 documents/doctemplates/surveydocument/index.php create mode 100644 documents/doctemplates/surveydocument/template_surveydocument.odt create mode 100644 documents/doctemplates/surveydocument/template_surveydocument_photo.odt 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/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..73fc94c6 --- /dev/null +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -0,0 +1,352 @@ + + * + * 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 . '/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; + +require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; +require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php'; +require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; + +// Load Saturne libraries +require_once __DIR__ . '/../../../../../../saturne/class/saturnesignature.class.php'; +require_once __DIR__ . '/../../../../../../saturne/core/modules/saturne/modules_saturne.php'; + +// Load DigiQuali libraries +require_once __DIR__ . '/mod_controldocument_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, $langs; + + $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); + + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet','', '', 'OR', 1, 'sourcetype', 0); + $questionIds = $sheet->linkedObjectsIds; + + if (is_array($questionIds['digiquali_question']) && !empty($questionIds['digiquali_question'])) { + $controldet = new ControlLine($this->db); + $question = new Question($this->db); + $answer = new Answer($this->db); + foreach ($questionIds['digiquali_question'] as $questionId) { + $question->fetch($questionId); + + $controldets = $controldet->fetchFromParentWithQuestion($object->id, $questionId); + + $tmpArray['ref'] = $question->ref; + $tmpArray['label'] = $question->label; + $tmpArray['description'] = strip_tags($question->description); + + if (is_array($controldets) && !empty($controldets)) { + $questionAnswerLine = array_shift($controldets); + $tmpArray['ref_answer'] = $questionAnswerLine->ref; + $tmpArray['comment'] = dol_htmlentitiesbr_decode(strip_tags($questionAnswerLine->comment, '
')); + + $answerResult = $questionAnswerLine->answer; + + $question->fetch($questionAnswerLine->fk_question); + $answerList = $answer->fetchAll('ASC', 'position', 0, 0, ['fk_question' => $questionAnswerLine->fk_question]); + + $answersArray = []; + if (is_array($answerList) && !empty($answerList)) { + foreach ($answerList as $answerSingle) { + $answersArray[$answerSingle->position] = $answerSingle->value; + } + } + + switch ($question->type) { + case 'OkKo' : + case 'OkKoToFixNonApplicable' : + case 'UniqueChoice' : + $tmpArray['answer'] = $answersArray[$answerResult]; + break; + case 'Text' : + case 'Range' : + $tmpArray['answer'] = $answerResult; + break; + case 'Percentage' : + $tmpArray['answer'] = $answerResult . ' %'; + break; + case 'MultipleChoices' : + $answers = explode(',', $answerResult); + $tmpArray['answer'] = ''; + foreach ($answers as $answerId) { + $tmpArray['answer'] .= $answersArray[$answerId] . ', '; + } + $tmpArray['answer'] = rtrim($tmpArray['answer'], ', '); + break; + default: + $tmpArray['answer'] = ''; + } + + $path = $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $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] = $questionAnswerLine->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); + $usertmp = new User($this->db); + $projecttmp = new Project($this->db); + + $sheet->fetch($object->fk_sheet); + $usertmp->fetch($object->fk_user_controller); + $projecttmp->fetch($object->projectid); + + $object->fetchObjectLinked('', '', $object->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); + $linkableElements = get_sheet_linkable_objects(); + + if (is_array($linkableElements) && !empty($linkableElements)) { + 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_label_ref'] .= $objectName . chr(0x0A); + $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; + } + } + } + + $tmpArray['control_ref'] = $object->ref; + $tmpArray['object_label_ref'] = rtrim($tmpArray['object_label_ref'], chr(0x0A)); + $tmpArray['control_date'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); + $tmpArray['project_label'] = $projecttmp->ref . ' - ' . $projecttmp->title; + $tmpArray['sheet_ref'] = $sheet->ref; + $tmpArray['sheet_label'] = $sheet->label; + + switch ($object->verdict) { + case 1: + $tmpArray['verdict'] = 'OK'; + break; + case 2: + $tmpArray['verdict'] = 'KO'; + break; + default: + $tmpArray['verdict'] = ''; + break; + } + + $tmpArray['public_note'] = $object->note_public; + + $tmpArray['mycompany_name'] = $conf->global->MAIN_INFO_SOCIETE_NOM; + $tmpArray['mycompany_address'] = (!empty($conf->global->MAIN_INFO_SOCIETE_ADRESS) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_ADRESS : ''); + $tmpArray['mycompany_website'] = (!empty($conf->global->MAIN_INFO_SOCIETE_WEBSITE) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_WEBSITE : ''); + $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']; + + 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/digiqualidocuments/surveydocument/modules_surveydocument.php b/core/modules/digiquali/digiqualidocuments/surveydocument/modules_surveydocument.php new file mode 100644 index 00000000..1aff05e5 --- /dev/null +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/modules_surveydocument.php @@ -0,0 +1,47 @@ + + * + * 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/modules_surveydocument.php + * \ingroup digiquali + * \brief File that contains parent class for surveydocuments document models + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../../../../../saturne/core/modules/saturne/modules_saturne.php'; + +/** + * Parent class for documents models + */ +abstract class ModeleODTSurveyDocument 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, 'surveydocument', $maxfilenamelength); + } +} 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 @@ +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 Fe*s9Eujc>& literal 0 HcmV?d00001 diff --git a/documents/doctemplates/surveydocument/template_surveydocument_photo.odt b/documents/doctemplates/surveydocument/template_surveydocument_photo.odt new file mode 100644 index 0000000000000000000000000000000000000000..2839e662c89950a8950441437469e1d42c858148 GIT binary patch literal 21967 zcmb5V19W9Uv^JP@l8)Kw*mlyfZQDslH@0otwr$(CZQI7A-~YdV&CGi5&6-+kud}Pp z-Kw+ityBBEB`W~}iVOq<0R$xCnW3URNDo5_1O)VtzK4J;Oe_HQF4h2DYin~8eO-GK zD@$5OO9L7!T{{yy8Y^pnrGb^cg9X6Sp2i;FY%lx&Q1&hVe;UR&Ctzi1Xkz4G`yXoT z=xMF3tR1YsWgTe$|H;|e>)Jcm>FL`34{~V#g*|&KEA#(Z>R;IZZ!dNA^#SI9Z>Ozn zY4sg!ZU4pUY+?T2yZm37HZ-@=wFmr94&(k8rcEq#jR1DE{3iAmy4H68KcoFOvHx?l zSpS7dYg;QLTY%mF$L#)BPxIgGTI*T@%>TEa5D*amky77A{!fGX<}7qAO$-5c_B6Hz zh7)l;mVR_FfmfVD$dd>9*W+zYu~_SZ;On=7ldSF!I*!xE}YJ@Ha0@Dl-INkPc# z+WP>t&~gN!7-u2hGGZ4=s=@XUG-#X(;e&BOPmH(H1I%$nyL^Y~WY;G=t}v4fds(&! zI*x*_AjfPvu#lg>lIvmTsG*MC^6H1@_`_54y(^>s`aMj-q=tFpSF(EQw^7dAcSdQ* z-W5}S#0=>vV-s;ph#b{DPf~Nfn-YR0a%wjZ@(8sLk6HL9acpwf$dF#)5;~c_M49Hw z$Vz}igx=JCfdB&m`GEid{ja?GuO<)bd-Ck;UCaS?|MKqw`yz6!`Nk`Y|9U4BrPy4o zaS2!I5g}g`6R+iv#_fqEn|RcaFvOR8@eCRK9{Uy9BeGL0E5DC6|{NT9lY^x)?Q%1A>u{bS= zb#$DO1C*61+b+IRAU!83k27QuxqwJZgslIB-EN6-Fy^$D#%9SKIaes&GpmUhcnMX z^P_1)s&z5{!}<@}$vAI)mrGFa=JXPQq`sI3VuNQI%+-{gQ;!bNi=an9 zb2j$ErGl07Q%TX2lvR^ol6%_)LROo=$o7DpL5CBP5m#@P3=-BH5#@6@b6-J!%tK2q z=lZq*U7c(ohVM_UjVFpTZhUlUM*Akz4Iv+m)IDnj(A0D&h1UpUrXA6MVVy;J{Wyyq z4fj-$YRhX8TTge?J%2g`DCZ^zfPX8_lf16RufV)(5Z51&vydi)idaZJKJsbx7{K z<4#HJvtCQNctaJ}7>n}E8U$XE9Yt_kmhY=E^OtD-Ur;6jpHu!k9vuI?Fb+PC0nU`l z@h4&>8#cC;@tQ7}f5y3rzy;D!COyHQuy%$6;EFx} zvHGO|+`NA-U&Nkp@xDT;C5*QwH1GW3+oD5fx{qiL@$*Y8x_DJMSMH}Ef{boFvPHF< z02+eqvQSseGcDu~R3#wy&$8JdmsK)i^z!A#a;paE3j}0~mGi`)rdoi&uPBR6Z~BqLo63^vTLYV-ifRA4I}baE*hBdx5oVIp|vf*)7PGK_2no^q?v~ zVq!r*2RL>kkt^Rg;&|PG%OF-i{^LCD-(ZPXa5r!w>kG-ix7|Z2=_r=Rz>s2mpS)6$ zcf%95fAH*($Ek%iqBlNDE9;l2JP6Vm4joN`&Yk;UnDTK9%hpYWpVR1h0DwgYSjr|P zm2S|WU_la&0!nIo3{jiR%TqG`Xm%9is<2%}yM?0FSk_ik9OmxU)DZnn2>530-b-L{ zlUCDw)*+-XNJL=lo{$tM=D`mVb`txSATkbu(&(f-3WG%5F{&ac^#^{ zCb^^=C79i@4Xp(N0*oE$4Zv{4=-O)8bWNfsSojkax-C56XJAaxOPMq`uRshu)0zv~ zpT=Vdmg(z}8g7eXr{g`Nq&7IYADzes*mY=t!)vqx^Q~zreNr2Uzve~uX*A39;hA~a zIIPI4eST@d;4Eazc>=Ck`U{%*oXg}x&(TL6txXvNYDInKSf3cbB>Lmvd`j(>Y(#q` zX4|lw&M;ovtBDMNLUS8P+#pd6bpB3p|M=uo>rlPCaRl#K4g9Gs1PR9b5=LX4lZUlaJdH*g z?w8GbUd0k`dR7%GfrsgBqBxV8Tgpst{P0tVKq^GkpJwRJ)@TIo4^6$Ut<_&QxvNyV z8e`w-<726P*toU3{V($dPOTp*dkdxy&GOIqNn#&C_N(u;_BzeTI=GsFS`i6IJEEo) zAAfEojkLZ_Vmat~6_#vyXTpfd$Dyu=n_mMBR!a^bgcq5k^(T)n;dZsAtHR6?LHxvx zO288`-n?-XLTUO~LQEN#7vB20_VqhQGOLeENuk$TUAwK?l_ig&3VuJn8&_;=NiT**ZXTHp)c>U{{HsP;cw`p9puridtok^k z-qV&|{u{evik+LgN`KhYd;;SrcxNd3VA(WNkH!lve4F#RA+~WBSuf&9Zzm!ynB74U z4&2aZ%geo4mSE`=x>nsM6&CxKK?fl8m+oapzrHW?QAsrnc0n^s=aHUr)rY!z1O6~e1EZZ>p4#C$3# z#xWzQfRswNto>d!4!w}tGPZ0w<48dD^}?lKBaq`M)Tk|;Hh_X6$h859uFkHkNk|aN z&NX^KuH-Y@z=YW?lfyTNlkq~0mV=EMN=4E#7ijRKI2R>xi}TSTq)Z;EbO9`}^EgB^ zD*bXa#8J^x!$Hl4^>$hH>DVyFYz!nAI*j0k{n0v<*}@hKZ3MwDyt*$`qkTeAezQAz zvfM5qN)3hJ9XnMK_R|=Z2i$hmMLE53DA8kK)C608;#ItRfQ`$;Fx?RaJKPcF!u{|9 z)#&a#rN-rQoc&po-!vS>JIv1YbDXt9m5PH^i{CW;-@xmsn$0S@#@yqDY>*;r1rX}d z{ceiQs#>E}5PSZh32-xOQPNcS+?^v!1+b}mCQoRbHWvdkt+JLo4L*}F^tVk5jj7Ou zxf&+3EqDjffCVUPxUw?5j4vv;xZW4e+Yx3N=Z;`GAjuT#hcGM}@D@SjL`8aJigL znhSr}9~fA%6m`&f^L^@x)Xl2wU607KgKb z#tv#nP)oFha9pJp(7LUDPzkRSUHgXb=KVS&BCe(ytGzZqrF9TxYv{GfC^<`9DtIsp z{*;f(qu}~yr}C4<;+0~Ralf>%#73dLy7JwPak5wC(KdK6!;y9_rzpzW?C!Hq%mF+g zkOz#(z7lWWt(t!~NVZR0jlch~FZ*v#dO?G_BTvE^t%l++n3;mbT`;z8L~(|<1xncG z%7xn=C^%1r)VGAgLd28}CqA!GmaMnTtIVSa+EXhl48#Gj=4v%Q?|mKs4Wna*#If)C zN)`<&hUNVa&Fh$h?Fog$-gh#Z?_V`U2JH+sLiNd`w1qzmG?VEwZK>4QN6F|(Mw4U@ zhr#G7N0l>osFUx-*aPf#qXm1D(}=VRx}$rJH7K1l9Ve&3q&Zg~`F{#ZnNK1cLfI2C zeUX7hZ->VnAt##JlJa0}d8~dto=lmZgaqanxT>lL_2{3c2sy_xcO@%QE8qOWNG(z+ zzrG|0nCCUd#U~Q$J@xPx(dTBsGnZPP_|$3H$qFCE^@kVOw^U?4dif{@|9Y!VG)>>VqwC)pZZ{dpBuEFmJfzaGG?a};AiLKQ9WL$q`((I zUdI_sfIICCER=R6u3L7bTzHNa&8vRtC`Tu!G!ow`S^bjtWS?nRzG8++fp$>$!HiF& z@?i=VQIKjU+QMSwd`Zm>AuX=B>~D}`X4T2!Pc)qq+||7D8T0MN2o%>&!*8X81S3f3 zM1~+&Hli#cTwJbguuFLLM@N9d=(!niqi+KPe>tzyu^@B3$(n@UuTjVu#ygV+=H$z% zKk$oOUBY>Y0%^E)N(fqJh(g+mp13KZ!z** ztnh*H%?V6{`6mnrF-vxf&#YkW0Twm~{4lqnq{^tg0ORgT(fuTi4^Z{`tNK*e;T(_Y z2kN!p^qFj=#RF&VG!sBM39J7=f@w6fNtDVhA42^}q_vuR{5^D+ce}oWeXM#XHpY7w zis}{8c^8%ko@F8_YsPZor&;Ca&EY~?3PV<>XG7|mms+RmgCSPkkh~s5jdy&tLYS$9 z(|C$rusP>~|Fa4faGdZJ*)5R+K4gsnFsT{RnALiMdjb+rM3}n2wSL*^GK>YuDNa28 znBlxYp=!q?z-qixJHZpD58a&JbaK&MvPaHR=*5ZH`n(Lk%S$Lcaa6v)Yo^LkYVufp z_NF`$jCla!SWQ7jX3y*EwP{9i83(1aNE>b_wF~Bo5mPIU`UPJE*aV;c^3IZOi>D9d zONKtTugKf-gjPx*%&bkr;qMuyr#gCO!>67x?C@PTenCPb*Cw5q(kj28uedKk+BcS8>sHmC+2MT0{E3A4w>}g< zc>_J)%+$=oI9vG}Gnl&gSkr%u)P_?%{m@an!A7m!E|45UG8TIB-w&hL2&VFPzHEy1 z4VcJLlQfnrgC$=k01(NHwu=rK1sIu+y0vt7+zC8MhZxUeZ#5u5+s#@=pco@9#IT^o zfXLgps&wz{~R_@q|lYSdB{Ykf)4k$i_wl(da3;$Xyadbtu#v9*a>jF0cZoJ z_FAszEO4~qe=R>-dw1yIaxiq0iL=jhfXz%o*s_-rN+xmtCec@vl1ZAEiWBLSGMDQ? znck>BJe-!QB!2liYhh@lDUSi3dff*|=ksoFW@b!WKA-d+6bDaTT0H4f+*8w^8xqFG z#%jG}W+b<-c zkw}y!EOtC?IeE^$^77Ka#N5zkw>08 zS8VTv#f{qh+1bLwiCD5~vvu@r$-Cb~Bh?Bfv(4s4F$Fag>I0JVl`X(5V_23FQCMJ< zhFRAKP0NQ5VAKfXkrZ@AD}yLR=wMoW#k;$b?LJ#^B(V~v)L>E`np~#Igl@LgKgY&Z z=)IzGQfS5ctC^m4MX}fq0{J$DLYqk3)vFGM&2vTaj;c3H zDp!WjB5VPjBQG&dSLYC~maELbk)%yd(GmFE-ZbxNmJ|pWKOaEseS54zZwMT=_i_B+FS&jw)65zkUP%%Odt#-MEW5~X) zr58rFp^pLCR_Tf|wv89c&L7r^r+QXEkdF=4XcVA5Fv%tB9Vn<eT92TmrE|pbP(o&Q z5n~RyWz&r~ojLbt7>L6tTXP-Q4#8M5U_B?8l+lU4PREF@AmJ%GEgn(Ka9#DWbp zYa9(O)Vb_f91O{}i>oV^E7H!}m~Ji9xskarc`^@|2O{k{Zi9br1y3YDA>|b>U|EYL z*W!@L^tgF_`zOU1?B4vR%i(cusJpobb`6hy?(?U9590@CRNkC5;X1&E=odGKC5<^Q zn;X;a{z+M29?PO7OshGn+BsWwq!MBnJ+$`k=PtBC_Zpl14lb-#h#ld|~qWzE}<;~?tT+X<&P>dFtN9uFf} z%)a_+cb1nU$-}^8lve`^8MQ+z$aWN&L#O(IL7C%GEADVwt5;eOmaiZGR!!&&W03cs zKtT3%|G8@ZCpxPmU*Vbky`KIfrtei#)x~%v z_4UOg?>;s>{6n{5r5-~~Z>q9`p=k2__?V2Ac4+?a^wjlqzNGO|*#^=EPeog~XJO=F zFDS}yy(!um&>QdkvU_klFlO_D0^xYIX<=x{+~Ni0|F*Li)a%=ugEXSb+3r39rK_u} z*%<(&W4+#PW7oGD!TUHQgUjj8+3wLER2oF9)gGUj31^Vb<)*BrhMs)sWAgm*_QZ=c z{4*42AR;0{OH;E_tK%nAKnUcpkOU8ccX~SA%5%eXuC;FO^~J$d7B|QTh(S<505CeQ zhL3@f5$SZ+)n*6C;CqMLlXop|4@c8gw*nJgjD8{lFOUsmIxvxsd3h(mAJ~9q;UoNV zr|NGfkQg5ypHBP_uAet-big1h;^N}GH}&=P)6=r5i0}@tQ&~J{YG^fsXzOkWOMNZI zH;^$x5)k*nx|TRNc#!wh(^kB=KlFdgQ-QSb@_-CbQcxI9qy{9iuB@+A4S?3CQX8E>i2L+j%^+%B&#ZXAnlgg<}&ghYnHK|F%n0tyaNF;kl?SJ;Qo zbajmN$P4oJayL6YK*HrYQ^!U}6%`ZkI}eV2E!O~s@SdnUO(B;(#U~^y%FE5ItcFU5 zMpjN$+Gu*i-B4>Kg;}4Rj31WOa0OAhr#3bV>sLB=({$dBc1|sNfKKea+ebfOw*B z`tLPDf8W)&vb6u-nEr*}Quuj*>nqL$~ZMwftHoU(*;4SfQ281T#6&{F zT*O(f=Vjlt#^h161^x8+!;)PlV8F#TLykwi@Q$r$%bd9`0%6X!v~+T>2)(58z+QV_ z<4;26L=M;=v!3)co4askh^+F-+~ANf1&AH}<%U0n)Qt-s)hDYca|CzG7Bt5jY0$1U z7T496oCiS;5wG=b+fJG8lA7ihZn}QA1ugJgOYnAVeo9W%1N{Rv>BHK5U3JyG5aOZ2 zjjbk(ixF0V)3niM5&Ia`^%u71oydqX?~$^ps4?tOA+`xQa!n{_Rdz>u%`tw55m@r< z-0t_;R=XnIp=s1kuFFk(!ZfUoktX!6V+A_+4*UT)j##mf73P2T^Hd!7TEnsFNN)2x z�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 literal 0 HcmV?d00001 From e83d77bfb38afc4fd65379fa71fb9d01609ef1f4 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 18:34:10 +0100 Subject: [PATCH 04/64] #1660 [ODT] fix: missing survey document conf and clean --- class/actions_digiquali.class.php | 4 +++ .../doc_surveydocument_odt.modules.php | 30 ++++--------------- core/modules/modDigiQuali.class.php | 7 +++-- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index 064acb81..7da1b46c 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -354,6 +354,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; diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index 73fc94c6..76358e39 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -23,20 +23,10 @@ */ // Load Dolibarr libraries -require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; - require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; -require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php'; -require_once DOL_DOCUMENT_ROOT . '/projet/class/task.class.php'; - -// Load Saturne libraries -require_once __DIR__ . '/../../../../../../saturne/class/saturnesignature.class.php'; -require_once __DIR__ . '/../../../../../../saturne/core/modules/saturne/modules_saturne.php'; // Load DigiQuali libraries -require_once __DIR__ . '/mod_controldocument_standard.php'; +require_once __DIR__ . '/mod_surveydocument_standard.php'; require_once __DIR__ . '/../../../../../lib/digiquali_sheet.lib.php'; require_once __DIR__ . '/../../../../../class/question.class.php'; @@ -90,6 +80,7 @@ public function info(Translate $langs): string return parent::info($langs); } + //@todo /** * Fill all odt tags for segments lines * @@ -131,7 +122,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo $questionIds = $sheet->linkedObjectsIds; if (is_array($questionIds['digiquali_question']) && !empty($questionIds['digiquali_question'])) { - $controldet = new ControlLine($this->db); + $controldet = new SurveyLine($this->db); $question = new Question($this->db); $answer = new Answer($this->db); foreach ($questionIds['digiquali_question'] as $questionId) { @@ -185,7 +176,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo $tmpArray['answer'] = ''; } - $path = $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $question->ref; + $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)) { @@ -245,6 +236,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo return 0; } + //@todo /** * Function to build a document on disk * @@ -324,18 +316,6 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $tmpArray['sheet_ref'] = $sheet->ref; $tmpArray['sheet_label'] = $sheet->label; - switch ($object->verdict) { - case 1: - $tmpArray['verdict'] = 'OK'; - break; - case 2: - $tmpArray['verdict'] = 'KO'; - break; - default: - $tmpArray['verdict'] = ''; - break; - } - $tmpArray['public_note'] = $object->note_public; $tmpArray['mycompany_name'] = $conf->global->MAIN_INFO_SOCIETE_NOM; diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 9e428b25..dac0b3bd 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -139,7 +139,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. @@ -227,7 +228,7 @@ 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_CONTROLDOCUMENT_DISPLAY_MEDIAS', 'integer', 1,'', 0, 'current'], $i++ => ['DIGIQUALI_DOCUMENT_MEDIA_VIGNETTE_USED', 'chaine', 'small','', 0, 'current'], // CONST CONTROL LINE @@ -620,9 +621,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; From 246307d387cdaaf1defff6fc522dea5f4884ec58 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Thu, 11 Jan 2024 19:40:48 +0100 Subject: [PATCH 05/64] #1660 [ODT] add: ODT v1.1 and doc_survey --- .../doc_surveydocument_odt.modules.php | 108 ++++++++---------- .../template_surveydocument.odt | Bin 22080 -> 19804 bytes .../template_surveydocument_photo.odt | Bin 21967 -> 19746 bytes 3 files changed, 49 insertions(+), 59 deletions(-) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index 76358e39..0d3e055d 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -93,7 +93,7 @@ public function info(Translate $langs): string */ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $moreParam): int { - global $conf, $langs; + global $conf; $object = $moreParam['object']; @@ -194,18 +194,18 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo } } - // Get answer photos. + // 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. + // We may arrive here if tags for lines not present into template $foundTagForLines = 0; $listLines = ''; dol_syslog($e->getMessage()); } - // Loop on previous photos array. + // Loop on previous photos array if ($foundTagForLines) { if (is_array($photoArray) && !empty($photoArray)) { foreach ($photoArray as $photoPath => $answerRef) { @@ -236,7 +236,6 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo return 0; } - //@todo /** * Function to build a document on disk * @@ -267,65 +266,56 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa } $outputLangs->loadLangs(['products', 'bills', 'orders', 'contracts', 'projects', 'companies']); - $sheet = new Sheet($this->db); - $usertmp = new User($this->db); - $projecttmp = new Project($this->db); + + $sheet = new Sheet($this->db); + $project = new Project($this->db); $sheet->fetch($object->fk_sheet); - $usertmp->fetch($object->fk_user_controller); - $projecttmp->fetch($object->projectid); - - $object->fetchObjectLinked('', '', $object->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); - $linkableElements = get_sheet_linkable_objects(); - - if (is_array($linkableElements) && !empty($linkableElements)) { - 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_label_ref'] .= $objectName . chr(0x0A); - $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; - } - } - } - - $tmpArray['control_ref'] = $object->ref; - $tmpArray['object_label_ref'] = rtrim($tmpArray['object_label_ref'], chr(0x0A)); - $tmpArray['control_date'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); - $tmpArray['project_label'] = $projecttmp->ref . ' - ' . $projecttmp->title; - $tmpArray['sheet_ref'] = $sheet->ref; - $tmpArray['sheet_label'] = $sheet->label; + $project->fetch($object->projectid); - $tmpArray['public_note'] = $object->note_public; + $object->fetchObjectLinked('', '', $object->id, 'digiquali_survey', 'OR', 1, 'sourcetype', 0); - $tmpArray['mycompany_name'] = $conf->global->MAIN_INFO_SOCIETE_NOM; - $tmpArray['mycompany_address'] = (!empty($conf->global->MAIN_INFO_SOCIETE_ADRESS) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_ADRESS : ''); - $tmpArray['mycompany_website'] = (!empty($conf->global->MAIN_INFO_SOCIETE_WEBSITE) ? ' - ' . $conf->global->MAIN_INFO_SOCIETE_WEBSITE : ''); - $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 : ''); + $linkableElements = get_sheet_linkable_objects(); + if (is_array($linkableElements) && !empty($linkableElements)) { + 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_label_ref'] .= $objectName . chr(0x0A); + $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; + } + } + } + + $tmpArray['object_ref'] = $object->ref; + $tmpArray['object_label_ref'] = rtrim($tmpArray['object_label_ref'], chr(0x0A)); + // @todo + //$tmpArray['actioncom_creation_date'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); + //$tmpArray['average'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); + $tmpArray['project_label'] = $project->ref . ' - ' . $project->title; + $tmpArray['sheet_ref'] = $sheet->ref; + $tmpArray['sheet_label'] = $sheet->label; + + $tmpArray['public_note'] = $object->note_public; - $moreParam['tmparray'] = $tmpArray; - $moreParam['multipleAttendantsSegment'] = ['controller', 'attendant']; + $moreParam['tmparray'] = $tmpArray; return parent::write_file($objectDocument, $outputLangs, $srcTemplatePath, $hideDetails, $hideDesc, $hideRef, $moreParam); } diff --git a/documents/doctemplates/surveydocument/template_surveydocument.odt b/documents/doctemplates/surveydocument/template_surveydocument.odt index f83ae822e72dc382ec0656e0bccaa624689bff28..69c5d348fc46a68cae337adb4679ce0b6d737b51 100644 GIT binary patch delta 16976 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 delta 19275 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 X*!&y*(-IKK(}tpc8UdBH-OBz8OQ}^j diff --git a/documents/doctemplates/surveydocument/template_surveydocument_photo.odt b/documents/doctemplates/surveydocument/template_surveydocument_photo.odt index 2839e662c89950a8950441437469e1d42c858148..36d477d3a8ed2cf20cdbc6d647941ef0d61724b0 100644 GIT binary patch delta 16847 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 z z4zMsav$C-(r~tlfjn0V(d@!LB|Dc2;#(K7$^<-gbV;-hrW>fr&nGX`WGeKJi7KG4p=*dch8Q zezrEj?&d+BZXtnQNq%-oAs+cro<*?$AAkP<|G_e3poHky)WqSve#KiQ(%#8HJ^o)$a=)AC?j^M=N;Pl#* z@b=Vb+$Ejc6Nq^4F@O8$A$D|Cl6#b4(4SJ zmoyKRbgw?H?$p%r^vvn-%E{>3!{Q9!aCz=%aq(<%<6v>; zY;Egcef@HI^LA@*XX@Z-?ci+X^kwt$`)FnKU~~CoX>V`)>hxga{c!sAeD(8T^XTa4 z;OOGy^zi8X^5SUs>iqER{CxlD{_yHy|N8y>>f!S7{r>9o;qK(=?)>xd;Lqdb_vhu^ zUwQiT_~+~6`3~^)`S|tq^|$|je}C&lC#?YiA^w&W6;k=New78Mf+POBcY)Nl`mOaj z-3x=F%+ZN`%o@2C#k&r#-BNc=?g51RBQVM%zBW(*aY%mUofxE^?jlf;OrT%$bO7q8 z+Pzh30GXPrJV7ed*wj|vG>V~3hv{q?-pTcOoN$RLTmW#3|Dfmo1Pnp~`_&uE0Z=tjxr!cLD)30y@ERfeOy{nE))VqEe`;Ust6AzPr1tLM1AAr2>u4oi^R#PzjNFVK z;~djOnI=7IN5g945ypGTRu|u7ba@BNYDo`n0d&ne7+t`fS{f40{n3Fdma}!JW`Py9 zX$-=bhtCtSA0@|W$ietwtN3ecKo>c1#e^PdT?dsHNO>c2s^Xx7|%x2is z%GUo7-HIVqMZ?Uw(hP3Ep!#ZXefu!~!EN~S=H=ds=A?I7%JD2ujjTYmlvk5m!Zixn zBWSY1lw#H52!ZFJaUP|~C`5+b)XR!b3St>X3tX%0jHUF`Q+vaFpX<(gLza7W7!a?> z16;L9Cv~ba^Heupu&weZHmXBP?>T>s6OE0n>a=VF>2oWeUrL^4`0jFTt!L8xCfhy& zq#x)c6HdotexF01?=an=;{B}C^rE^F>A1Qh`5S~GN1s|h=*Z3||C3%xnK8SQ4B?e1 z(wT+R?QP%YnYkY#NrCD?H;Fp28(K64JfH$w4@W`#nSK!x;!@+*W&4UZ> zusaD`I{%$?toDw(+>GhWTcy{1M51^$zcT)8 zXik0(C$o-AUPEblHk42T2?#zhV*k=JTX-CjPG{YM%M)|28M<@};k9M%;Mwe*eBoKL zFXanQ72ibgo}jpniYFi(WeRT zEXKj|;hm})>s_gVv$QN4QLe|WCQfFq6KxqFU%YM z3ZE*%@=9#xC>m)}r^eN}^ws$(e9cA9B4D==&cGc3bo696`bN2m3x@YR>dh`N+Hwp) z=NKs%FdPdC3VJFuwE7*w_iDer(7RA^yAmD$j_y9}lNzeDj_lhU91U?0OoEDxGEWHu zM2Ot72`de!Y#nm9fDbWWX-o_7UsNrjsjKD6Y6gf*3M&`;er6R$b}O+4>rBF$yoc1= zM$DC~(+M2HzXYl5YwOijV6=;nyPJGK1EbdC*LBL~@0T)vg}S^56`9<$(`O5nZB*`T zj@0x=OUua>siSlJP{jC3edi`^fnTtddCVHf|w*{rmc>0f@;aJv`ByCssg%0bNuUl38!VJHKJ1VA;PC%m{g}3M#vAO`6>s$)su3?Ly zI@PK(wo=#1fOyS7Y>vMOK2*w-KX;KEWxR<5<#DHh1)y^EY4@`o65=`qaALphz4*4f zEx?`j9GJ*p@`yt-k5OEx*Jo3?-AeRQ;Un&Z?L~f7+^%2w zvRzR+2E?eJEYbV#b^4D;@dwT%(dvDYDm$4RH+LiEDL2cOcL*XlNg@YKiD{nS=I|q$ z*mfBn6fFNi(|$*>ryXxfC)MF-=4TQ_*A{TLnz6ox#{bx+ay=n#t&8?9yomA}XGcsLA=b#g=F68sjH@7(ORDI9UW%F>F^a%kvm(FlXy>UEqSVC zh!_Mj*378V8M#_@OHJeCFE_L2gUr>dxqez1m$&Gud+<^9kFoxyI;#E?7e||x2>{}l zvfFafaAuK+QiTmdMM~;U=Q=$Yy73>C!YVzX=CLYt!5*u_{hJ_VLJABTm3!eI>V|2? zU7{M%wu;HdN0`!d)N1PY9-C*#kuwst#5Pq5seh=4^{J@)NLNAbE)=uyro=?~YD)d+ zI!+sMn0J(FGBPL0m9O25*Wg)N<^Xbd-IN$25_qf*V$9h1GwT<@1DU$o&jakbnKc%& zvIUOf3%`Q6nj|q$vD{#u;K(DStmLzSUo~tPGGovl;O-~&_Pq5X@Hc=bCpKXBs*Eah zOc~E|IM!i#d=0}1k9+9j8f0n7yJr{6Y3+JNn@398}n zo?SO2#*rllMOtH_q^n-Ix&Z7ncouM;mKE*-Vy{`->?CvcMNwmnGl3)~gsbGD854Ho zzI<3#lPJz0dC=H6IHb zWgj{n*%1Pa`n0_RU8HAH>iNl<+&&QkrOGZXGBho}g5HW37e8{zy8zpUN%hz(;OeOt z@Y{*llQ8GT2AKV^3^vA@DMqkef~6=z>0m!3f~=vgasU3If6hmjlrOV%1z?{`gt-=! zLxRQ2cFdyDq&KmiV=6k+@ zy*k}<dbVq^dQvh6ZDDL(k#ynYO{7^EVsIT_~i%Wc*v% zJ-jPvS?}TUXLh|~SINr*Qug8n&9DNK4*JE%kJdCcc&>ISR)CVujIHvvEx9V-o8@&b z?F1G3f-ES|@}x*y`#{I>6`XvZFui5aDzl`bjddlm5jsW_H_5}-Jy0{r0f%z|&(|0C zqpIYAE_&yqYlKWTSeNT7M0aVEs&@36;{z*!4dDY<9xE;S?M)H#A46F@8JZ|9ifJ@C zLsU$S20@;mUw|Ke`S9G2>+4PdNf&4{AM+Qb8wlbta!MT7W4Ng%y2PyPZmHd@C_U`$ zI?|uP_?xO`r%X%2BX{t|MKJDulkSTZ0(LH$X|?xUwjt2@;3YbIVqT>imXYj=^!@yM zI28Dp49^Gik=Y4RiwDn{x3C@5g;#nN`nplt(yM5%34onR-}T}@Ax1&BcnxJxUMCc8 zr;N8pvJeS!*=7AEYYern(@t=T)VT$Fv8h*YzuB!4z667yteoL4&_bTAnd-_;Cm0=V z$QQ*y1N?-D(P4n~X{B(Vf`F0mBmE?b!!>BdvR|!|7aeyWKdE8g$51qF@giQFMU+*b z295lHLIFV<>R*SGv})afU}kPVa|ABf5^pD*ZlziBi;oeSHi|yWY>Bq`qCy(>glpND zrkuM$*9b_@8(!w+UrrUArDNz6dBGG(tVE;!@scLC7>fP>O#%G>DfIuQj1NPZ?r&lU zVPBwL6p$1`Nl^d$w!AbTl5T@h={bC6W?0E_YcMb@Ks|g1DB(F4@JIlEJUN76V!ln+ z7k+qdUv3N-X)fQ~Mamx#-^+zs;{*&)R1S9N)w%)pT^Pk)=Rb@3JXR{>*d)ut>ijV@ zaJL0nHH)SCjR4)X_MY9_KikgQCU8B|# zh4_^~K*!ek5gh#9m#(Tc`*Jwu$a^)1lofSqb$ax;CzII$N0QCz3cM?C)2veGwi~1k zoB`*Z7v<*73oP)5Xa2lE9=53MnLZOVGvS*t$1Sxs+mFmmc{9B);z3fHgefoiVlfDt z2$w4^15nGT7FnNbUG;{W0JDZU5E0;>gF`?w0K&bwX}@u4x4uoVyyJ94FwWvm4yFgc~%npcSWNA z%7iMj!~bK`f&Vb|6DF0ZRsUi7Z*unEEeifi{`>_dZB1Pa>3`YUC`kXSE&nF=+Zg|c zwFdiNWq^R#5~Njd{uR|q1slW?FQFZeBcV$L7V00-{y|$ym{UOq=nw({*%*4*ySmUB z8yZ`f{+|K<8#9IZ-vU(sEop3T=VEH-^4~4{zadp1pnnkL{v-0tKtL)MuC_*YhL#Ct sseARx5=oclK?A(nt7kCD)(iU{#7{twUp1JDC_VE_OC delta 19051 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 Date: Fri, 12 Jan 2024 12:43:04 +0100 Subject: [PATCH 06/64] #1660 [ODT] add: improve doc_survey --- .../doc_surveydocument_odt.modules.php | 178 ++++++++++-------- .../template_surveydocument.odt | Bin 19804 -> 28293 bytes .../template_surveydocument_photo.odt | Bin 19746 -> 28146 bytes 3 files changed, 95 insertions(+), 83 deletions(-) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index 0d3e055d..da6ccd30 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -24,6 +24,7 @@ // 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'; @@ -80,7 +81,6 @@ public function info(Translate $langs): string return parent::info($langs); } - //@todo /** * Fill all odt tags for segments lines * @@ -109,85 +109,72 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo } catch (OdfException $e) { // We may arrive here if tags for lines not present into template $foundTagForLines = 0; - $listLines = ''; + $listLines = ''; dol_syslog($e->getMessage()); } if ($foundTagForLines) { - if (!empty($object)) { - $sheet = new Sheet($this->db); - - $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet','', '', 'OR', 1, 'sourcetype', 0); - $questionIds = $sheet->linkedObjectsIds; - - if (is_array($questionIds['digiquali_question']) && !empty($questionIds['digiquali_question'])) { - $controldet = new SurveyLine($this->db); - $question = new Question($this->db); - $answer = new Answer($this->db); - foreach ($questionIds['digiquali_question'] as $questionId) { - $question->fetch($questionId); - - $controldets = $controldet->fetchFromParentWithQuestion($object->id, $questionId); - - $tmpArray['ref'] = $question->ref; - $tmpArray['label'] = $question->label; - $tmpArray['description'] = strip_tags($question->description); - - if (is_array($controldets) && !empty($controldets)) { - $questionAnswerLine = array_shift($controldets); - $tmpArray['ref_answer'] = $questionAnswerLine->ref; - $tmpArray['comment'] = dol_htmlentitiesbr_decode(strip_tags($questionAnswerLine->comment, '
')); - - $answerResult = $questionAnswerLine->answer; - - $question->fetch($questionAnswerLine->fk_question); - $answerList = $answer->fetchAll('ASC', 'position', 0, 0, ['fk_question' => $questionAnswerLine->fk_question]); + $sheet = new Sheet($this->db); + $answer = new Answer($this->db); + + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + 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; + } + } - $answersArray = []; - if (is_array($answerList) && !empty($answerList)) { - foreach ($answerList as $answerSingle) { - $answersArray[$answerSingle->position] = $answerSingle->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'] = ''; } - } - switch ($question->type) { - case 'OkKo' : - case 'OkKoToFixNonApplicable' : - case 'UniqueChoice' : - $tmpArray['answer'] = $answersArray[$answerResult]; - break; - case 'Text' : - case 'Range' : - $tmpArray['answer'] = $answerResult; - break; - case 'Percentage' : - $tmpArray['answer'] = $answerResult . ' %'; - break; - case 'MultipleChoices' : - $answers = explode(',', $answerResult); - $tmpArray['answer'] = ''; - foreach ($answers as $answerId) { - $tmpArray['answer'] .= $answersArray[$answerId] . ', '; + $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; } - $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] = $questionAnswerLine->ref; } + $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); } } - $this->setTmpArrayVars($tmpArray, $listLines, $outputLangs); } $odfHandler->mergeSegment($listLines); } @@ -201,7 +188,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo } catch (OdfException $e) { // We may arrive here if tags for lines not present into template $foundTagForLines = 0; - $listLines = ''; + $listLines = ''; dol_syslog($e->getMessage()); } @@ -267,8 +254,9 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $outputLangs->loadLangs(['products', 'bills', 'orders', 'contracts', 'projects', 'companies']); - $sheet = new Sheet($this->db); - $project = new Project($this->db); + $sheet = new Sheet($this->db); + $project = new Project($this->db); + $actionComm = new ActionComm($this->db); $sheet->fetch($object->fk_sheet); $project->fetch($object->projectid); @@ -276,7 +264,9 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $object->fetchObjectLinked('', '', $object->id, 'digiquali_survey', 'OR', 1, 'sourcetype', 0); $linkableElements = get_sheet_linkable_objects(); - if (is_array($linkableElements) && !empty($linkableElements)) { + 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']]; @@ -284,7 +274,7 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa foreach ($object->linkedObjectsIds as $linkedObjectType => $linkedObjectsIds) { $className = $objectInfo[$linkedObjectType]['className']; $linkedObject = new $className($this->db); - $result = $linkedObject->fetch(array_shift($object->linkedObjectsIds[$linkedObjectType])); + $result = $linkedObject->fetch(array_shift($object->linkedObjectsIds[$linkedObjectType])); if ($result > 0) { $objectName = ''; $objectNameField = $nameField[$linkedObjectType]; @@ -298,22 +288,44 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa } else { $objectName = $linkedObject->$objectNameField; } + $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; $tmpArray['object_label_ref'] .= $objectName . chr(0x0A); - $tmpArray['object_type'] = $outputLangs->transnoentities($objectInfo[$linkedObjectType]['title']) . ' : '; } } } - $tmpArray['object_ref'] = $object->ref; - $tmpArray['object_label_ref'] = rtrim($tmpArray['object_label_ref'], chr(0x0A)); - // @todo - //$tmpArray['actioncom_creation_date'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); - //$tmpArray['average'] = dol_print_date($object->date_creation, 'dayhour', 'tzuser'); - $tmpArray['project_label'] = $project->ref . ' - ' . $project->title; - $tmpArray['sheet_ref'] = $sheet->ref; - $tmpArray['sheet_label'] = $sheet->label; + $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->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + + $questionCounter = count($sheet->linkedObjectsIds['digiquali_question']); + + $average = 0; + foreach ($sheet->linkedObjects['digiquali_question'] as $questionLinked) { + if ($questionLinked->type !== 'Percentage') { + continue; // Skip non-percentage questions + } + + foreach ($object->lines as $line) { + if ($line->fk_question === $questionLinked->id) { + $average += $line->answer; + } + } + } + + $average = ($questionCounter > 0) ? ($average / $questionCounter) : 0; - $tmpArray['public_note'] = $object->note_public; + $tmpArray['average'] = price2num($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; diff --git a/documents/doctemplates/surveydocument/template_surveydocument.odt b/documents/doctemplates/surveydocument/template_surveydocument.odt index 69c5d348fc46a68cae337adb4679ce0b6d737b51..2e21fa4995c562cc77c904d0de099da76809b30d 100644 GIT binary patch delta 26039 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 WGs}}<;= delta 17436 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 From 90d107af8fa5905e400dfff8059f4186f3317953 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 18:34:10 +0100 Subject: [PATCH 07/64] #1660 [ODT] fix: missing survey document conf and clean --- .../surveydocument/doc_surveydocument_odt.modules.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index da6ccd30..e4298201 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -81,6 +81,7 @@ public function info(Translate $langs): string return parent::info($langs); } + //@todo /** * Fill all odt tags for segments lines * @@ -223,6 +224,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo return 0; } + //@todo /** * Function to build a document on disk * From 69d842c6848c207470c4f3aaafb1feca898ed051 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Thu, 11 Jan 2024 19:40:48 +0100 Subject: [PATCH 08/64] #1660 [ODT] add: ODT v1.1 and doc_survey --- .../surveydocument/doc_surveydocument_odt.modules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index e4298201..ceec858d 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -224,7 +224,6 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo return 0; } - //@todo /** * Function to build a document on disk * From d362a7e3277602b5da75296c82a85030173f464f Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 12 Jan 2024 12:43:04 +0100 Subject: [PATCH 09/64] #1660 [ODT] add: improve doc_survey --- .../surveydocument/doc_surveydocument_odt.modules.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index ceec858d..da6ccd30 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -81,7 +81,6 @@ public function info(Translate $langs): string return parent::info($langs); } - //@todo /** * Fill all odt tags for segments lines * From 0e4437aae3f79b85dab9e35403ac09213e825759 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Mon, 8 Jan 2024 15:55:33 +0100 Subject: [PATCH 10/64] #1661 [Class] add: survey --- class/actions_digiquali.class.php | 9 +- class/survey.class.php | 981 ++++++++++++++++++ core/modules/digiquali/survey/index.php | 2 + .../digiquali/survey/mod_survey_standard.php | 42 + core/modules/digiquali/surveydet/index.php | 2 + .../surveydet/mod_surveydet_standard.php | 42 + core/modules/modDigiQuali.class.php | 97 +- 7 files changed, 1161 insertions(+), 14 deletions(-) create mode 100644 class/survey.class.php create mode 100644 core/modules/digiquali/survey/index.php create mode 100644 core/modules/digiquali/survey/mod_survey_standard.php create mode 100644 core/modules/digiquali/surveydet/index.php create mode 100644 core/modules/digiquali/surveydet/mod_surveydet_standard.php diff --git a/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index 7da1b46c..31b84641 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) { diff --git a/class/survey.class.php b/class/survey.class.php new file mode 100644 index 00000000..dd645fae --- /dev/null +++ b/class/survey.class.php @@ -0,0 +1,981 @@ + + * + * 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], + 'track_id' => ['type' => 'text', 'label' => 'TrackID', '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_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 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; + + /** + * 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; + } + + // @todo pas fait + /** + * 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 + { + // Protection + if ($this->status <= self::STATUS_DRAFT) { + return 0; + } + + $signatory = new SaturneSignature($this->db); + $signatory->deleteSignatoriesSignatures($this->id, 'control'); + + return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'CONTROL_UNVALIDATE'); + } + + /** + * 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; + } + } + + // @todo pas fait + /** + * 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 (property_exists($object, 'verdict')) { + $object->verdict = 0; + } + if (empty($options['photos'])) { + $object->photo = ''; + } + if (property_exists($object, 'control_date')) { + $object->control_date = ''; + } + if (property_exists($object, 'next_control_date')) { + $object->next_control_date = ''; + } + + $object->context = 'createfromclone'; + + $object->fetchObjectLinked('','', $object->id, 'digiquali_' . $object->element, 'OR', 1, 'sourcetype', 0); + + $controlID = $object->create($user); + + if ($controlID > 0) { + $objectFromClone = new self($this->db); + $objectFromClone->fetch($controlID); + + // Categories. + $cat = new Categorie($this->db); + $categories = $cat->containing($fromID, 'control'); + 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, $controlID); + } + } + } + } else { + $signatory->setSignatory($objectFromClone->id, $this->element, 'user', [$objectFromClone->fk_user_controller], 'Controller', 1); + } + + // Add Photos. + if (!empty($options['photos'])) { + $dir = $conf->digiquali->multidir_output[$conf->entity] . '/control'; + $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 $controlID; + } else { + $this->db->rollback(); + return -1; + } + } + + // @todo pas fait + /** + * 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(); + } + + /** + * Create an array of lines + * + * @return array|int array of lines if OK, < 0 if KO + */ + public function getLinesArray() + { + $this->lines = []; + + $objectLine = new SurveyLine($this->db); + $result = $objectLine->fetchAll('ASC', 'position', 0, 0, ['customsql' => 'fk_survey = ' . $this->id]); + + if (is_numeric($result)) { + $this->error = $objectLine->error; + $this->errors = $objectLine->errors; + return $result; + } else { + $this->lines = $result; + return $this->lines; + } + } + + // @todo pas fait + /** + * Load dashboard info + * + * @return array + * @throws Exception + */ + public function load_dashboard(): array + { + $getNbControlsTagsByVerdict = $this->getNbControlsTagsByVerdict(); + $getNbControlsByVerdict = $this->getNbControlsByVerdict(); + $getNbControlsByMonth = $this->getNbControlsByMonth(); + $getControlListsByNextControl = $this->getControlListsByNextControl(); + + $array['graphs'] = [$getNbControlsTagsByVerdict, $getNbControlsByVerdict, $getNbControlsByMonth]; + $array['lists'] = [$getControlListsByNextControl]; + + return $array; + } + + // @todo pas fait + /** + * Get controls by verdict. + * + * @return array Graph datas (label/color/type/title/data etc..). + * @throws Exception + */ + public function getNbControlsByVerdict(): array + { + global $langs; + + // Graph Title parameters. + $array['title'] = $langs->transnoentities('ControlsRepartition'); + $array['picto'] = $this->picto; + + // Graph parameters. + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'pie'; + $array['dataset'] = 1; + + $array['labels'] = [ + 0 => [ + 'label' => 'N/A', + 'color' => '#999999' + ], + 1 => [ + 'label' => $langs->transnoentities('OK'), + 'color' => '#47e58e' + ], + 2 => [ + 'label' => $langs->transnoentities('KO'), + 'color' => '#e05353' + ], + ]; + + $arrayNbControlByVerdict = [0 => 0, 1 => 0, 2 => 0]; + $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 't.status >= 0']); + if (is_array($controls) && !empty($controls)) { + foreach ($controls as $control) { + if (empty($control->verdict)) { + $arrayNbControlByVerdict[0]++; + } else { + $arrayNbControlByVerdict[$control->verdict]++; + } + } + ksort($arrayNbControlByVerdict); + } + + $array['data'] = $arrayNbControlByVerdict; + + return $array; + } + + // @todo pas fait + /** + * Get controls with tags by verdict. + * + * @return array Graph datas (label/color/type/title/data etc..). + * @throws Exception + */ + public function getNbControlsTagsByVerdict(): array + { + global $db, $langs; + + require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; + + $category = new Categorie($db); + + // Graph Title parameters. + $array['title'] = $langs->transnoentities('ControlsTagsRepartition'); + $array['picto'] = $this->picto; + + // Graph parameters. + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'bar'; + $array['dataset'] = 3; + + $array['labels'] = [ + 0 => [ + 'label' => 'N/A', + 'color' => '#999999' + ], + 1 => [ + 'label' => $langs->transnoentities('OK'), + 'color' => '#47e58e' + ], + 2 => [ + 'label' => $langs->transnoentities('KO'), + 'color' => '#e05353' + ] + ]; + + $categories = $category->get_all_categories('control'); + if (is_array($categories) && !empty($categories)) { + foreach ($categories as $category) { + $arrayNbControlByVerdict = []; + $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 'cp.fk_categorie = ' . $category->id . ' AND t.status >= 0'], 'AND', true); + if (is_array($controls) && !empty($controls)) { + foreach ($controls as $control) { + if (empty($control->verdict)) { + $arrayNbControlByVerdict[0]++; + } else { + $arrayNbControlByVerdict[$control->verdict]++; + } + } + $array['data'][] = [$category->label, $arrayNbControlByVerdict[0], $arrayNbControlByVerdict[1], $arrayNbControlByVerdict[2]]; + } + } + } + + return $array; + } + + // @todo pas fait + /** + * Get controls by month. + * + * @return array Graph datas (label/color/type/title/data etc..). + * @throws Exception + */ + public function getNbControlsByMonth(): 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('ControlsByFiscalYear'); + $array['picto'] = $this->picto; + + // Graph parameters. + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'bars'; + $array['dataset'] = 3; + + $array['labels'] = [ + 0 => [ + 'label' => $langs->trans("$years[0]"), + 'color' => '#9567AA' + ], + 1 => [ + 'label' => $langs->trans("$years[1]"), + 'color' => '#4F9EBE' + ], + 2 => [ + 'label' => $langs->trans("$years[2]"), + 'color' => '#FAC461' + ] + ]; + + $arrayNbControls = []; + for ($i = 1; $i < 13; $i++) { + foreach ($years as $key => $year) { + $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 'MONTH (t.date_creation) = ' . $i . ' AND YEAR (t.date_creation) = ' . $year . ' AND t.status >= 0']); + if (is_array($controls) && !empty($controls)) { + $arrayNbControls[$key][$i] = count($controls); + } + } + + $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf('%02d', $i)); + $arrayKey = $i - $startMonth; + $arrayKey = $arrayKey >= 0 ? $arrayKey : $arrayKey + 12; + $array['data'][$arrayKey] = [$month, $arrayNbControls[0][$i], $arrayNbControls[1][$i], $arrayNbControls[2][$i]]; + } + ksort($array['data']); + + return $array; + } + + // @todo pas fait + /** + * Get controls list by next control. + * + * @return array Graph datas (label/color/type/title/data etc..). + * @throws Exception + */ + public function getControlListsByNextControl(): array + { + global $langs; + + // Graph Title parameters. + $array['title'] = $langs->transnoentities('ControlListsByNextControl'); + $array['picto'] = $this->picto; + + // Graph parameters. + $array['type'] = 'list'; + $array['labels'] = ['Ref', 'LinkedObject', 'Controller', 'Project', 'Sheet', 'ControlDate', 'NextControl', 'Verdict']; + + $arrayControlListsByNextControl = []; + + $elementArray = get_sheet_linkable_objects(); + $controls = $this->fetchAll('ASC', 'next_control_date', 10, 0, ['customsql' => 't.status = ' . self::STATUS_LOCKED . ' AND t.next_control_date IS NOT NULL']); + if (is_array($controls) && !empty($controls)) { + foreach ($controls as $control) { + $control->fetchObjectLinked('', '', $control->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); + $linkedObjectsInfos = $control->getLinkedObjectsWithQcFrequency($elementArray); + $linkedObjects = $linkedObjectsInfos['linkedObjects']; + $qcFrequencyArray = $linkedObjectsInfos['qcFrequencyArray']; + foreach ($elementArray as $linkableObjectType => $linkableObject) { + if (is_object($linkedObjects[$linkableObjectType])) { + if ($linkableObject['conf'] > 0 && (!empty($control->linkedObjectsIds[$linkableObject['link_name']]))) { + $currentObject = $linkedObjects[$linkableObjectType]; + if ($qcFrequencyArray[$linkableObjectType] > 0) { + require_once __DIR__ . '/sheet.class.php'; + + $userTmp = new User($this->db); + $project = new Project($this->db); + $sheet = new Sheet($this->db); + + $userTmp->fetch($control->fk_user_controller); + $project->fetch($control->projectid); + $sheet->fetch($control->fk_sheet); + + if (!empty($control->next_control_date)) { + $nextControl = floor(($control->next_control_date - dol_now('tzuser'))/(3600 * 24)); + $nextControlColor = $nextControl < 0 ? 'red' : ($nextControl <= 30 ? 'orange' : ($nextControl <= 60 ? 'yellow' : 'green')); + + $verdictColor = $control->verdict == 1 ? 'green' : ($control->verdict == 2 ? 'red' : 'grey'); + + $arrayControlListsByNextControl[$control->id]['Ref']['value'] = $control->getNomUrl(1); + $arrayControlListsByNextControl[$control->id]['LinkedObject']['value'] = $currentObject->getNomUrl(1); + $arrayControlListsByNextControl[$control->id]['UserController']['value'] = $userTmp->getNomUrl(1); + $arrayControlListsByNextControl[$control->id]['Project']['value'] = $project->id > 0 ? $project->getNomUrl(1) : ''; + $arrayControlListsByNextControl[$control->id]['Sheet']['value'] = $sheet->getNomUrl(1); + $arrayControlListsByNextControl[$control->id]['ControlDate']['value'] = dol_print_date($control->date_creation, 'day'); + $arrayControlListsByNextControl[$control->id]['NextControl']['value'] = '
' . $nextControl . '
' . $langs->trans('Days') . '
'; + $arrayControlListsByNextControl[$control->id]['NextControl']['morecss'] = 'dashboard-control'; + $arrayControlListsByNextControl[$control->id]['Verdict']['value'] = '
' . $control->fields['verdict']['arrayofkeyval'][(!empty($control->verdict)) ?: 3] . '
'; + $arrayControlListsByNextControl[$control->id]['Verdict']['morecss'] = 'dashboard-control'; + } + } + } + } + } + } + } + $array['data'] = $arrayControlListsByNextControl; + + 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 SurveyLine extends SaturneObject +{ + /** + * @var string Module name + */ + public $module = 'digiquali'; + + /** + * @var string ID to identify managed object + */ + public $element = 'surveydet'; + + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'digiquali_surveydet'; + + 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_survey' => array('type' => 'integer', 'label' => 'FkControl', 'enabled' => '1', 'position' => 100, 'notnull' => 1, 'visible' => 0,), + ); + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + public function __construct(DoliDB $db) + { + parent::__construct($db, $this->module, $this->element); + } + + // @todo pas fait + /** + * 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; + } + } + + // @todo pas fait + /** + * 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; + + + $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; + } + + } +} 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 dac0b3bd..f214ee63 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -218,6 +218,9 @@ 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'], + // CONST DIGIQUALI DOCUMENTS $i++ => ['DIGIQUALI_AUTOMATIC_PDF_GENERATION', 'integer', 0, '', 0, 'current'], $i++ => ['DIGIQUALI_MANUAL_PDF_GENERATION', 'integer', 0, '', 0, 'current'], @@ -231,6 +234,13 @@ public function __construct($db) //$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'], @@ -238,6 +248,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'], @@ -298,51 +312,60 @@ public function __construct($db) } } - // 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 ] @@ -427,6 +450,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',$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', $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', $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'); @@ -552,6 +592,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', From f23bc555f89565153a3b7544cc5eb8fcd8179c8c Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 19:27:52 +0100 Subject: [PATCH 11/64] #1661 [Class] add: dashbord/conf/hook --- class/actions_digiquali.class.php | 6 +- class/digiqualidashboard.class.php | 17 +- class/survey.class.php | 647 ++++++++++------------------ core/modules/modDigiQuali.class.php | 8 +- 4 files changed, 244 insertions(+), 434 deletions(-) diff --git a/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index 31b84641..dbca3748 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -298,9 +298,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 = ''; } @@ -322,7 +322,7 @@ 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 (preg_match('/publiccontrol|publicsurvey|publicanswer|publiccontrolhistory/', $parameters['context'])) { if (!empty($conf->global->SATURNE_SHOW_COMPANY_LOGO)) { // Define logo and logoSmall. $logoSmall = $mysoc->logo_small; diff --git a/class/digiqualidashboard.class.php b/class/digiqualidashboard.class.php index ee5605b1..560cbae7 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/survey.class.php b/class/survey.class.php index dd645fae..e43fb68f 100644 --- a/class/survey.class.php +++ b/class/survey.class.php @@ -209,6 +209,16 @@ class Survey extends SaturneObject */ public $projectid; + /** + * @var string Name of subtable line + */ + public $table_element_line = 'digiquali_surveydet'; + + /** + * @var SurveyLine[] Array of subtable lines + */ + public $lines = []; + /** * Constructor * @@ -251,7 +261,6 @@ public function create(User $user, bool $notrigger = false): int return $result; } - // @todo pas fait /** * Set draft status * @@ -262,15 +271,9 @@ public function create(User $user, bool $notrigger = false): int */ public function setDraft(User $user, int $notrigger = 0): int { - // Protection - if ($this->status <= self::STATUS_DRAFT) { - return 0; - } - - $signatory = new SaturneSignature($this->db); - $signatory->deleteSignatoriesSignatures($this->id, 'control'); - - return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'CONTROL_UNVALIDATE'); + $signatory = new SaturneSignature($this->db, $this->module, $this->element); + $signatory->deleteSignatoriesSignatures($this->id, $this->element); + return parent::setDraft($user, $notrigger); } /** @@ -316,14 +319,13 @@ public function isLinkedToOtherObjects(): int } } - // @todo pas fait /** - * Clone an object into another one. + * 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. + * @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 @@ -337,19 +339,19 @@ public function createFromClone(User $user, int $fromID, array $options): int $object = new self($this->db); $this->db->begin(); - // Load source object. + // Load source object $result = $object->fetchCommon($fromID); - if ($result > 0 && ! empty($object->table_element_line)) { + if ($result > 0 && !empty($object->table_element_line)) { $object->fetchLines(); } $objectRef = $object->ref; - // Reset some properties. + // Reset some properties unset($object->fk_user_creat); unset($object->import_key); - // Clear fields. + // Clear fields if (property_exists($object, 'ref')) { $object->ref = ''; } @@ -359,32 +361,22 @@ public function createFromClone(User $user, int $fromID, array $options): int if (property_exists($object, 'status')) { $object->status = 0; } - if (property_exists($object, 'verdict')) { - $object->verdict = 0; - } if (empty($options['photos'])) { $object->photo = ''; } - if (property_exists($object, 'control_date')) { - $object->control_date = ''; - } - if (property_exists($object, 'next_control_date')) { - $object->next_control_date = ''; - } $object->context = 'createfromclone'; $object->fetchObjectLinked('','', $object->id, 'digiquali_' . $object->element, 'OR', 1, 'sourcetype', 0); - $controlID = $object->create($user); - - if ($controlID > 0) { + $surveyID = $object->create($user); + if ($surveyID > 0) { $objectFromClone = new self($this->db); - $objectFromClone->fetch($controlID); + $objectFromClone->fetch($surveyID); - // Categories. + // Categories $cat = new Categorie($this->db); - $categories = $cat->containing($fromID, 'control'); + $categories = $cat->containing($fromID, 'survey'); if (is_array($categories) && !empty($categories)) { foreach($categories as $cat) { $categoryIds[] = $cat->id; @@ -392,38 +384,35 @@ public function createFromClone(User $user, int $fromID, array $options): int $object->setCategories($categoryIds); } - // Add objects linked. - $linkableElements = get_sheet_linkable_objects(); - - if (!empty($linkableElements)) { - foreach($linkableElements as $linkableElement) { + // 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. + 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. + // 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, $controlID); + $signatory->createFromClone($user, $signatoryRole->id, $surveyID); } } } - } else { - $signatory->setSignatory($objectFromClone->id, $this->element, 'user', [$objectFromClone->fk_user_controller], 'Controller', 1); } - // Add Photos. + // Add Photos if (!empty($options['photos'])) { - $dir = $conf->digiquali->multidir_output[$conf->entity] . '/control'; + $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); @@ -434,17 +423,16 @@ public function createFromClone(User $user, int $fromID, array $options): int $this->errors = $object->errors; } - // End. + // End if (!$error) { $this->db->commit(); - return $controlID; + return $surveyID; } else { $this->db->rollback(); return -1; } } - // @todo pas fait /** * Return the status * @@ -498,29 +486,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 SurveyLine($this->db); - $result = $objectLine->fetchAll('ASC', 'position', 0, 0, ['customsql' => 'fk_survey = ' . $this->id]); - - if (is_numeric($result)) { - $this->error = $objectLine->error; - $this->errors = $objectLine->errors; - return $result; - } else { - $this->lines = $result; - return $this->lines; - } - } - - // @todo pas fait /** * Load dashboard info * @@ -529,140 +494,20 @@ public function getLinesArray() */ public function load_dashboard(): array { - $getNbControlsTagsByVerdict = $this->getNbControlsTagsByVerdict(); - $getNbControlsByVerdict = $this->getNbControlsByVerdict(); - $getNbControlsByMonth = $this->getNbControlsByMonth(); - $getControlListsByNextControl = $this->getControlListsByNextControl(); + $getNbSurveysByMonth = $this->getNbSurveysByMonth(); - $array['graphs'] = [$getNbControlsTagsByVerdict, $getNbControlsByVerdict, $getNbControlsByMonth]; - $array['lists'] = [$getControlListsByNextControl]; + $array['graphs'] = [$getNbSurveysByMonth]; return $array; } - // @todo pas fait /** - * Get controls by verdict. + * Get surveys by month * - * @return array Graph datas (label/color/type/title/data etc..). + * @return array Graph datas (label/color/type/title/data etc..) * @throws Exception */ - public function getNbControlsByVerdict(): array - { - global $langs; - - // Graph Title parameters. - $array['title'] = $langs->transnoentities('ControlsRepartition'); - $array['picto'] = $this->picto; - - // Graph parameters. - $array['width'] = '100%'; - $array['height'] = 400; - $array['type'] = 'pie'; - $array['dataset'] = 1; - - $array['labels'] = [ - 0 => [ - 'label' => 'N/A', - 'color' => '#999999' - ], - 1 => [ - 'label' => $langs->transnoentities('OK'), - 'color' => '#47e58e' - ], - 2 => [ - 'label' => $langs->transnoentities('KO'), - 'color' => '#e05353' - ], - ]; - - $arrayNbControlByVerdict = [0 => 0, 1 => 0, 2 => 0]; - $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 't.status >= 0']); - if (is_array($controls) && !empty($controls)) { - foreach ($controls as $control) { - if (empty($control->verdict)) { - $arrayNbControlByVerdict[0]++; - } else { - $arrayNbControlByVerdict[$control->verdict]++; - } - } - ksort($arrayNbControlByVerdict); - } - - $array['data'] = $arrayNbControlByVerdict; - - return $array; - } - - // @todo pas fait - /** - * Get controls with tags by verdict. - * - * @return array Graph datas (label/color/type/title/data etc..). - * @throws Exception - */ - public function getNbControlsTagsByVerdict(): array - { - global $db, $langs; - - require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; - - $category = new Categorie($db); - - // Graph Title parameters. - $array['title'] = $langs->transnoentities('ControlsTagsRepartition'); - $array['picto'] = $this->picto; - - // Graph parameters. - $array['width'] = '100%'; - $array['height'] = 400; - $array['type'] = 'bar'; - $array['dataset'] = 3; - - $array['labels'] = [ - 0 => [ - 'label' => 'N/A', - 'color' => '#999999' - ], - 1 => [ - 'label' => $langs->transnoentities('OK'), - 'color' => '#47e58e' - ], - 2 => [ - 'label' => $langs->transnoentities('KO'), - 'color' => '#e05353' - ] - ]; - - $categories = $category->get_all_categories('control'); - if (is_array($categories) && !empty($categories)) { - foreach ($categories as $category) { - $arrayNbControlByVerdict = []; - $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 'cp.fk_categorie = ' . $category->id . ' AND t.status >= 0'], 'AND', true); - if (is_array($controls) && !empty($controls)) { - foreach ($controls as $control) { - if (empty($control->verdict)) { - $arrayNbControlByVerdict[0]++; - } else { - $arrayNbControlByVerdict[$control->verdict]++; - } - } - $array['data'][] = [$category->label, $arrayNbControlByVerdict[0], $arrayNbControlByVerdict[1], $arrayNbControlByVerdict[2]]; - } - } - } - - return $array; - } - - // @todo pas fait - /** - * Get controls by month. - * - * @return array Graph datas (label/color/type/title/data etc..). - * @throws Exception - */ - public function getNbControlsByMonth(): array + public function getNbSurveysByMonth(): array { global $conf, $langs; @@ -670,122 +515,51 @@ public function getNbControlsByMonth(): array $currentYear = date('Y', dol_now()); $years = [0 => $currentYear - 2, 1 => $currentYear - 1, 2 => $currentYear]; - // Graph Title parameters. - $array['title'] = $langs->transnoentities('ControlsByFiscalYear'); + // Graph Title parameters + $array['title'] = $langs->transnoentities('SurveysByFiscalYear'); $array['picto'] = $this->picto; - // Graph parameters. - $array['width'] = '100%'; - $array['height'] = 400; - $array['type'] = 'bars'; - $array['dataset'] = 3; + // Graph parameters + $array['width'] = '100%'; + $array['height'] = 400; + $array['type'] = 'bars'; + $array['showlegend'] = 1; + $array['dataset'] = 3; $array['labels'] = [ 0 => [ - 'label' => $langs->trans("$years[0]"), + 'label' => $langs->trans($years[0]), 'color' => '#9567AA' ], 1 => [ - 'label' => $langs->trans("$years[1]"), + 'label' => $langs->trans($years[1]), 'color' => '#4F9EBE' ], 2 => [ - 'label' => $langs->trans("$years[2]"), + 'label' => $langs->trans($years[2]), 'color' => '#FAC461' ] ]; - $arrayNbControls = []; + $arrayNbSurveys = []; for ($i = 1; $i < 13; $i++) { foreach ($years as $key => $year) { - $controls = $this->fetchAll('', '', 0, 0, ['customsql' => 'MONTH (t.date_creation) = ' . $i . ' AND YEAR (t.date_creation) = ' . $year . ' AND t.status >= 0']); - if (is_array($controls) && !empty($controls)) { - $arrayNbControls[$key][$i] = count($controls); + $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)); + $month = $langs->transnoentitiesnoconv('MonthShort' . sprintf('%02d', $i)); $arrayKey = $i - $startMonth; $arrayKey = $arrayKey >= 0 ? $arrayKey : $arrayKey + 12; - $array['data'][$arrayKey] = [$month, $arrayNbControls[0][$i], $arrayNbControls[1][$i], $arrayNbControls[2][$i]]; + $array['data'][$arrayKey] = [$month, $arrayNbSurveys[0][$i], $arrayNbSurveys[1][$i], $arrayNbSurveys[2][$i]]; } ksort($array['data']); return $array; } - // @todo pas fait - /** - * Get controls list by next control. - * - * @return array Graph datas (label/color/type/title/data etc..). - * @throws Exception - */ - public function getControlListsByNextControl(): array - { - global $langs; - - // Graph Title parameters. - $array['title'] = $langs->transnoentities('ControlListsByNextControl'); - $array['picto'] = $this->picto; - - // Graph parameters. - $array['type'] = 'list'; - $array['labels'] = ['Ref', 'LinkedObject', 'Controller', 'Project', 'Sheet', 'ControlDate', 'NextControl', 'Verdict']; - - $arrayControlListsByNextControl = []; - - $elementArray = get_sheet_linkable_objects(); - $controls = $this->fetchAll('ASC', 'next_control_date', 10, 0, ['customsql' => 't.status = ' . self::STATUS_LOCKED . ' AND t.next_control_date IS NOT NULL']); - if (is_array($controls) && !empty($controls)) { - foreach ($controls as $control) { - $control->fetchObjectLinked('', '', $control->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); - $linkedObjectsInfos = $control->getLinkedObjectsWithQcFrequency($elementArray); - $linkedObjects = $linkedObjectsInfos['linkedObjects']; - $qcFrequencyArray = $linkedObjectsInfos['qcFrequencyArray']; - foreach ($elementArray as $linkableObjectType => $linkableObject) { - if (is_object($linkedObjects[$linkableObjectType])) { - if ($linkableObject['conf'] > 0 && (!empty($control->linkedObjectsIds[$linkableObject['link_name']]))) { - $currentObject = $linkedObjects[$linkableObjectType]; - if ($qcFrequencyArray[$linkableObjectType] > 0) { - require_once __DIR__ . '/sheet.class.php'; - - $userTmp = new User($this->db); - $project = new Project($this->db); - $sheet = new Sheet($this->db); - - $userTmp->fetch($control->fk_user_controller); - $project->fetch($control->projectid); - $sheet->fetch($control->fk_sheet); - - if (!empty($control->next_control_date)) { - $nextControl = floor(($control->next_control_date - dol_now('tzuser'))/(3600 * 24)); - $nextControlColor = $nextControl < 0 ? 'red' : ($nextControl <= 30 ? 'orange' : ($nextControl <= 60 ? 'yellow' : 'green')); - - $verdictColor = $control->verdict == 1 ? 'green' : ($control->verdict == 2 ? 'red' : 'grey'); - - $arrayControlListsByNextControl[$control->id]['Ref']['value'] = $control->getNomUrl(1); - $arrayControlListsByNextControl[$control->id]['LinkedObject']['value'] = $currentObject->getNomUrl(1); - $arrayControlListsByNextControl[$control->id]['UserController']['value'] = $userTmp->getNomUrl(1); - $arrayControlListsByNextControl[$control->id]['Project']['value'] = $project->id > 0 ? $project->getNomUrl(1) : ''; - $arrayControlListsByNextControl[$control->id]['Sheet']['value'] = $sheet->getNomUrl(1); - $arrayControlListsByNextControl[$control->id]['ControlDate']['value'] = dol_print_date($control->date_creation, 'day'); - $arrayControlListsByNextControl[$control->id]['NextControl']['value'] = '
' . $nextControl . '
' . $langs->trans('Days') . '
'; - $arrayControlListsByNextControl[$control->id]['NextControl']['morecss'] = 'dashboard-control'; - $arrayControlListsByNextControl[$control->id]['Verdict']['value'] = '
' . $control->fields['verdict']['arrayofkeyval'][(!empty($control->verdict)) ?: 3] . '
'; - $arrayControlListsByNextControl[$control->id]['Verdict']['morecss'] = 'dashboard-control'; - } - } - } - } - } - } - } - $array['data'] = $arrayControlListsByNextControl; - - return $array; - } - /** * Write information of trigger description * @@ -816,6 +590,9 @@ public function getTriggerDescription(SaturneObject $object): string } } +/** + * Class for SurveyLine + */ class SurveyLine extends SaturneObject { /** @@ -824,46 +601,165 @@ class SurveyLine extends SaturneObject public $module = 'digiquali'; /** - * @var string ID to identify managed object + * @var string Element type of object */ public $element = 'surveydet'; /** - * @var string Name of table without prefix where object is stored + * @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'; - public $ref = ''; + /** + * @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 + */ - public $date_creation = ''; + /** + * @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'], + ]; - public $comment = ''; + /** + * @var int ID + */ + public int $rowid; - public $answer = ''; + /** + * @var string Ref + */ + public $ref; - public $answer_photo = ''; + /** + * @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; - public $fk_control = ''; + /** + * @var string|null Answer + */ + public ?string $answer = ''; - public $fk_question = ''; + /** + * @var string|null Answer photo + */ + public ?string $answer_photo; - /** - * @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_survey' => array('type' => 'integer', 'label' => 'FkControl', 'enabled' => '1', 'position' => 100, 'notnull' => 1, 'visible' => 0,), - ); + /** + * @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 @@ -875,107 +771,16 @@ public function __construct(DoliDB $db) parent::__construct($db, $this->module, $this->element); } - // @todo pas fait - /** - * 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; - } - } - - // @todo pas fait - /** - * 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; - - - $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 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/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index f214ee63..134e7aca 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -220,6 +220,7 @@ public function __construct($db) // 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'], @@ -306,6 +307,7 @@ 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']; @@ -452,17 +454,17 @@ public function __construct($db) /* 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',$langs->transnoentities('Surveys')); // Permission label + $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', $langs->transnoentities('Surveys')); // Permission label + $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', $langs->transnoentities('Surveys')); // Permission label + $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++; From 748c0efad37cb05fcd12c855721e0b4e490ce38f Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Mon, 8 Jan 2024 15:57:42 +0100 Subject: [PATCH 12/64] #1662 [Survey] add: view/langs/trigger --- ...9_modDigiQuali_DigiQualiTriggers.class.php | 7 + langs/fr_FR/digiquali.lang | 57 + lib/digiquali.lib.php | 5 + lib/digiquali_survey.lib.php | 47 + view/survey/index.php | 2 + view/survey/survey_card.php | 1032 +++++++++++++++++ view/survey/survey_list.php | 368 ++++++ view/survey/survey_medias.php | 157 +++ 8 files changed, 1675 insertions(+) create mode 100644 lib/digiquali_survey.lib.php create mode 100644 view/survey/index.php create mode 100644 view/survey/survey_card.php create mode 100644 view/survey/survey_list.php create mode 100644 view/survey/survey_medias.php diff --git a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php index c539cf99..f2d2fbc9 100644 --- a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php +++ b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php @@ -166,6 +166,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf 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 +183,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,6 +200,7 @@ 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); @@ -296,6 +299,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') . '
'; @@ -309,6 +313,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf break; case 'CONTROL_SAVEANSWER' : + case 'SURVEY_SAVEANSWER' : $actioncomm->code = 'AC_' . strtoupper($object->element) . 'SAVEANSWER'; $actioncomm->label = $langs->transnoentities('AnswerSaveTrigger'); $actioncomm->create($user); @@ -321,12 +326,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/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 8b038c24..cf4c83f9 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -360,6 +360,63 @@ controldocument_photo.odt = Fiche de Contrôle avec photos controldocument_photo = fiche de controle avec photos + +# +# Survey - Questionnaire +# + +# Data - Donnée +Survey = Questionnaire +SurveysMin = contrôles +TheSurvey = le contrôle +CreateControl = Créer un contrôle +AddControl = Ajouter un contrôle +NewSurvey = Nouveau contrôle +ControlList = Liste des contrôles +Controls = Contrôles +ActionsOnControl = Événements liés au contrôle +SurveysCategoriesArea = Espace des tags/catégories des contrôles +ControlsRepartition = Répartition des contrôles par verdict +ControlsTagsRepartition = Répartition des contrôles par verdict et par catégories +ControlsByFiscalYear = Rapport des contrôles sur l'exercice fiscal +OfControl = du contrôle +PublicControl = Interface publique de 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 +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 +NoProductLot = Pas de numéro de lot/série +ErrorEquipmentLink = L'équipement n'a pas pu être lié +LastControl = Dernier contrôle +ShowObjectControlHistory = Voir l'historique de contrôle +NoControlOnThisObject = Le contrôle initial n'a pas été réalisé, veuillez vous référer à la notice et contacter votre responsable qualité +ControlHistoryLink = Historique de contrôle +EnablePublicControlHistory = Afficher l'historique public de contrôle +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 +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 + +# +# SurveyDocument - Fiche du Questionnaire +# + +# Data - Donnée +SurveyDocument = Fiche du Questionnaire +Surveydocument = Fiche du Questionnaire +SurveyDocumentsMin = fiches du questionnaire +surveydocument = fiche du questionnaire +surveydocument.odt = Fiche du Questionnaire +surveydocument_photo.odt = Fiche du Questionnaire avec photos +surveydocument_photo = fiche du questionnaire avec photos + + + # # Tools - Outils # diff --git a/lib/digiquali.lib.php b/lib/digiquali.lib.php index b71bb93d..978106a7 100644 --- a/lib/digiquali.lib.php +++ b/lib/digiquali.lib.php @@ -58,6 +58,11 @@ function digiquali_admin_prepare_head(): array $head[$h][2] = 'control'; $h++; + $head[$h][0] = dol_buildpath('/saturne/admin/object.php', 1) . '?module_name=DigiQuali&object_type=survey'; + $head[$h][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('Survey') : ''; + $head[$h][2] = 'survey'; + $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_survey.lib.php b/lib/digiquali_survey.lib.php new file mode 100644 index 00000000..eb6ed6f4 --- /dev/null +++ b/lib/digiquali_survey.lib.php @@ -0,0 +1,47 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file lib/digiquali_survey.lib.php + * \ingroup digiquali + * \brief Library files with common functions for Survey + */ + +// Load Saturne libraries +require_once __DIR__ . '/../../saturne/lib/object.lib.php'; + +/** + * Prepare array of tabs for survey + * + * @param Survey $object Survey object + * @return array Array of tabs + * @throws Exception + */ +function survey_prepare_head(Survey $object): array +{ + // Global variables definitions + global $conf, $langs; + + $head[1][0] = dol_buildpath('/digiquali/view/survey/survey_medias.php', 1) . '?id=' . $object->id; + $head[1][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('Medias') : ''; + $head[1][2] = 'medias'; + + $moreparam['documentType'] = 'SurveyDocument'; + $moreparam['attendantTableMode'] = 'simple'; + + return saturne_object_prepare_head($object, $head, $moreparam, true); +} diff --git a/view/survey/index.php b/view/survey/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/view/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 . + */ + +/** + * \file view/survey/survey_card.php + * \ingroup digiquali + * \brief Page to create/edit/view survey + */ + +// 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/class/doleditor.class.php'; +require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; +require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php'; +require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmdirectory.class.php'; +require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; + +// Load Saturne libraries +require_once __DIR__ . '/../../../saturne/class/saturnesignature.class.php'; + +// Load DigiQuali libraries +require_once __DIR__ . '/../../class/survey.class.php'; +require_once __DIR__ . '/../../class/sheet.class.php'; +require_once __DIR__ . '/../../class/question.class.php'; +require_once __DIR__ . '/../../class/answer.class.php'; +require_once __DIR__ . '/../../class/digiqualidocuments/surveydocument.class.php'; +require_once __DIR__ . '/../../lib/digiquali_survey.lib.php'; +require_once __DIR__ . '/../../lib/digiquali_answer.lib.php'; +require_once __DIR__ . '/../../lib/digiquali_sheet.lib.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $user; + +// Load translation files required by the page +saturne_load_langs(['other', 'bills', 'orders']); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'aZ09'); +$subaction = GETPOST('subaction', 'aZ09'); +$confirm = GETPOST('confirm', 'alpha'); +$cancel = GETPOST('cancel', 'aZ09'); +$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'controlcard'; // To manage different context of search +$backtopage = GETPOST('backtopage', 'alpha'); +$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); + +// Initialize objects +// Technical objets +$object = new Survey($db); +$controldet = new SurveyLine($db); +$document = new SurveyDocument($db); +$signatory = new SaturneSignature($db, 'digiquali'); +$product = new Product($db); +$sheet = new Sheet($db); +$question = new Question($db); +$answer = new Answer($db); +$usertmp = new User($db); +$thirdparty = new Societe($db); +$contact = new Contact($db); +$extrafields = new ExtraFields($db); +$ecmfile = new EcmFiles($db); +$ecmdir = new EcmDirectory($db); +$category = new Categorie($db); + +// View objects +$form = new Form($db); + +$hookmanager->initHooks(array('surveycard', 'globalcard')); // Note that conf->hooks_modules contains array + +// Fetch optionals attributes and labels +$extrafields->fetch_name_optionals_label($object->table_element); + +$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); + +// Initialize array of search criterias +$searchAll = GETPOST('search_all', 'alpha'); +$search = array(); +foreach ($object->fields as $key => $val) { + if (GETPOST('search_'.$key, 'alpha')) $search[$key] = GETPOST('search_'.$key, 'alpha'); +} + +if (empty($action) && empty($id) && empty($ref)) $action = 'view'; + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once. + +$permissiontoread = $user->rights->digiquali->survey->read; +$permissiontoadd = $user->rights->digiquali->survey->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php +$permissiontodelete = $user->rights->digiquali->survey->delete || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT); +$upload_dir = $conf->digiquali->multidir_output[isset($object->entity) ? $object->entity : 1]; + +// Security check - Protection if external user +saturne_check_access($permissiontoread, $object); + +/* + * Actions + */ + +$parameters = ['id' => $id]; +$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks. +if ($resHook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} + +if (empty($resHook)) { + $error = 0; + + $backurlforlist = dol_buildpath('/digiquali/view/survey/survey_list.php', 1); + + if (empty($backtopage) || ($cancel && empty($id))) { + if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { + if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) $backtopage = $backurlforlist; + else $backtopage = dol_buildpath('/digiquali/view/survey/survey_card.php', 1).'?id='.($id > 0 ? $id : '__ID__'); + } + } + + // Action clone object + if ($action == 'confirm_clone' && $confirm == 'yes') { + $options['attendants'] = GETPOST('clone_attendants'); + $options['photos'] = GETPOST('clone_photos'); + if ($object->id > 0) { + $result = $object->createFromClone($user, $object->id, $options); + if ($result > 0) { + header("Location: " . $_SERVER['PHP_SELF'] . '?id=' . $result); + exit(); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } + } + } + + if ($action == 'add' && !$cancel) { + $linkableElements = get_sheet_linkable_objects(); + $controlledObjectSelected = 0; + + if (!empty($linkableElements)) { + foreach ($linkableElements as $linkableElementType => $linkableElement) { + if (!empty(GETPOST($linkableElement['post_name'])) && GETPOST($linkableElement['post_name']) > 0) { + $controlledObjectSelected++; + } + } + } + + if (GETPOST('fk_sheet') > 0) { + if ($controlledObjectSelected == 0) { + setEventMessages($langs->trans('NeedObjectToControl'), [], 'errors'); + header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create&fk_sheet=' . GETPOST('fk_sheet')); + exit; + } + } else { + setEventMessages($langs->trans('NeedFkSheet'), [], 'errors'); + header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create'); + exit; + } + + } + + // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen + include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; + + // Actions set_thirdparty, set_project + require_once __DIR__ . '/../../../saturne/core/tpl/actions/banner_actions.tpl.php'; + + if ($action == 'set_categories' && $permissiontoadd) { + if ($object->fetch($id) > 0) { + $result = $object->setCategories(GETPOST('categories', 'array')); + header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $id); + exit(); + } + } + + if ($action == 'show_only_questions_with_no_answer') { + $data = json_decode(file_get_contents('php://input'), true); + + $showOnlyQuestionsWithNoAnswer = $data['showOnlyQuestionsWithNoAnswer']; + + $tabParam['DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER'] = $showOnlyQuestionsWithNoAnswer; + + dol_set_user_param($db, $conf, $user, $tabParam); + } + + require_once __DIR__ . '/../../core/tpl/digiquali_control_answers_save_action.tpl.php'; + + // Actions builddoc, forcebuilddoc, remove_file. + require_once __DIR__ . '/../../../saturne/core/tpl/documents/documents_action.tpl.php'; + + // Action to generate pdf from odt file + require_once __DIR__ . '/../../../saturne/core/tpl/documents/saturne_manual_pdf_generation_action.tpl.php'; + + // Action to set status STATUS_VALIDATED + if ($action == 'confirm_setValidated') { + $object->fetch($id); + if ( ! $error) { + $result = $object->validate($user, false); + if ($result > 0) { + $controldet = new ControlLine($db); + $sheet->fetch($object->fk_sheet); + $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); + $questionIds = $object->linkedObjectsIds; + foreach ($questionIds['digiquali_question'] as $questionId) { + $controldettmp = $controldet; + //fetch controldet avec le fk_question et fk_control, s'il existe on l'update sinon on le crée + $result = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); + + //sauvegarder réponse + $questionAnswer = GETPOST('answer'.$questionId); + if (!empty($questionAnswer)) { + $controldettmp->answer = $questionAnswer; + } + + //sauvegarder commentaire + $comment = GETPOST('comment'.$questionId); + if (dol_strlen($comment) > 0) { + $controldettmp->comment = $comment; + } + + if ($result > 0 && is_array($result)) { + $controldettmp = array_shift($result); + + $controldettmp->update($user); + } else { + if (empty($controldettmp->ref)) { + $controldettmp->ref = $controldettmp->getNextNumRef(); + } + $controldettmp->fk_control = $object->id; + $controldettmp->fk_question = $questionId; + $controldettmp->entity = $conf->entity; + + $controldettmp->insert($user); + } + } + // Set validated OK + $urltogo = str_replace('__ID__', $result, $backtopage); + $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation + header('Location: ' . $urltogo); + exit; + } else { + // Set validated KO + if ( ! empty($object->errors)) setEventMessages(null, $object->errors, 'errors'); + else setEventMessages($object->error, null, 'errors'); + } + } + } + + // Action to set status STATUS_REOPENED + if ($action == 'confirm_setReopened') { + $object->fetch($id); + if ( ! $error) { + $result = $object->setDraft($user, false); + if ($result > 0) { + $object->verdict = null; + $result = $object->update($user); + // Set reopened OK + $urltogo = str_replace('__ID__', $result, $backtopage); + $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation + header('Location: ' . $urltogo); + exit; + } else { + // Set reopened KO + if ( ! empty($object->errors)) setEventMessages(null, $object->errors, 'errors'); + else setEventMessages($object->error, null, 'errors'); + } + } + } + + // Action to set status STATUS_LOCKED + if ($action == 'confirm_lock') { + $object->fetch($id); + if (!$error) { + $result = $object->setLocked($user); + if ($result > 0) { + // Set Locked OK + $urltogo = str_replace('__ID__', $result, $backtopage); + $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation + header('Location: ' . $urltogo); + exit; + } elseif (!empty($object->errors)) { // Set Locked KO. + setEventMessages('', $object->errors, 'errors'); + } else { + setEventMessages($object->error, [], 'errors'); + } + } + } + + // Action to set status STATUS_ARCHIVED. + if ($action == 'confirm_archive' && $permissiontoadd) { + $object->fetch($id); + if (!$error) { + $result = $object->setArchived($user); + if ($result > 0) { + // Set Archived OK. + $urltogo = str_replace('__ID__', $result, $backtopage); + $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation. + header('Location: ' . $urltogo); + exit; + } elseif (!empty($object->errors)) { // Set Archived KO. + setEventMessages('', $object->errors, 'errors'); + } else { + setEventMessages($object->error, [], 'errors'); + } + } + } + + // Actions to send emails + $triggersendname = 'SURVEY_SENTBYMAIL'; + $autocopy = 'MAIN_MAIL_AUTOCOPY_AUDIT_TO'; + $trackid = 'survey' . $object->id; + require_once DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php'; +} + +/* + * View + */ + +$title = $langs->trans('Survey'); +$help_url = 'FR:Module_DigiQuali'; + +saturne_header(1,'', $title, $help_url); +$object->fetch(GETPOST('id')); + +$elementArray = get_sheet_linkable_objects(); + +// Part to create +if ($action == 'create') { + if (empty($permissiontoadd)) { + accessforbidden($langs->trans('NotEnoughPermissions'), 0); + exit; + } + + print load_fiche_titre($langs->trans('New' . ucfirst($object->element)), '', 'object_' . $object->picto); + + print '
'; + print ''; + print ''; + if ($backtopage) { + print ''; + } + if ($backtopageforcancel) { + print ''; + } + + print dol_get_fiche_head(); + + print ''; + + if (!empty(GETPOST('fk_sheet'))) { + $sheet->fetch(GETPOST('fk_sheet')); + } + + //FK SHEET + print ''; + + // Common attributes + require_once DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php'; + + // Categories + if (isModEnabled('categorie')) { + print ''; + } + + print '
' . $langs->trans('Sheet') . ''; + print img_picto('', $sheet->picto, 'class="pictofixedwidth"') . $sheet->selectSheetList(GETPOST('fk_sheet') ?: $sheet->id); + print ''; + print '
' . $langs->trans('Categories') . ''; + $categoriesArborescence = $form->select_all_categories($object->element, '', 'parent', 64, 0, 1); + print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $categoriesArborescence, GETPOST('categories', 'array'), '', 0, 'maxwidth500 widthcentpercentminusx'); + print ''; + print '
'; + print '
'; + + print ''; + + print '
'; + + foreach($elementArray as $linkableElementType => $linkableElement) { + if (!empty($linkableElement['conf'] && preg_match('/"'. $linkableElementType .'":1/',$sheet->element_linked))) { + + $objectArray = []; + $objectPostName = $linkableElement['post_name']; + $objectPost = GETPOST($objectPostName) ?: (GETPOST('fromtype') == $linkableElement['link_name'] ? GETPOST('fromid') : ''); + + if ((dol_strlen($linkableElement['fk_parent']) > 0 && GETPOST($linkableElement['parent_post']) > 0)) { + $objectFilter = [ + 'customsql' => $linkableElement['fk_parent'] . ' = ' . GETPOST($linkableElement['parent_post']) + ]; + } else { + $objectFilter = []; + } + $objectList = saturne_fetch_all_object_type($linkableElement['className'], '', '', 0, 0, $objectFilter); + + if (is_array($objectList) && !empty($objectList)) { + foreach($objectList as $objectSingle) { + $objectName = ''; + $nameField = $linkableElement['name_field']; + if (strstr($nameField, ',')) { + $nameFields = explode(', ', $nameField); + if (is_array($nameFields) && !empty($nameFields)) { + foreach($nameFields as $subnameField) { + $objectName .= $objectSingle->$subnameField . ' '; + } + } + } else { + $objectName = $objectSingle->$nameField; + } + $objectArray[$objectSingle->id] = $objectName; + } + } + + print '
'; + } + } + + print ''; + + // Other attributes + require_once DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; + + print '
' . $langs->transnoentities($linkableElement['langs']) . ''; + print img_picto('', $linkableElement['picto'], 'class="pictofixedwidth"'); + print $form->selectArray($objectPostName, $objectArray, $objectPost, $langs->trans('Select') . ' ' . strtolower($langs->trans($linkableElement['langs'])), 0, 0, '', 0, 0, dol_strlen(GETPOST('fromtype')) > 0 && GETPOST('fromtype') != $linkableElement['link_name'], '', 'maxwidth500 widthcentpercentminusxx'); + print ''; + print '
'; + + print dol_get_fiche_end(); + + print $form->buttonsSaveCancel('Create', 'Cancel', [], 0, 'wpeo-button'); + + print '
'; +} + +// Part to show record +if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'create'))) { + $res = $object->fetch_optionals(); + + saturne_get_fiche_head($object, 'card', $title); + saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); + + $formConfirm = ''; + + // SetValidated confirmation + if (($action == 'setValidated' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + $sheet->fetch($object->fk_sheet); + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $questionIds = $sheet->linkedObjectsIds['digiquali_question']; + + if (!empty($questionIds)) { + $questionCounter = count($questionIds); + } else { + $questionCounter = 0; + } + + $object->fetchLines(); + $answerCounter = 0; + if (is_array($object->lines) && !empty($object->lines)) { + foreach($object->lines as $objectLine) { + if (dol_strlen($objectLine->answer) > 0) { + $answerCounter++; + } + } + } + + $questionConfirmInfo = $langs->trans('YouAnswered') . ' ' . $answerCounter . ' ' . $langs->trans('question(s)') . ' ' . $langs->trans('On') . ' ' . $questionCounter . '.'; + if ($questionCounter - $answerCounter != 0) { + $questionConfirmInfo .= '
' . $langs->trans('BewareQuestionsAnswered', $questionCounter - $answerCounter) . ''; + } + + $questionConfirmInfo .= '

' . $langs->trans('ConfirmValidateControl') . ''; + $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ValidateControl'), $questionConfirmInfo, 'confirm_setValidated', '', 'yes', 'actionButtonValidate', 250); + } + + // SetReopened confirmation + if (($action == 'setReopened' && (empty($conf->use_javascript_ajax) || ! empty($conf->dol_use_jmobile))) || ( ! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ReOpenObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmReOpenObject', $langs->transnoentities('The' . ucfirst($object->element)), $langs->transnoentities('The' . ucfirst($object->element))) . '
' . $langs->trans('ConfirmReOpenControl', $object->ref), 'confirm_setReopened', '', 'yes', 'actionButtonReOpen', 350, 600); + } + + // Lock confirmation + if (($action == 'lock' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('LockObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmLockObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_set_Lock', '', 'yes', 'actionButtonLock', 350, 600); + } + + // Clone confirmation + if (($action == 'clone' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + // Define confirmation messages. + $formquestionclone = [ + ['type' => 'checkbox', 'name' => 'clone_attendants', 'label' => $langs->trans('CloneAttendants'), 'value' => 1], + ['type' => 'checkbox', 'name' => 'clone_photos', 'label' => $langs->trans('ClonePhotos'), 'value' => 1] + ]; + + $formconfirm .= $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('CloneObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmCloneObject', $langs->transnoentities('The' . ucfirst($object->element)), $object->ref), 'confirm_clone', $formquestionclone, 'yes', 'actionButtonClone', 350, 600); + } + // Delete confirmation + if ($action == 'delete') { + $formConfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('DeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmDeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_delete', '', 'yes', 1); + } + + // Call Hook formConfirm + $parameters = ['formConfirm' => $formConfirm, 'lineid' => $lineid]; + $resHook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if (empty($resHook)) { + $formConfirm .= $hookmanager->resPrint; + } elseif ($resHook > 0) { + $formConfirm = $hookmanager->resPrint; + } + + // Print form confirm + print $formConfirm; + + if ($conf->browser->layout == 'phone') { + $onPhone = 1; + } else { + $onPhone = 0; + } + + print '
'; + print '
'; + print ''; + + // Common attributes + unset($object->fields['projectid']); // Hide field already shown in banner + + if (getDolGlobalInt('SATURNE_ENABLE_PUBLIC_INTERFACE')) { + $publicControlInterfaceUrl = dol_buildpath('custom/digiquali/public/control/public_control.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); + print ''; + print ''; + print ''; + + //Survey public interface + print ''; + print ''; + } + + require_once DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; + + // Categories + if ($conf->categorie->enabled) { + print ''; + if ($action != 'categories') { + print ''; + } + if ($permissiontoadd && $action == 'categories') { + $categoryArborescence = $form->select_all_categories('control', '', 'parent', 64, 0, 1); + $categoryArborescence = empty($categoryArborescence) ? [] : $categoryArborescence; + if (is_array($categoryArborescence)) { + // Categories + print ''; + } + } + print ''; + } + + $object->fetchObjectLinked('', '', $object->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); + + foreach($elementArray as $linkableElementType => $linkableElement) { + if ($linkableElement['conf'] > 0 && (!empty($object->linkedObjectsIds[$linkableElement['link_name']]))) { + $className = $linkableElement['className']; + $linkedObject = new $className($db); + + $linkedObjectKey = array_key_first($object->linkedObjectsIds[$linkableElement['link_name']]); + $linkedObjectId = $object->linkedObjectsIds[$linkableElement['link_name']][$linkedObjectKey]; + + $result = $linkedObject->fetch($linkedObjectId); + + if ($result > 0) { + print ''; + print ''; + } + } + } + + print ''; + + // Other attributes. Fields from hook formObjectOptions and Extrafields + require_once DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; + + print '
' . $langs->trans('PublicControl') . ' '; + print ' '; + print ''; + print '' . saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/qrcode/', 'small', 1, 0, 0, 0, 80, 80, 0, 0, 0, 'control/'. $object->ref . '/qrcode/', $object, '', 0, 0) . '
'; + $publicSurveyUrl = dol_buildpath('custom/digiquali/public/control/public_survey.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); + print $langs->trans('PublicSurvey'); + print ' '; + print showValueWithClipboardCPButton($publicSurveyUrl, 0, ' '); + print ''; + print ''. $langs->trans('GoToPublicSurveyPage') .' '; + print '
' . $langs->trans('Categories') . '' . ($object->status < Control::STATUS_LOCKED ? '' . img_edit($langs->trans('Modify')) . '' : ''); + print $form->showCategories($object->id, 'control', 1) . ''; + print '
'; + print ''; + print ''; + + $cats = $category->containing($object->id, 'control'); + $arrayselected = array(); + foreach ($cats as $cat) { + $arrayselected[] = $cat->id; + } + + print img_picto('', 'category') . $form->multiselectarray('categories', $categoryArborescence, $arrayselected, '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0); + print ''; + print '
'; + print '
'; + print $langs->trans($linkableElement['langs']); + print ''; + + print $linkedObject->getNomUrl(1); + + if ($linkedObject->array_options['options_qc_frequency'] > 0) { + print ' '; + print ''; + print $langs->transnoentities('QcFrequency') . ' : ' . $linkedObject->array_options['options_qc_frequency']; + print ''; + } + + print '
'; + $pathPhotos = $conf->digiquali->multidir_output[$conf->entity] . '/control/'. $object->ref . '/photos/'; + $fileArray = dol_dir_list($pathPhotos, 'files'); + ?> + status < Control::STATUS_LOCKED) ? '' : 'style="display:none"' ?>> + + + + + + ref . '/photos/', $object, 'photo', $object->status < Control::STATUS_LOCKED, $permissiontodelete && $object->status < Control::STATUS_LOCKED); + print '
'; + print '
'; + print '
'; + + $sheet->fetch($object->fk_sheet); + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + + $questionIds = $sheet->linkedObjectsIds['digiquali_question']; + $cantValidateControl = 0; + $mandatoryArray = json_decode($sheet->mandatory_questions, true); + + if (is_array($mandatoryArray) && !empty($mandatoryArray) && is_array($questionIds) && !empty($questionIds)) { + foreach ($questionIds as $questionId) { + if (in_array($questionId, $mandatoryArray)) { + $controldettmp = $controldet; + $resultQuestion = $question->fetch($questionId); + $resultAnswer = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); + if (($resultAnswer > 0 && is_array($resultAnswer)) || !empty($controldettmp)) { + $itemControlDet = !empty($resultAnswer) ? array_shift($resultAnswer) : $controldettmp; + if ($resultQuestion > 0) { + if (empty($itemControlDet->comment) && empty($itemControlDet->answer)) { + $cantValidateControl++; + } + } + } + } + } + } + + print '
'; + + + print '
'; + print ''; + print ''; + + // Buttons for actions + if ($action != 'presend' && $action != 'editline') { + print '
'; + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + + if (empty($reshook)) { + // Save question answer + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Save'); + if ($object->status == $object::STATUS_DRAFT) { + print '' . $displayButton . ' '; + } else { + print '' . $displayButton . ''; + } + + // Validate + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Validate'); + if ($object->status == $object::STATUS_DRAFT && empty($cantValidateControl) && !$equipmentOutdated) { + print '' . $displayButton . ''; + } else if ($cantValidateControl > 0) { + print '' . $displayButton . ''; + } else if ($equipmentOutdated) { + print '' . $displayButton . ''; + } elseif ($object->status < $object::STATUS_DRAFT) { + print '' . $displayButton . ''; + } + + // ReOpen + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('ReOpenDoli'); + if ($object->status == $object::STATUS_VALIDATED) { + print '' . $displayButton . ''; + } elseif ($object->status > $object::STATUS_VALIDATED) { + print '' . $displayButton . ''; + } + + // Sign + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Sign'); + if ($object->status == $object::STATUS_VALIDATED && !$signatory->checkSignatoriesSignatures($object->id, $object->element)) { + print '' . $displayButton . ''; + } else { + print '' . $displayButton . ''; + } + + // Lock + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Lock'); + if ($object->status == $object::STATUS_VALIDATED && $object->verdict != null && $signatory->checkSignatoriesSignatures($object->id, $object->element) && !$equipmentOutdated) { + print '' . $displayButton . ''; + } else { + print '' . $displayButton . ''; + } + + // Send email + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('SendMail') . ' '; + if ($object->status == $object::STATUS_LOCKED) { + $fileparams = dol_most_recent_file($upload_dir . '/' . $object->element . 'document' . '/' . $object->ref); + $file = $fileparams['fullname']; + if (file_exists($file) && !strstr($fileparams['name'], 'specimen')) { + $forcebuilddoc = 0; + } else { + $forcebuilddoc = 1; + } + print dolGetButtonAction($displayButton, '', 'default', $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&action=presend&forcebuilddoc=' . $forcebuilddoc . '&mode=init#formmailbeforetitle', '', $object->status == $object::STATUS_LOCKED); + } else { + print '' . $displayButton . ''; + } + + // Archive + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Archive'); + if ($object->status == $object::STATUS_LOCKED) { + print '' . $displayButton . ''; + } else { + print '' . $displayButton . ''; + } + + // Clone + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('ToClone'); + print '' . $displayButton . ''; + + // Delete (need delete permission, or if draft, just need create/modify permission) + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Delete'); + print dolGetButtonAction($displayButton, '', 'delete', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=delete&token='.newToken(), '', $permissiontodelete || ($object->status == $object::STATUS_DRAFT && $permissiontoadd)); + } + print '
'; + } + + // QUESTION LINES + print '
'; + + $sheet->fetch($object->fk_sheet); + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $questionIds = $sheet->linkedObjectsIds['digiquali_question']; + + if (is_array($questionIds) && !empty($questionIds)) { + ksort($questionIds); + } + if (!empty($questionIds)) { + $questionCounter = count($questionIds); + } else { + $questionCounter = 0; + } + + $object->fetchLines(); + $answerCounter = 0; + if (is_array($object->lines) && !empty($object->lines)) { + foreach($object->lines as $objectLine) { + if (dol_strlen($objectLine->answer) > 0) { + $answerCounter++; + } + } + } ?> + +
+ +
+
+
+ + conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? img_picto($langs->trans('Enabled'), 'switch_on', 'class="show-only-questions-with-no-answer marginrightonly"') : img_picto($langs->trans('Disabled'), 'switch_off', 'class="show-only-questions-with-no-answer marginrightonly"'); + print $form->textwithpicto($user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? '' : '', $langs->trans('ShowOnlyQuestionsWithNoAnswer')); + print '
'; + + if (!$user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER || $answerCounter != $questionCounter) { + print load_fiche_titre($langs->trans('LinkedQuestionsList'), '', ''); + print '
'; + require_once __DIR__ . '/../../core/tpl/digiquali_control_answers.tpl.php'; + print '
'; + } + + print '
'; + print '
'; + print dol_get_fiche_end(); + + $includedocgeneration = 1; + if ($includedocgeneration) { + print '
'; + + $objref = dol_sanitizeFileName($object->ref); + $dirFiles = $object->element . 'document/' . $objref; + $filedir = $upload_dir . '/' . $dirFiles; + $urlsource = $_SERVER['PHP_SELF'] . '?id=' . $id; + + $defaultmodel = 'controldocument_odt'; + $title = $langs->trans('WorkUnitDocument'); + + print saturne_show_documents('digiquali:ControlDocument', $dirFiles, $filedir, $urlsource, 1,1, '', 1, 0, 0, 0, 0, '', 0, '', empty($soc->default_lang) ? '' : $soc->default_lang, $object, 0, 'remove_file', (($object->status > $object::STATUS_DRAFT) ? 1 : 0), $langs->trans('ControlMustBeValidatedToGenerated')); + print '
'; + + print '
'; + + $maxEvent = 10; + + $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', dol_buildpath('/saturne/view/saturne_agenda.php', 1) . '?id=' . $object->id . '&module_name=DigiQuali&object_type=' . $object->element); + + // List of actions on element + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; + $formactions = new FormActions($db); + $somethingshown = $formactions->showactions($object, $object->element.'@'.$object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $maxEvent, '', $morehtmlcenter); + + print '
'; + } + + //Select mail models is same action as presend + if (GETPOST('modelselected')) { + $action = 'presend'; + } + + if ($action == 'presend') { + $langs->load('mails'); + + $ref = dol_sanitizeFileName($object->ref); + $filelist = dol_dir_list($upload_dir . '/' . $object->element . 'document' . '/' . $ref, 'files', 0, '', '', 'date', SORT_DESC); + if (!empty($filelist) && is_array($filelist)) { + $filetype = ['controldocument' => 0]; + foreach ($filelist as $file) { + if (!strstr($file['name'], 'specimen')) { + if (strstr($file['name'], str_replace(' ', '_', $langs->transnoentities('controldocument'))) && $filetype['controldocument'] == 0) { + $files[] = $file['fullname']; + $filetype['controldocument'] = 1; + } + } + } + } + + // Define output language + $outputlangs = $langs; + $newlang = ''; + if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang)) { + $newlang = $object->thirdparty->default_lang; + if (GETPOST('lang_id', 'aZ09')) { + $newlang = GETPOST('lang_id', 'aZ09'); + } + } + + if (!empty($newlang)) { + $outputlangs = new Translate('', $conf); + $outputlangs->setDefaultLang($newlang); + } + + print '
'; + print '
'; + print '
'; + print load_fiche_titre($langs->trans('SendMail'), '', $object->picto); + + print dol_get_fiche_head(); + + // Create form for email. + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); + + $formmail->param['langsmodels'] = (empty($newlang) ? $langs->defaultlang : $newlang); + $formmail->fromtype = (GETPOST('fromtype') ?GETPOST('fromtype') : (!empty($conf->global->MAIN_MAIL_DEFAULT_FROMTYPE) ? $conf->global->MAIN_MAIL_DEFAULT_FROMTYPE : 'user')); + + if ($formmail->fromtype === 'user') { + $formmail->fromid = $user->id; + } + + $formmail->withfrom = 1; + + // Define $liste, a list of recipients with email inside <>. + $liste = []; + if (!empty($object->socid) && $object->socid > 0 && !is_object($object->thirdparty) && method_exists($object, 'fetch_thirdparty')) { + $object->fetch_thirdparty(); + } + if (is_object($object->thirdparty)) { + foreach ($object->thirdparty->thirdparty_and_contact_email_array(1) as $key => $value) { + $liste[$key] = $value; + } + } + + if (!empty($conf->global->MAIN_MAIL_ENABLED_USER_DEST_SELECT)) { + $listeuser = []; + $fuserdest = new User($db); + + $result = $fuserdest->fetchAll('ASC', 't.lastname', 0, 0, ['customsql' => "t.statut = 1 AND t.employee = 1 AND t.email IS NOT NULL AND t.email <> ''"], 'AND', true); + if ($result > 0 && is_array($fuserdest->users) && count($fuserdest->users) > 0) { + foreach ($fuserdest->users as $uuserdest) { + $listeuser[$uuserdest->id] = $uuserdest->user_get_property($uuserdest->id, 'email'); + } + } elseif ($result < 0) { + setEventMessages(null, $fuserdest->errors, 'errors'); + } + if (count($listeuser) > 0) { + $formmail->withtouser = $listeuser; + $formmail->withtoccuser = $listeuser; + } + } + + //$arrayoffamiliestoexclude=array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...); + if (!isset($arrayoffamiliestoexclude)) { + $arrayoffamiliestoexclude = null; + } + + // Make substitution in email content. + if ($object) { + // First we set ->substit (useless, it will be erased later) and ->substit_lines. + $formmail->setSubstitFromObject($object, $langs); + } + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, $arrayoffamiliestoexclude, $object); + $substitutionarray['__TYPE__'] = $langs->trans(ucfirst($object->element)); + $substitutionarray['__THETYPE__'] = $langs->trans('The' . ucfirst($object->element)); + + $parameters = ['mode' => 'formemail']; + complete_substitutions_array($substitutionarray, $outputlangs, $object, $parameters); + + // Find all external contact addresses + $tmpobject = $object; + $contactarr = []; + $contactarr = $tmpobject->liste_contact(-1); + + if (is_array($contactarr) && count($contactarr) > 0) { + require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; + require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; + $contactstatic = new Contact($db); + $tmpcompany = new Societe($db); + + foreach ($contactarr as $contact) { + $contactstatic->fetch($contact['id']); + // Complete substitution array + $substitutionarray['__CONTACT_NAME_' . $contact['code'] . '__'] = $contactstatic->getFullName($outputlangs, 1); + $substitutionarray['__CONTACT_LASTNAME_' . $contact['code'] . '__'] = $contactstatic->lastname; + $substitutionarray['__CONTACT_FIRSTNAME_' . $contact['code'] . '__'] = $contactstatic->firstname; + $substitutionarray['__CONTACT_TITLE_' . $contact['code'] . '__'] = $contactstatic->getCivilityLabel(); + + // Complete $liste with the $contact + if (empty($liste[$contact['id']])) { // If this contact id not already into the $liste. + $contacttoshow = ''; + if (isset($object->thirdparty) && is_object($object->thirdparty)) { + if ($contactstatic->fk_soc != $object->thirdparty->id) { + $tmpcompany->fetch($contactstatic->fk_soc); + if ($tmpcompany->id > 0) { + $contacttoshow .= $tmpcompany->name . ': '; + } + } + } + $contacttoshow .= $contactstatic->getFullName($outputlangs, 1); + $contacttoshow .= ' <' . ($contactstatic->email ?: $langs->transnoentitiesnoconv('NoEMail')) . '>'; + $liste[$contact['id']] = $contacttoshow; + } + } + } + + $formmail->withto = $liste; + $formmail->withtofree = (GETPOSTISSET('sendto') ? (GETPOST('sendto', 'alphawithlgt') ? GETPOST('sendto', 'alphawithlgt') : '1') : '1'); + $formmail->withtocc = $liste; + $formmail->withtoccc = getDolGlobalString('MAIN_EMAIL_USECCC'); + $formmail->withtopic = $outputlangs->trans('SendMailSubject', '__REF__'); + $formmail->withfile = 2; + $formmail->withbody = 1; + $formmail->withdeliveryreceipt = 1; + $formmail->withcancel = 1; + + // Array of substitutions. + $formmail->substit = $substitutionarray; + + // Array of other parameters. + $formmail->param['action'] = 'send'; + $formmail->param['models'] = 'saturne'; + $formmail->param['models_id'] = GETPOST('modelmailselected', 'int'); + $formmail->param['id'] = $object->id; + $formmail->param['returnurl'] = $_SERVER['PHP_SELF'] . '?id=' . $object->id; + $formmail->param['fileinit'] = $files; + $formmail->trackid = 'control' . $object->id; + + // Show form. + print $formmail->get_form(); + + print dol_get_fiche_end(); + } +} + +// End of page +llxFooter(); +$db->close(); diff --git a/view/survey/survey_list.php b/view/survey/survey_list.php new file mode 100644 index 00000000..a4e03711 --- /dev/null +++ b/view/survey/survey_list.php @@ -0,0 +1,368 @@ + + * + * 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 view/survey/survey_list.php + * \ingroup digiquali + * \brief List page for survey + */ + +// 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/class/html.formcompany.class.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; + +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcategory.class.php'; +require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/contact.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/usergroups.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/contract.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/stock.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/sendings.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/supplier_proposal.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php'; + +// Load Saturne libraries +require_once __DIR__ . '/../../../saturne/class/saturnesignature.class.php'; + +// load DigiQuali libraries +require_once __DIR__ . '/../../lib/digiquali_sheet.lib.php'; + +require_once __DIR__ . '/../../class/survey.class.php'; +require_once __DIR__ . '/../../core/boxes/digiqualiwidget1.php'; +require_once __DIR__ . '/../../class/sheet.class.php'; +require_once __DIR__ . '/../../class/control.class.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $user; + +// Load translation files required by the page +saturne_load_langs(['other', 'bills', 'projects', 'orders', 'companies', 'product', 'productbatch', 'task', 'contracts']); + +$action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... +$massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) +$show_files = GETPOST('show_files', 'int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'controllist'; // To manage different context of search +$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page +$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') +$fromtype = GETPOST('fromtype', 'alpha'); // element type +$fromid = GETPOST('fromid', 'int'); //element id + +// Load variable for pagination +$limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; +$sortfield = GETPOST('sortfield', 'aZ09comma'); +$sortorder = GETPOST('sortorder', 'aZ09comma'); +$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); +if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { $page = 0; } // If $page is not defined, or '' or -1 or if we click on clear filters +$offset = $limit * $page; +$pageprev = $page - 1; +$pagenext = $page + 1; + +// Initialize objects +// Technical objets +$object = new Control($db); +$signatory = new SaturneSignature($db, 'digiquali', $object->element); +$box = new digiqualiwidget1($db); +$categorystatic = new Categorie($db); +$sheet = new Sheet($db); +$extrafields = new ExtraFields($db); +$controlstatic = new Control($db); +$userTmp = new User($db); +if (isModEnabled('societe')) { + $thirdparty = new Societe($db); + $contact = new Contact($db); +} + +// View objects +$form = new Form($db); + +$hookmanager->initHooks(array('controllist')); // Note that conf->hooks_modules contains array + +// Fetch optionals attributes and labels +$extrafields->fetch_name_optionals_label($object->table_element); +//$extrafields->fetch_name_optionals_label($object->table_element_line); + +if (!empty($conf->categorie->enabled)) { + $search_category_array = GETPOST("search_category_control_list", "array"); +} + +$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); + +// Default sort order (if not yet defined by previous GETPOST) +if (!$sortfield) { + reset($object->fields); // Reset is required to avoid key() to return null + $sortfield = 't.date_creation'; // Set here default search field. By default, date_creation +} +if (!$sortorder) { + $sortorder = 'DESC'; +} + +$linkableElements = get_sheet_linkable_objects(); + +$objectPosition = 20; +foreach($linkableElements as $linkableElementType => $linkableElement) { + $className = $linkableElement['className']; + + if ((empty($fromtype) && $linkableElement['conf'] > 0) || ($fromtype == $linkableElement['link_name'])) { + $arrayfields['t.' . $linkableElement['post_name']] = [ + 'type' => 'integer:' . $className . ':' . $linkableElement['class_path'], + 'label' => $langs->trans($linkableElement['langs']) . ' ' . $langs->trans('controlled'), + 'enabled' => '1', + 'position' => $objectPosition, + 'notnull' => 0, + 'visible' => 5, + 'checked' => 1 + ]; + + $object->fields[$linkableElement['post_name']] = $arrayfields['t.' . $linkableElement['post_name']]; + $elementElementFields[$linkableElement['post_name']] = $linkableElement['link_name']; + $linkNameElementCorrespondence[$linkableElement['link_name']] = $linkableElement; + $objectPosition++; + + if (!empty($fromtype)) { + $objectLinked = new $className($db); + $objectLinked->fetch($fromid); + } + } +} + +// Initialize array of search criterias +$searchAll = GETPOST('search_all', 'alphanohtml') ? GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml'); +$search = array(); +foreach ($object->fields as $key => $val) { + if (GETPOST('search_'.$key, 'alpha') !== '') $search[$key] = GETPOST('search_'.$key, 'alpha'); +} + +if(!empty($fromtype)) { + $search_key = array_search($fromtype, $elementElementFields); + $search[$search_key] = $fromid; + switch ($fromtype) { + case 'fk_sheet': + $search['fk_sheet'] = $fromid; + break; + case 'user': + $search['fk_user_controller'] = $fromid; + break; + } +} + +// List of fields to search into when doing a "search in all" +$fieldstosearchall = array(); +foreach ($object->fields as $key => $val) { + if ($val['searchall']) $fieldstosearchall['t.'.$key] = $val['label']; +} + +// Definition of array of fields for columns +$arrayfields = array(); +foreach ($object->fields as $key => $val) { + // If $val['visible']==0, then we never show the field + if (!empty($val['visible'])) { + $visible = (int) dol_eval($val['visible'], 1); + $arrayfields['t.'.$key] = array( + 'label'=>$val['label'], + 'checked'=>(($visible < 0) ? 0 : 1), + 'enabled'=>($visible != 3 && dol_eval($val['enabled'], 1)), + 'position'=>$val['position'], + 'help'=>$val['help'], + 'css' => $val['css'] + ); + } +} + +// Extra fields +include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_list_array_fields.tpl.php'; + +$object->fields = dol_sort_array($object->fields, 'position'); +$arrayfields = dol_sort_array($arrayfields, 'position'); + +$permissiontoread = $user->rights->digiquali->control->read; +$permissiontoadd = $user->rights->digiquali->control->write; +$permissiontodelete = $user->rights->digiquali->control->delete; + +// Security check +saturne_check_access($permissiontoread, $object); + +/* + * Actions + */ + +if (GETPOST('cancel', 'alpha')) { $action = 'list'; $massaction = ''; } +if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction = ''; } + +$parameters = array(); +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + +if (empty($reshook)) { + // Selection of new fields + include DOL_DOCUMENT_ROOT . '/core/actions_changeselectedfields.inc.php'; + + // Purge search criteria + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers + foreach ($object->fields as $key => $val) { + $search[$key] = ''; + $_POST[$key] = ''; + } + $toselect = ''; + $search_array_options = array(); + $search_category_array = array(); + } + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') + || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) + { + $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } + + // Mass actions + $objectclass = 'Control'; + $objectlabel = 'Control'; + $uploaddir = $conf->digiquali->dir_output; + + if (!$error && ($massaction == 'delete' || ($action == 'delete' && $confirm == 'yes')) && $permissiontodelete) { + $db->begin(); + + $objecttmp = new $objectclass($db); + $nbok = 0; + $TMsg = array(); + foreach ($toselect as $toselectid) { + $result = $objecttmp->fetch($toselectid); + if ($result > 0) { + $result = $objecttmp->delete($user); + + if (empty($result)) { // if delete returns 0, there is at least one object linked + $TMsg = array_merge($objecttmp->errors, $TMsg); + } elseif ($result < 0) { // if delete returns is < 0, there is an error, we break and rollback later + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } else { + $nbok++; + } + } else { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } + } + + if (empty($error)) { + // Message for elements well deleted + if ($nbok > 1) { + setEventMessages($langs->trans("RecordsDeleted", $nbok), null, 'mesgs'); + } elseif ($nbok > 0) { + setEventMessages($langs->trans("RecordDeleted", $nbok), null, 'mesgs'); + } + + // Message for elements which can't be deleted + if (!empty($TMsg)) { + sort($TMsg); + setEventMessages('', array_unique($TMsg), 'warnings'); + } + + $db->commit(); + } else { + $db->rollback(); + } + + //var_dump($listofobjectthirdparties);exit; + } + +// include DOL_DOCUMENT_ROOT . '/core/actions_massactions.inc.php'; + + // Mass actions archive + require_once __DIR__ . '/../../../saturne/core/tpl/actions/list_massactions.tpl.php'; +} + +/* + * View + */ + +$now = dol_now(); +$help_url = ''; +$title = $langs->trans("ControlList"); + +saturne_header(0,'', $title, $help_url); +if (!empty($fromtype)) { + print saturne_get_fiche_head($objectLinked, 'control', $langs->trans("Control")); + + $linkback = ''.$langs->trans("BackToList").''; + + saturne_banner_tab($objectLinked, 'fromtype=' . $fromtype . '&fromid', '', 1, 'rowid', ($fromtype == 'productbatch' ? 'batch' : 'ref')); +} + +if ($fromid) { + print '
'; + print '
'; + print '
'; + $controls = $controlstatic->fetchAll(); + + if (is_array($controls) && !empty($controls)) { + foreach ($controls as $control) { + $control->fetchObjectLinked('','', $control->id, 'digiquali_' . $control->element, 'OR', 1, 'sourcetype', 0); + if (!empty($control->linkedObjectsIds)) { + if (array_key_exists($fromtype, $control->linkedObjectsIds)) { + $linkedObjectsIds = array_values($control->linkedObjectsIds[$fromtype]); + if (in_array($fromid, $linkedObjectsIds)) { + $categories = $categorystatic->getListForItem($control->id, $control->element); + if (is_array($categories) && !empty($categories)) { + foreach ($categories as $category) { + $nbBox[$category['label']] = 1; + } + } + } + } + } + } + + if (is_array($nbBox) || is_object($nbBox)) { + $box->loadBox(); + for ($i = 0; $i < count($nbBox); $i++) { + $box->showBox($i,$i); + } + } + } + print '
'; +} + +$newcardbutton = dolGetButtonTitle($langs->trans('NewControl'), '', 'fa fa-plus-circle', dol_buildpath('/digiquali/view/control/control_card.php', 1).'?action=create', '', $permissiontoadd); + +include_once '../../core/tpl/digiquali_control_list.tpl.php'; + +// End of page +llxFooter(); +$db->close(); diff --git a/view/survey/survey_medias.php b/view/survey/survey_medias.php new file mode 100644 index 00000000..66ce0f16 --- /dev/null +++ b/view/survey/survey_medias.php @@ -0,0 +1,157 @@ + + * + * 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 view/control/control_medias.php + * \ingroup digiquali + * \brief Tab for medias on Control + */ + +// 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'); +} + +// Libraries +require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; + +require_once __DIR__ . '/../../class/control.class.php'; +require_once __DIR__ . '/../../class/question.class.php'; +require_once __DIR__ . '/../../lib/digiquali_control.lib.php'; + +// Global variables definitions +global $conf, $db,$hookmanager, $langs, $user; + +// Load translation files required by the page +saturne_load_langs(["companies"]); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'aZ09'); +$cancel = GETPOST('cancel', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize technical objects +$object = new Control($db); +$controldet = new ControlLine($db); +$question = new Question($db); +$extrafields = new ExtraFields($db); +$project = new Project($db); + +// View objects +$form = new Form($db); + +$hookmanager->initHooks(array('controlmedia', 'globalcard')); // Note that conf->hooks_modules contains array + +// Fetch optionals attributes and labels +$extrafields->fetch_name_optionals_label($object->table_element); + +// Load object +include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals +if ($id > 0 || !empty($ref)) { + $upload_dir = $conf->digiquali->multidir_output[$object->entity]."/".$object->id; +} + +$permissiontoread = $user->rights->digiquali->control->read; +$permissiontoadd = $user->rights->digiquali->control->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php +$permissiontodelete = $user->rights->digiquali->control->delete || ($permissiontoadd && isset($object->status)); +$permissionnote = $user->rights->digiquali->control->write; // Used by the include of actions_setnotes.inc.php +$upload_dir = $conf->digiquali->multidir_output[$conf->entity]; + +// Security check (enable the most restrictive one) +if ($user->socid > 0) accessforbidden(); +if ($user->socid > 0) $socid = $user->socid; +if (empty($conf->digiquali->enabled)) accessforbidden(); +if (!$permissiontoread) accessforbidden(); + +/* + * Action + */ + +$parameters = ['id' => $id]; +$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks. +if ($resHook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} + +if (empty($resHook)) { + // Actions set_thirdparty, set_project + require_once __DIR__ . '/../../../saturne/core/tpl/actions/banner_actions.tpl.php'; +} + +/* + * View + */ + +$help_url = ''; +$title = $langs->trans('Medias'); + +saturne_header(0,'', $title, $help_url); + +if ($id > 0 || !empty($ref)) { + $object->fetch_thirdparty(); + + $head = control_prepare_head($object); + + print saturne_get_fiche_head($object, 'medias', $title); + + // Object card + // ------------------------------------------------------------ + + saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); + + print '
'; + print '
'; + + print load_fiche_titre($langs->trans('MediaGalleryQuestionAnswers'), '', ''); + + $object->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet'); + $questionIds = $object->linkedObjectsIds; + $questionsLinked = $object->linkedObjects; + $relativepath = 'digiquali/medias/thumbs'; + $linkedMedias = 0; + + if (is_array($questionsLinked['digiquali_question']) && !empty($questionsLinked['digiquali_question'])) { + foreach ($questionsLinked['digiquali_question'] as $questionLinked) { + if ($questionLinked->authorize_answer_photo > 0 && file_exists($conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref)) { + print '
'; + print '' . $questionLinked->ref . ''; + print '
'; + print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, ($conf->global->DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY ? 'large' : 'medium'), '', 0, 0, 0, 200, 200, 0, 0, 0, 'control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, null, '', 0, 0); + print '
'; + print '
'; + $linkedMedias++; + } + } + } + + if ($linkedMedias == 0) { + print $langs->trans('NoControlAnswersPhoto'); + } + + print '
'; + print dol_get_fiche_end(); +} + +// End of page +llxFooter(); +$db->close(); From 4516605007de425a6a04478ae0365af7ddb2fcec Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 19:37:53 +0100 Subject: [PATCH 13/64] #1662 [View] add: rework object media view --- ...ntrol-medias.scss => _element-medias.scss} | 2 +- css/scss/page/_page.scss | 2 +- lib/digiquali_control.lib.php | 2 +- lib/digiquali_survey.lib.php | 2 +- view/control/control_medias.php | 157 ------------------ view/object_medias.php | 129 ++++++++++++++ view/survey/survey_medias.php | 157 ------------------ 7 files changed, 133 insertions(+), 318 deletions(-) rename css/scss/page/{_control-medias.scss => _element-medias.scss} (95%) delete mode 100644 view/control/control_medias.php create mode 100644 view/object_medias.php delete mode 100644 view/survey/survey_medias.php 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/lib/digiquali_control.lib.php b/lib/digiquali_control.lib.php index f5d283dc..d1815940 100644 --- a/lib/digiquali_control.lib.php +++ b/lib/digiquali_control.lib.php @@ -36,7 +36,7 @@ function control_prepare_head(Control $object): array // Global variables definitions. global $conf, $db, $langs; - $head[1][0] = dol_buildpath('/digiquali/view/control/control_medias.php', 1) . '?id=' . $object->id; + $head[1][0] = dol_buildpath('/digiquali/view/object_medias.php', 1) . '?id=' . $object->id . '&object_type=control'; $head[1][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('Medias') : ''; $head[1][2] = 'medias'; diff --git a/lib/digiquali_survey.lib.php b/lib/digiquali_survey.lib.php index eb6ed6f4..383c8730 100644 --- a/lib/digiquali_survey.lib.php +++ b/lib/digiquali_survey.lib.php @@ -36,7 +36,7 @@ function survey_prepare_head(Survey $object): array // Global variables definitions global $conf, $langs; - $head[1][0] = dol_buildpath('/digiquali/view/survey/survey_medias.php', 1) . '?id=' . $object->id; + $head[1][0] = dol_buildpath('/digiquali/view/object_medias.php', 1) . '?id=' . $object->id . '&object_type=survey'; $head[1][1] = $conf->browser->layout != 'phone' ? '' . $langs->trans('Medias') : ''; $head[1][2] = 'medias'; diff --git a/view/control/control_medias.php b/view/control/control_medias.php deleted file mode 100644 index 66ce0f16..00000000 --- a/view/control/control_medias.php +++ /dev/null @@ -1,157 +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 . - */ - -/** - * \file view/control/control_medias.php - * \ingroup digiquali - * \brief Tab for medias on Control - */ - -// 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'); -} - -// Libraries -require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; - -require_once __DIR__ . '/../../class/control.class.php'; -require_once __DIR__ . '/../../class/question.class.php'; -require_once __DIR__ . '/../../lib/digiquali_control.lib.php'; - -// Global variables definitions -global $conf, $db,$hookmanager, $langs, $user; - -// Load translation files required by the page -saturne_load_langs(["companies"]); - -// Get parameters -$id = GETPOST('id', 'int'); -$ref = GETPOST('ref', 'alpha'); -$action = GETPOST('action', 'aZ09'); -$cancel = GETPOST('cancel', 'aZ09'); -$backtopage = GETPOST('backtopage', 'alpha'); - -// Initialize technical objects -$object = new Control($db); -$controldet = new ControlLine($db); -$question = new Question($db); -$extrafields = new ExtraFields($db); -$project = new Project($db); - -// View objects -$form = new Form($db); - -$hookmanager->initHooks(array('controlmedia', 'globalcard')); // Note that conf->hooks_modules contains array - -// Fetch optionals attributes and labels -$extrafields->fetch_name_optionals_label($object->table_element); - -// Load object -include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals -if ($id > 0 || !empty($ref)) { - $upload_dir = $conf->digiquali->multidir_output[$object->entity]."/".$object->id; -} - -$permissiontoread = $user->rights->digiquali->control->read; -$permissiontoadd = $user->rights->digiquali->control->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php -$permissiontodelete = $user->rights->digiquali->control->delete || ($permissiontoadd && isset($object->status)); -$permissionnote = $user->rights->digiquali->control->write; // Used by the include of actions_setnotes.inc.php -$upload_dir = $conf->digiquali->multidir_output[$conf->entity]; - -// Security check (enable the most restrictive one) -if ($user->socid > 0) accessforbidden(); -if ($user->socid > 0) $socid = $user->socid; -if (empty($conf->digiquali->enabled)) accessforbidden(); -if (!$permissiontoread) accessforbidden(); - -/* - * Action - */ - -$parameters = ['id' => $id]; -$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks. -if ($resHook < 0) { - setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); -} - -if (empty($resHook)) { - // Actions set_thirdparty, set_project - require_once __DIR__ . '/../../../saturne/core/tpl/actions/banner_actions.tpl.php'; -} - -/* - * View - */ - -$help_url = ''; -$title = $langs->trans('Medias'); - -saturne_header(0,'', $title, $help_url); - -if ($id > 0 || !empty($ref)) { - $object->fetch_thirdparty(); - - $head = control_prepare_head($object); - - print saturne_get_fiche_head($object, 'medias', $title); - - // Object card - // ------------------------------------------------------------ - - saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); - - print '
'; - print '
'; - - print load_fiche_titre($langs->trans('MediaGalleryQuestionAnswers'), '', ''); - - $object->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet'); - $questionIds = $object->linkedObjectsIds; - $questionsLinked = $object->linkedObjects; - $relativepath = 'digiquali/medias/thumbs'; - $linkedMedias = 0; - - if (is_array($questionsLinked['digiquali_question']) && !empty($questionsLinked['digiquali_question'])) { - foreach ($questionsLinked['digiquali_question'] as $questionLinked) { - if ($questionLinked->authorize_answer_photo > 0 && file_exists($conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref)) { - print '
'; - print '' . $questionLinked->ref . ''; - print '
'; - print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, ($conf->global->DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY ? 'large' : 'medium'), '', 0, 0, 0, 200, 200, 0, 0, 0, 'control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, null, '', 0, 0); - print '
'; - print '
'; - $linkedMedias++; - } - } - } - - if ($linkedMedias == 0) { - print $langs->trans('NoControlAnswersPhoto'); - } - - print '
'; - print dol_get_fiche_end(); -} - -// End of page -llxFooter(); -$db->close(); diff --git a/view/object_medias.php b/view/object_medias.php new file mode 100644 index 00000000..14ad5d9a --- /dev/null +++ b/view/object_medias.php @@ -0,0 +1,129 @@ + + * + * 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 view/object_medias.php + * \ingroup digiquali + * \brief Tab for medias on object + */ + +// 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'); +} + +// Get module parameters +$objectType = GETPOST('object_type', 'alpha'); + +// Load DigiQuali libraries +require_once __DIR__ . '/../class/' . $objectType . '.class.php'; +require_once __DIR__ . '/../lib/digiquali_' . $objectType . '.lib.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $user; + +// Load translation files required by the page +saturne_load_langs(); + +// Get parameters +$id = GETPOST('id', 'int'); +$ref = GETPOST('ref', 'alpha'); +$action = GETPOST('action', 'aZ09'); +$cancel = GETPOST('cancel', 'aZ09'); +$backtopage = GETPOST('backtopage', 'alpha'); + +// Initialize technical objects +$className = ucfirst($objectType); +$object = new $className($db); + +$hookmanager->initHooks([$object->element . 'media', 'globalcard']); // Note that conf->hooks_modules contains array + +// Load object +require_once DOL_DOCUMENT_ROOT . '/core/actions_fetchobject.inc.php'; // Must be included, not include_once + +// Security check - Protection if external user +$permissionToRead = $user->rights->digiquali->$objectType->read; +saturne_check_access($permissionToRead); + +/* + * Action + */ + +$parameters = ['id' => $id]; +$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($resHook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} + +if (empty($resHook)) { + // Actions set_thirdparty, set_project + require_once __DIR__ . '/../../saturne/core/tpl/actions/banner_actions.tpl.php'; +} + +/* + * View + */ + +$title = $langs->trans('Medias'); +$helpUrl = 'FR:Module_DigiQuali'; + +saturne_header(0,'', $title, $helpUrl); + +if ($id > 0 || !empty($ref)) { + saturne_get_fiche_head($object, 'medias', $title); + saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); + + print '
'; + print '
'; + + print load_fiche_titre($langs->trans('MediaGalleryQuestionAnswers'), '', ''); + + $object->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet'); + $questionIds = $object->linkedObjectsIds; + $questionsLinked = $object->linkedObjects; + $linkedMedias = 0; + + if (is_array($questionsLinked['digiquali_question']) && !empty($questionsLinked['digiquali_question'])) { + foreach ($questionsLinked['digiquali_question'] as $questionLinked) { + if ($questionLinked->authorize_answer_photo > 0 && file_exists($conf->digiquali->multidir_output[$conf->entity] . '/' . $object->element . '/' . $object->ref . '/answer_photo/' . $questionLinked->ref)) { + print '
'; + print '' . $questionLinked->ref . ''; + print '
'; + $confName = 'DIGIQUALI_' . dol_strtoupper($object->element) . '_USE_LARGE_MEDIA_IN_GALLERY'; + print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/' . $object->element . '/' . $object->ref . '/answer_photo/' . $questionLinked->ref, ($conf->global->$confName ? 'large' : 'medium'), '', 0, 0, 0, 200, 200, 0, 0, 0, $object->element . '/' . $object->ref . '/answer_photo/' . $questionLinked->ref, null, '', 0, 0); + print '
'; + print '
'; + $linkedMedias++; + } + } + } + + if ($linkedMedias == 0) { + print $langs->trans('NoObjectLineAnswersPhoto', dol_strtolower($langs->transnoentities(ucfirst($object->element)))); + } + + print '
'; + print dol_get_fiche_end(); +} + +// End of page +llxFooter(); +$db->close(); diff --git a/view/survey/survey_medias.php b/view/survey/survey_medias.php deleted file mode 100644 index 66ce0f16..00000000 --- a/view/survey/survey_medias.php +++ /dev/null @@ -1,157 +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 . - */ - -/** - * \file view/control/control_medias.php - * \ingroup digiquali - * \brief Tab for medias on Control - */ - -// 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'); -} - -// Libraries -require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; - -require_once __DIR__ . '/../../class/control.class.php'; -require_once __DIR__ . '/../../class/question.class.php'; -require_once __DIR__ . '/../../lib/digiquali_control.lib.php'; - -// Global variables definitions -global $conf, $db,$hookmanager, $langs, $user; - -// Load translation files required by the page -saturne_load_langs(["companies"]); - -// Get parameters -$id = GETPOST('id', 'int'); -$ref = GETPOST('ref', 'alpha'); -$action = GETPOST('action', 'aZ09'); -$cancel = GETPOST('cancel', 'aZ09'); -$backtopage = GETPOST('backtopage', 'alpha'); - -// Initialize technical objects -$object = new Control($db); -$controldet = new ControlLine($db); -$question = new Question($db); -$extrafields = new ExtraFields($db); -$project = new Project($db); - -// View objects -$form = new Form($db); - -$hookmanager->initHooks(array('controlmedia', 'globalcard')); // Note that conf->hooks_modules contains array - -// Fetch optionals attributes and labels -$extrafields->fetch_name_optionals_label($object->table_element); - -// Load object -include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals -if ($id > 0 || !empty($ref)) { - $upload_dir = $conf->digiquali->multidir_output[$object->entity]."/".$object->id; -} - -$permissiontoread = $user->rights->digiquali->control->read; -$permissiontoadd = $user->rights->digiquali->control->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php -$permissiontodelete = $user->rights->digiquali->control->delete || ($permissiontoadd && isset($object->status)); -$permissionnote = $user->rights->digiquali->control->write; // Used by the include of actions_setnotes.inc.php -$upload_dir = $conf->digiquali->multidir_output[$conf->entity]; - -// Security check (enable the most restrictive one) -if ($user->socid > 0) accessforbidden(); -if ($user->socid > 0) $socid = $user->socid; -if (empty($conf->digiquali->enabled)) accessforbidden(); -if (!$permissiontoread) accessforbidden(); - -/* - * Action - */ - -$parameters = ['id' => $id]; -$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks. -if ($resHook < 0) { - setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); -} - -if (empty($resHook)) { - // Actions set_thirdparty, set_project - require_once __DIR__ . '/../../../saturne/core/tpl/actions/banner_actions.tpl.php'; -} - -/* - * View - */ - -$help_url = ''; -$title = $langs->trans('Medias'); - -saturne_header(0,'', $title, $help_url); - -if ($id > 0 || !empty($ref)) { - $object->fetch_thirdparty(); - - $head = control_prepare_head($object); - - print saturne_get_fiche_head($object, 'medias', $title); - - // Object card - // ------------------------------------------------------------ - - saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); - - print '
'; - print '
'; - - print load_fiche_titre($langs->trans('MediaGalleryQuestionAnswers'), '', ''); - - $object->fetchObjectLinked($object->fk_sheet, 'digiquali_sheet'); - $questionIds = $object->linkedObjectsIds; - $questionsLinked = $object->linkedObjects; - $relativepath = 'digiquali/medias/thumbs'; - $linkedMedias = 0; - - if (is_array($questionsLinked['digiquali_question']) && !empty($questionsLinked['digiquali_question'])) { - foreach ($questionsLinked['digiquali_question'] as $questionLinked) { - if ($questionLinked->authorize_answer_photo > 0 && file_exists($conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref)) { - print '
'; - print '' . $questionLinked->ref . ''; - print '
'; - print saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, ($conf->global->DIGIQUALI_CONTROL_USE_LARGE_MEDIA_IN_GALLERY ? 'large' : 'medium'), '', 0, 0, 0, 200, 200, 0, 0, 0, 'control/' . $object->ref . '/answer_photo/' . $questionLinked->ref, null, '', 0, 0); - print '
'; - print '
'; - $linkedMedias++; - } - } - } - - if ($linkedMedias == 0) { - print $langs->trans('NoControlAnswersPhoto'); - } - - print '
'; - print dol_get_fiche_end(); -} - -// End of page -llxFooter(); -$db->close(); From 5d300490ca2680975998fdfba2cbbec05846d6a3 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 20:28:28 +0100 Subject: [PATCH 14/64] #1662 [Admin] add: config page for survey --- admin/survey.php | 138 ++++++++++++++++++++++++++++ class/actions_digiquali.class.php | 40 ++++++-- core/modules/modDigiQuali.class.php | 3 +- langs/fr_FR/digiquali.lang | 8 +- lib/digiquali.lib.php | 2 +- 5 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 admin/survey.php diff --git a/admin/survey.php b/admin/survey.php new file mode 100644 index 00000000..129ac62e --- /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'; + +/* + * Numbering module line + */ + +$object = new SurveyLine($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'; + +// 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 dbca3748..e048f8fb 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -374,20 +374,42 @@ 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) { + // Do something only for the current context +// if ($parameters['currentcontext'] == 'digiqualiadmindocuments') { +// $constArray['digiquali'] = [ +//// 'controldocument' => [ +//// 'name' => 'ControlDocumentDisplayMedias', +//// 'description' => 'ControlDocumentDisplayMediasDescription', +//// 'code' => 'DIGIQUALI_CONTROLDOCUMENT_DISPLAY_MEDIAS' +//// ] +// ]; +// $this->results = $constArray; +// return 1; +// } + + if (strpos($parameters['context'], 'surveyadmin') !== false) { $constArray['digiquali'] = [ - 'controldocument' => [ - 'name' => 'ControlDocumentDisplayMedias', - 'description' => 'ControlDocumentDisplayMediasDescription', - 'code' => 'DIGIQUALI_CONTROLDOCUMENT_DISPLAY_MEDIAS' + 'DisplayMedias' => [ + 'name' => 'DisplayMediasSample', + 'description' => 'DisplayMediasSampleDescription', + '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; diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 134e7aca..71a10dd5 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -126,7 +126,8 @@ public function __construct($db) 'publicsurvey', 'digiqualiadmindocuments', 'projecttaskscard', - 'main' + 'main', + 'surveyadmin' ], // Set this to 1 if features of module are opened to external users 'moduleforexternal' => 0, diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index cf4c83f9..312f332d 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -41,7 +41,13 @@ 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 -# +# Survey - Questionnaire +DisplayMediasSample = Afficher les médias d'exemple des questions +DisplayMediasSampleDescription = Afficher les médias d'exemple des questions dans le questionnaire +ExtrafieldsSurveyManagement = Gestion des attributs supplémentaires des questionnaires + + + # Questions # diff --git a/lib/digiquali.lib.php b/lib/digiquali.lib.php index 978106a7..74cf7c89 100644 --- a/lib/digiquali.lib.php +++ b/lib/digiquali.lib.php @@ -58,7 +58,7 @@ function digiquali_admin_prepare_head(): array $head[$h][2] = 'control'; $h++; - $head[$h][0] = dol_buildpath('/saturne/admin/object.php', 1) . '?module_name=DigiQuali&object_type=survey'; + $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++; From 915ed78bf577cdf6cb304eaf3ccfd40fcd80dbc5 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 20:33:14 +0100 Subject: [PATCH 15/64] #1662 [Triggers] add: missing survey trigger --- ...9_modDigiQuali_DigiQualiTriggers.class.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php index f2d2fbc9..411bfea7 100644 --- a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php +++ b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php @@ -163,6 +163,23 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf $actioncomm->create($user); break; + case 'SURVEY_CREATE' : + 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' : @@ -207,6 +224,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf 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); @@ -214,6 +232,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') . '
'; @@ -312,8 +331,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf $actioncomm->create($user); break; - case 'CONTROL_SAVEANSWER' : - case 'SURVEY_SAVEANSWER' : + case 'OBJECT_SAVEANSWER' : $actioncomm->code = 'AC_' . strtoupper($object->element) . 'SAVEANSWER'; $actioncomm->label = $langs->transnoentities('AnswerSaveTrigger'); $actioncomm->create($user); From b171429d0c519d658f13e428ca8f1598647e1a0c Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 20:41:07 +0100 Subject: [PATCH 16/64] #1662 [JS/CSS] add: generic call and clean --- css/digiquali.min.css | 2 +- css/scss/page/_control.scss | 2 +- js/digiquali.min.js | 2 +- js/modules/control.js | 171 +----------------------------- js/modules/object.js | 205 ++++++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+), 172 deletions(-) create mode 100644 js/modules/object.js diff --git a/css/digiquali.min.css b/css/digiquali.min.css index 5678cc82..fb1f6f5b 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}.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/js/digiquali.min.js b/js/digiquali.min.js index 7d1f7edc..8e5208f7 100644 --- a/js/digiquali.min.js +++ b/js/digiquali.min.js @@ -1 +1 @@ -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,o=void 0;for(t in window.digiquali)for(o in window.digiquali[t].init&&window.digiquali[t].init(),window.digiquali[t])window.digiquali[t]&&window.digiquali[t][o]&&window.digiquali[t][o].init&&window.digiquali[t][o].init();window.digiquali.scriptsLoaded=!0}},window.digiquali.refresh=function(){var t=void 0,o=void 0;for(t in window.digiquali)for(o in window.digiquali[t].refresh&&window.digiquali[t].refresh(),window.digiquali[t])window.digiquali[t]&&window.digiquali[t][o]&&window.digiquali[t][o].refresh&&window.digiquali[t][o].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",".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),$(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.selectAnswer=function(t){var o=$(this).closest(".select-answer.answer-cell"),e=o.attr("data-questionId"),i=$(this).closest(".table-id-"+e).attr("data-publicInterface"),n=$(this).closest(".table-id-"+e).attr("data-autoSave");let a="";var s=$(this).hasClass("answer")?$(this).attr("value"):$(this).val(),l=$(this).closest(".table-id-"+e).find("#comment"+e).val();if($(this).closest(".table-cell").hasClass("select-answer")){if($(this).hasClass("multiple-answers")){$(this).closest("span").toggleClass("active");let t=[];o.find(".multiple-answers.active").each(function(){t.push($(this).attr("value"))}),a=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"),a=s;$(this).hasClass("active")?(o=$(this).closest(".answer-cell").find(".answer-color-"+$(this).attr("value")).val(),$(this).attr("style",$(this).attr("style")+" background:"+o+";")):$(this).attr("style",$(this).attr("style")+" background:#fff;"),$(this).closest(".answer-cell").find(".question-answer").val(a)}i||1!=n||$(this).hasClass("multiple-answers")?window.digiquali.control.updateButtonsStatus():window.digiquali.control.saveAnswer(e,a,l)},window.digiquali.control.disableOtherSelectors=function(t){var o=document.getElementById("createControlForm"),o=new FormData(o),e=$(this).attr("id");0<=o.get(e)?$(".control-table.linked-objects").find("select").not("#"+e).attr("disabled",1):$(".control-table.linked-objects").find("select").not("#"+e).removeAttr("disabled")},window.digiquali.control.showCommentUnsaved=function(t){$(this).hasClass("show-comment-unsaved-message")||($(this).after('

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 i=void 0,t=void 0;for(i in window.digiquali)for(t in window.digiquali[i].init&&window.digiquali[i].init(),window.digiquali[i])window.digiquali[i]&&window.digiquali[i][t]&&window.digiquali[i][t].init&&window.digiquali[i][t].init();window.digiquali.scriptsLoaded=!0}},window.digiquali.refresh=function(){var i=void 0,t=void 0;for(i in window.digiquali)for(t in window.digiquali[i].refresh&&window.digiquali[i].refresh(),window.digiquali[i])window.digiquali[i]&&window.digiquali[i][t]&&window.digiquali[i][t].refresh&&window.digiquali[i][t].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)},window.digiquali.control.getAnswerCounter=function(i){let t=0;jQuery("#tablelines").children().each(function(){0<$(this).find(".answer.active").length&&(t+=1)}),document.cookie="answerCounter="+t},window.digiquali.control.showSelectObjectLinked=function(){var i=$(this).val(),t=window.saturne.toolbox.getToken(),o=window.saturne.toolbox.getQuerySeparator(document.URL),o=document.URL+o+"fk_sheet="+i+"&token="+t;window.saturne.loader.display($(".linked-objects")),$.ajax({url:o,type:"POST",processData:!1,contentType:!1,success:function(i){$(".linked-objects").replaceWith($(i).find(".linked-objects"))},error:function(){}})},window.digiquali.control.copyToClipboard=function(i){var t=$(".copy-to-clipboard").attr("value");navigator.clipboard.writeText(t).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(i){var t=document.getElementById("add_control_equipment"),t=new FormData(t),o=window.saturne.toolbox.getToken(),t=t.get("productId"),o=document.URL+"&token="+o;o+="&fk_product="+t,window.saturne.loader.display($(".product-lot")),$.ajax({url:o,type:"POST",processData:!1,contentType:!1,success:function(i){$(".product-lot").replaceWith($(i).find(".product-lot"))},error:function(){}})},window.digiquali.control.switchPublicControlView=function(i){var t=$(this).find(".public-control-view").val(),o=window.saturne.toolbox.getToken();let e=document.URL+"&token="+o;e+=0==t?"&show_control_list=1":"&show_last_control=1",window.saturne.loader.display($(".signature-container")),$.ajax({url:e,type:"POST",processData:!1,contentType:!1,success:function(i){$("#publicControlHistory").replaceWith($(i).find("#publicControlHistory"))},error:function(){}})},window.digiquali.control.showOnlyQuestionsWithNoAnswer=function(){var i=window.saturne.toolbox.getQuerySeparator(document.URL),t=window.saturne.toolbox.getToken();let o;o=$(this).hasClass("fa-toggle-off")?1:0,window.saturne.loader.display($(this)),$.ajax({url:document.URL+i+"action=show_only_questions_with_no_answer&token="+t,type:"POST",processData:!1,data:JSON.stringify({showOnlyQuestionsWithNoAnswer:o}),contentType:!1,success:function(i){$(".progress-info").replaceWith($(i).find(".progress-info")),$(".question-answer-container").replaceWith($(i).find(".question-answer-container"))},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 i=document.getElementById("createObjectForm"),i=new FormData(i),t=$(this).attr("id");0<=i.get(t)?$(".object-table.linked-objects").find("select").not("#"+t).attr("disabled",1):$(".object-table.linked-objects").find("select").not("#"+t).removeAttr("disabled")},window.digiquali.object.selectAnswer=function(){var t=$(this).closest(".select-answer.answer-cell"),i=t.attr("data-questionId"),o=$(this).closest(".table-id-"+i).attr("data-publicInterface"),e=$(this).closest(".table-id-"+i).attr("data-autoSave");let n="";var a=$(this).hasClass("answer")?$(this).attr("value"):$(this).val(),s=$(this).closest(".table-id-"+i).find("#comment"+i).val();if($(this).closest(".table-cell").hasClass("select-answer")){if($(this).hasClass("multiple-answers")){$(this).closest("span").toggleClass("active");let i=[];t.find(".multiple-answers.active").each(function(){i.push($(this).attr("value"))}),n=i}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")?(t=$(this).closest(".answer-cell").find(".answer-color-"+$(this).attr("value")).val(),$(this).attr("style",$(this).attr("style")+" background:"+t+";")):$(this).attr("style",$(this).attr("style")+" background:#fff;"),$(this).closest(".answer-cell").find(".question-answer").val(n)}o||1!=e||$(this).hasClass("multiple-answers")?window.digiquali.object.updateButtonsStatus():window.digiquali.object.saveAnswer(i,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(i,t,o){var e=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".table-id-"+i)),$.ajax({url:document.URL+n+"action=save&token="+e,type:"POST",data:JSON.stringify({autoSave:!0,questionId:i,answer:t,comment:o}),processData:!1,contentType:!1,success:function(i){$(".fiche").replaceWith($(i).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(i){$(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(i){$("#dialogforpopup").attr("style","border:")},window.digiquali.question.showPhoto=function(){var i=$(this).closest(".question-table").find(".linked-medias");i.hasClass("hidden")?(i.attr("style",""),i.removeClass("hidden")):(i.attr("style","display:none"),i.addClass("hidden"))},window.digiquali.question.selectAnswerPicto=function(i){var t=$(this).closest(".wpeo-dropdown");$(this).closest(".content").removeClass("active"),t.find(".dropdown-toggle span").hide(),t.find(".dropdown-toggle.button-picto").html($(this).closest(".wpeo-tooltip-event").html()),t.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() {} + }); +}; From 4cd561326f198092566146fcc9dd8f01e1fb0141 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 20:44:41 +0100 Subject: [PATCH 17/64] #1662 [TPL] add: rework answer TPL --- ...wers.tpl.php => digiquali_answers.tpl.php} | 151 +++++++++-------- .../tpl/digiquali_answers_save_action.tpl.php | 104 ++++++++++++ ...iquali_control_answers_save_action.tpl.php | 87 ---------- documents/temp/index.php | 2 - public/control/public_survey.php | 155 ------------------ public/index.php | 3 + public/public_answer.php | 143 ++++++++++++++++ 7 files changed, 335 insertions(+), 310 deletions(-) rename core/tpl/{digiquali_control_answers.tpl.php => digiquali_answers.tpl.php} (50%) create mode 100644 core/tpl/digiquali_answers_save_action.tpl.php delete mode 100644 core/tpl/digiquali_control_answers_save_action.tpl.php delete mode 100644 documents/temp/index.php delete mode 100644 public/control/public_survey.php create mode 100644 public/index.php create mode 100644 public/public_answer.php diff --git a/core/tpl/digiquali_control_answers.tpl.php b/core/tpl/digiquali_answers.tpl.php similarity index 50% rename from core/tpl/digiquali_control_answers.tpl.php rename to core/tpl/digiquali_answers.tpl.php index 7c000c33..92f9486b 100644 --- a/core/tpl/digiquali_control_answers.tpl.php +++ b/core/tpl/digiquali_answers.tpl.php @@ -1,94 +1,115 @@ 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 ) : ?> - - -
status > 0) ? ' style="pointer-events: none"' : '' ?> data-questionId="id; ?>"> + + type == 'MultipleChoices') : + $answerList = $answer->fetchAll('ASC', 'position', 0, 0, ['customsql' => 't.status > ' . Answer::STATUS_DELETED . ' AND t.fk_question = ' . $question->id]); ?> +
status > 0) ? ' style="pointer-events: none"' : '' ?> data-questionId="id; ?>"> id .'" id="answer'. $item->id .'" value="0">'; + print ''; if (is_array($answerList) && !empty($answerList)) { foreach($answerList as $answerLinked) { - print ''; - print ''; + print ''; + print ''; if (!empty($answerLinked->pictogram)) { print $pictosArray[$answerLinked->pictogram]['picto_source']; } else { @@ -111,16 +132,15 @@ } ?>
- type == 'UniqueChoice' || $item->type == 'OkKo' || $item->type == 'OkKoToFixNonApplicable') : - $answerList = $answer->fetchAll('ASC', 'position', 0, 0, ['customsql' => 't.status > ' . Answer::STATUS_DELETED . ' AND t.fk_question = ' . $item->id]); - ?> -
status > 0) ? 'style="pointer-events: none"' : '' ?> data-questionId="id; ?>"> + type == 'UniqueChoice' || $question->type == 'OkKo' || $question->type == 'OkKoToFixNonApplicable') : + $answerList = $answer->fetchAll('ASC', 'position', 0, 0, ['customsql' => 't.status > ' . Answer::STATUS_DELETED . ' AND t.fk_question = ' . $question->id]); ?> +
status > 0) ? 'style="pointer-events: none"' : '' ?> data-questionId="id; ?>"> id .'" id="answer'. $item->id .'" value="0">'; + print ''; if (is_array($answerList) && !empty($answerList)) { foreach($answerList as $answerLinked) { - print ''; - print ''; + print ''; + print ''; if (!empty($answerLinked->pictogram)) { print $pictosArray[$answerLinked->pictogram]['picto_source']; } else { @@ -128,28 +148,27 @@ } print ''; } - } - ?> + } ?>
- type == 'Percentage') : ?> -
" data-questionId="id; ?>"> + type == 'Percentage') : ?> +
" data-questionId="id; ?>"> '; print $langs->transnoentities('Answer') . ' : '; print ''; - print ''; ?> - status > $object::STATUS_DRAFT ? 'disabled' : '') .' name="answer'. $item->id .'" id="answer'. $item->id .'" type="number" min="0" max="100" onkeyup=window.saturne.utils.enforceMinMax(this) class="input-answer ' . ($object->status > 0 ? 'disable' : '') . ' ' . ($questionAnswer == $answerLinked->position ? 'active' : '') . '" value="'. $questionAnswer .'"> %'; + print ''; + print 'status > $object::STATUS_DRAFT ? ' disabled' : '') . ' name="answer' . $question->id . '" id="answer' . $question->id . '" type="number" min="0" max="100" class="input-answer ' . ($object->status > 0 ? 'disable' : '') . ' ' . ($questionAnswer == $answerLinked->position ? 'active' : '') . '" value="' . $questionAnswer . '"> %'; print ''; ?>
- type == 'Range') : ?> -
" data-questionId="id; ?>"> + type == 'Range') : ?> +
" data-questionId="id; ?>"> '; print $langs->transnoentities('Answer') . ' : '; print ''; print ''; - print 'status > $object::STATUS_DRAFT ? 'disabled' : '') .' name="answer'. $item->id .'" id="answer'. $item->id .'" type="number" class="input-answer ' . ($object->status > 0 ? 'disable' : '') . ' ' . ($questionAnswer == $answerLinked->position ? 'active' : '') . '" value="'. $questionAnswer .'">'; + print 'status > $object::STATUS_DRAFT ? 'disabled' : '') .' name="answer' . $question->id . '" id="answer' . $question->id . '" type="number" class="input-answer ' . ($object->status > 0 ? 'disable' : '') . ' ' . ($questionAnswer == $answerLinked->position ? 'active' : '') . '" value="' . $questionAnswer . '">'; print ''; ?>
@@ -158,5 +177,5 @@
+ * + * 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_sava_action.tpl.php + * \ingroup digiquali + * \brief Template page for answers save action + */ + +/** + * The following vars must be defined: + * Global : $conf, $db, $langs, $user + * Parameters : $action + * Objects : $object, $objectLine, $sheet + */ + +if ($action == 'save') { + $data = json_decode(file_get_contents('php://input'), true); + + $sheet->fetch($object->fk_sheet); + $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); + $questionIds = $object->linkedObjectsIds['digiquali_question']; + foreach ($questionIds as $questionId) { + $objectLineTmp = $objectLine->fetchFromParentWithQuestion($object->id, $questionId); + if (is_array($objectLineTmp) && !empty($objectLineTmp)) { + $objectLineTmp = array_shift($objectLineTmp); + + // Save answer value + if ($data['autoSave'] && $questionId == $data['questionId']) { + $questionAnswer = $data['answer']; + } else { + $questionAnswer = GETPOST('answer' . $questionId); + } + if (!empty($questionAnswer)) { + $objectLineTmp->answer = $questionAnswer; + } + + // Save answer comment + if ($data['autoSave'] && $questionId == $data['questionId']) { + $comment = $data['comment']; + } else { + $comment = GETPOST('comment' . $questionId); + } + if (dol_strlen($comment) > 0) { + $objectLineTmp->comment = $comment; + } else { + $objectLine->comment = ''; + } + + $objectLineTmp->update($user); + } else { + $objectLine->ref = $objectLine->getNextNumRef(); + $fk_element = 'fk_'. $object->element; + $objectLine->$fk_element = $object->id; + $objectLine->fk_question = $questionId; + + // Save answer value + if ($data['autoSave'] && $questionId == $data['questionId']) { + $questionAnswer = $data['answer']; + } else { + $questionAnswer = GETPOST('answer' . $questionId); + } + if (!empty($questionAnswer)) { + $objectLine->answer = $questionAnswer; + } + + // Save answer comment + if ($data['autoSave'] && $questionId == $data['questionId']) { + $comment = $data['comment']; + } else { + $comment = GETPOST('comment' . $questionId); + } + if (dol_strlen($comment) > 0) { + $objectLine->comment = $comment; + } else { + $objectLine->comment = ''; + } + + $objectLine->entity = $conf->entity; + $objectLine->status = 1; + + $objectLine->create($user); + } + } + + $object->call_trigger('OBJECT_SAVEANSWER', $user); + setEventMessages($langs->trans('AnswerSaved'), []); + header('Location: ' . $_SERVER['PHP_SELF'] . (dol_strlen(GETPOST('track_id')) > 0 ? '?action=saved_success&object_type=' . GETPOST('object_type') : '?id=' . GETPOST('id'))); + exit; +} diff --git a/core/tpl/digiquali_control_answers_save_action.tpl.php b/core/tpl/digiquali_control_answers_save_action.tpl.php deleted file mode 100644 index 0d8373f2..00000000 --- a/core/tpl/digiquali_control_answers_save_action.tpl.php +++ /dev/null @@ -1,87 +0,0 @@ -fetch($object->fk_sheet); - $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); - $questionIds = $object->linkedObjectsIds['digiquali_question']; - - foreach ($questionIds as $questionId) { - $controldettmp = $controldet; - //fetch controldet avec le fk_question et fk_control, s'il existe on l'update sinon on le crée - $result = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); - - if ($result > 0 && is_array($result)) { - $controldettmp = array_shift($result); - //sauvegarder réponse - if ($data['autoSave'] && $questionId == $data['questionId']) { - $questionAnswer = $data['answer']; - } else { - $questionAnswer = GETPOST('answer' . $questionId); - } - - if (!empty($questionAnswer)) { - $controldettmp->answer = $questionAnswer; - } - - //sauvegarder commentaire - if ($data['autoSave'] && $questionId == $data['questionId']) { - $comment = $data['comment']; - } else { - $comment = GETPOST('comment' . $questionId); - } - - if (dol_strlen($comment) > 0) { - $controldettmp->comment = $comment; - } - - $question->fetch($questionId); - $controldettmp->update($user); - } else { - $controldettmp = $controldet; - - $controldettmp->ref = $controldettmp->getNextNumRef(); - - $controldettmp->fk_control = $object->id; - $controldettmp->fk_question = $questionId; - - //sauvegarder réponse - if ($data['autoSave'] && $questionId == $data['questionId']) { - $questionAnswer = $data['answer']; - } else { - $questionAnswer = GETPOST('answer' . $questionId); - } - - if (!empty($questionAnswer)) { - $controldettmp->answer = $questionAnswer; - } else { - $controldettmp->answer = ''; - } - - //sauvegarder commentaire - if ($data['autoSave'] && $questionId == $data['questionId']) { - $comment = $data['comment']; - } else { - $comment = GETPOST('comment' . $questionId); - } - - if (dol_strlen($comment) > 0) { - $controldettmp->comment = $comment; - } else { - $controldettmp->comment = ''; - } - - $question->fetch($questionId); - - $controldettmp->entity = $conf->entity; - $controldettmp->insert($user); - } - } - - $object->call_trigger('CONTROL_SAVEANSWER', $user); - setEventMessages($langs->trans('AnswerSaved'), []); - header('Location: ' . $_SERVER['PHP_SELF'] . (dol_strlen(GETPOST('track_id')) > 0 ? '?action=saved_success' : '?id=' . GETPOST('id'))); - exit; -} diff --git a/documents/temp/index.php b/documents/temp/index.php deleted file mode 100644 index cd6990e2..00000000 --- a/documents/temp/index.php +++ /dev/null @@ -1,2 +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 . - */ - -/** - * \file public/control/public_survey.php - * \ingroup digiquali - * \brief Public page to answer control questions. - */ - -if (!defined('NOREQUIREUSER')) { - define('NOREQUIREUSER', '1'); -} -if (!defined('NOTOKENRENEWAL')) { - define('NOTOKENRENEWAL', '1'); -} -if (!defined('NOREQUIREMENU')) { - define('NOREQUIREMENU', 1); -} -if (!defined('NOREQUIREHTML')) { - define('NOREQUIREHTML', '1'); -} -if (!defined('NOLOGIN')) { // This means this output page does not require to be logged. - define('NOLOGIN', '1'); -} -if (!defined('NOCSRFCHECK')) { // We accept to go on this page from external website. - define('NOCSRFCHECK', '1'); -} -if (!defined('NOIPCHECK')) { // Do not check IP defined into conf $dolibarr_main_restrict_ip. - define('NOIPCHECK', '1'); -} -if (!defined('NOBROWSERNOTIF')) { - define('NOBROWSERNOTIF', '1'); -} - -// 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 . '/user/class/user.class.php'; -require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmdirectory.class.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - -// Load DigiQuali libraries. -require_once __DIR__ . '/../../../digiquali/class/control.class.php'; -require_once __DIR__ . '/../../../digiquali/class/sheet.class.php'; -require_once __DIR__ . '/../../../digiquali/class/question.class.php'; -require_once __DIR__ . '/../../../digiquali/class/answer.class.php'; -require_once __DIR__ . '/../../../digiquali/lib/digiquali_sheet.lib.php'; -require_once __DIR__ . '/../../../digiquali/lib/digiquali_answer.lib.php'; - -// Global variables definitions. -global $conf, $db, $hookmanager, $langs; - -// Load translation files required by the page. -saturne_load_langs(); - -// Get parameters. -$track_id = GETPOST('track_id', 'alpha'); -$entity = GETPOST('entity'); -$action = GETPOST('action'); -$subaction = GETPOST('subaction'); - -// Initialize technical objects. -$object = new Control($db); -$controldet = new ControlLine($db); -$question = new Question($db); -$answer = new Answer($db); -$sheet = new Sheet($db); -$user = new User($db); - -$hookmanager->initHooks(['publicsurvey']); // Note that conf->hooks_modules contains array. - -if (!isModEnabled('multicompany')) { - $entity = $conf->entity; -} - -$conf->setEntityValues($db, $entity); - -// Load object. -$object->fetch(0, '', ' AND track_id =' . "'" . $track_id . "'"); - -/* - * Actions -*/ - -// Set user for action update and insert for prevent error on public interface -$user->id = 1; - -require_once __DIR__ . '/../../core/tpl/digiquali_control_answers_save_action.tpl.php'; - -/* - * View - */ - -$title = $langs->trans('PublicControl'); - -$conf->dol_hide_topmenu = 1; -$conf->dol_hide_leftmenu = 1; - -saturne_header(1, '', $title); - -if ($action == 'saved_success' || $object->status > $object::STATUS_DRAFT) { - print '
'; - print '
' . $langs->trans('YourAnswersHaveBeenSaved') . '
'; - print '
'; -} else { - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); - $questionIds = $sheet->linkedObjectsIds['digiquali_question']; - - print '
'; - print ''; - print ''; - - ?> -
- ' . $conf->global->DIGIQUALI_PUBLIC_SURVEY_TITLE . ''; - print '
'; - $publicInterface = true; - require_once __DIR__ . '/../../core/tpl/digiquali_control_answers.tpl.php'; - print '
'; - print '
'; - print ''; - print '
'; - ?> -
- '; -} - - -llxFooter('', 'public'); -$db->close(); diff --git a/public/index.php b/public/index.php new file mode 100644 index 00000000..e0ee5083 --- /dev/null +++ b/public/index.php @@ -0,0 +1,3 @@ + + * + * 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 public/public_answer.php + * \ingroup digiquali + * \brief Public page to questions answer + */ + +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', 1); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOLOGIN')) { // This means this output page does not require to be logged + define('NOLOGIN', '1'); +} +if (!defined('NOCSRFCHECK')) { // We accept to go on this page from external website + define('NOCSRFCHECK', '1'); +} +if (!defined('NOIPCHECK')) { // Do not check IP defined into conf $dolibarr_main_restrict_ip + define('NOIPCHECK', '1'); +} +if (!defined('NOBROWSERNOTIF')) { + define('NOBROWSERNOTIF', '1'); +} + +// 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'); +} + +// Get module parameters +$objectType = GETPOST('object_type', 'alpha'); + +// Load DigiQuali libraries +require_once __DIR__ . '/../class/' . $objectType . '.class.php'; +require_once __DIR__ . '/../class/sheet.class.php'; +require_once __DIR__ . '/../class/question.class.php'; +require_once __DIR__ . '/../class/answer.class.php'; +require_once __DIR__ . '/../lib/digiquali_sheet.lib.php'; +require_once __DIR__ . '/../lib/digiquali_answer.lib.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs, $user; + +// Load translation files required by the page +saturne_load_langs(); + +// Get parameters +$track_id = GETPOST('track_id', 'alpha'); +$entity = GETPOST('entity'); +$action = GETPOST('action'); +$subaction = GETPOST('subaction'); + +// Initialize technical objects +$className = ucfirst($objectType); +$object = new $className($db); +$className = $className . 'Line'; +$objectLine = new $className($db); +$sheet = new Sheet($db); +$question = new Question($db); +$answer = new Answer($db); + +$hookmanager->initHooks(['publicsurvey']); // Note that conf->hooks_modules contains array + +if (!isModEnabled('multicompany')) { + $entity = $conf->entity; +} + +$conf->setEntityValues($db, $entity); + +// Load object +$object->fetch(0, '', ' AND track_id =' . "'" . $track_id . "'"); + +/* + * Actions +*/ + +// Set user for action update and insert for prevent error on public interface +$user->id = 1; + +require_once __DIR__ . '/../core/tpl/digiquali_answers_save_action.tpl.php'; + +/* + * View + */ + +$title = $langs->trans('PublicAnswer'); + +$conf->dol_hide_topmenu = 1; +$conf->dol_hide_leftmenu = 1; + +saturne_header(1, '', $title); + +if ($action == 'saved_success' || $object->status > $object::STATUS_DRAFT) { + print '
'; + print '
' . $langs->trans('YourAnswersHaveBeenSaved') . '
'; + print '
'; +} else { + print ''; + print ''; + print ''; ?> + +
+ ' . $conf->global->DIGIQUALI_PUBLIC_SURVEY_TITLE . ''; + print '
'; + $publicInterface = true; + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + require_once __DIR__ . '/../core/tpl/digiquali_answers.tpl.php'; + print '
'; + print '
'; + print ''; + print '
'; ?> +
+ '; +} + +llxFooter('', 'public'); +$db->close(); From 53afcfa402c6ea2c6cce7fb3b99266b032d5420b Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Wed, 10 Jan 2024 20:53:41 +0100 Subject: [PATCH 18/64] #1662 [Card] add: clean code survey --- core/tpl/digiquali_survey_list.tpl.php | 652 +++++++++++++++ langs/fr_FR/digiquali.lang | 54 +- view/control/control_card.php | 132 +-- view/survey/survey_card.php | 1035 +++++++++--------------- view/survey/survey_list.php | 315 +++----- 5 files changed, 1165 insertions(+), 1023 deletions(-) create mode 100644 core/tpl/digiquali_survey_list.tpl.php diff --git a/core/tpl/digiquali_survey_list.tpl.php b/core/tpl/digiquali_survey_list.tpl.php new file mode 100644 index 00000000..dfeebbfd --- /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 ''; + +// 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'); + } + 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/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 312f332d..3b4aa22c 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -287,7 +287,6 @@ 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é : @@ -303,7 +302,6 @@ 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 ControlReminder = Rappel d'événement de contrôle @@ -313,7 +311,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 +PublicAnswer = Interface publique de réponse 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 YourAnswersHaveBeenSaved = Vos réponses ont bien été sauvegardées @@ -330,7 +328,7 @@ EnablePublicControlHistoryDescription = Permet l'affichage de l'histori 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 +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 @@ -348,6 +346,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 @@ -372,41 +371,13 @@ controldocument_photo = fiche de controle avec photos # # Data - Donnée -Survey = Questionnaire -SurveysMin = contrôles -TheSurvey = le contrôle -CreateControl = Créer un contrôle -AddControl = Ajouter un contrôle -NewSurvey = Nouveau contrôle -ControlList = Liste des contrôles -Controls = Contrôles -ActionsOnControl = Événements liés au contrôle -SurveysCategoriesArea = Espace des tags/catégories des contrôles -ControlsRepartition = Répartition des contrôles par verdict -ControlsTagsRepartition = Répartition des contrôles par verdict et par catégories -ControlsByFiscalYear = Rapport des contrôles sur l'exercice fiscal -OfControl = du contrôle -PublicControl = Interface publique de 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 -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 -NoProductLot = Pas de numéro de lot/série -ErrorEquipmentLink = L'équipement n'a pas pu être lié -LastControl = Dernier contrôle -ShowObjectControlHistory = Voir l'historique de contrôle -NoControlOnThisObject = Le contrôle initial n'a pas été réalisé, veuillez vous référer à la notice et contacter votre responsable qualité -ControlHistoryLink = Historique de contrôle -EnablePublicControlHistory = Afficher l'historique public de contrôle -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 -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 +Survey = Questionnaire +Surveys = Questionnaires +SurveyList = Liste des questionnaires +NewSurvey = Nouveau questionnaire +TheSurvey = le questionnaire + + # # SurveyDocument - Fiche du Questionnaire @@ -414,12 +385,11 @@ ShowQcFrequencyPublicInterfaceDescription = Option permettant l'affichage l # Data - Donnée SurveyDocument = Fiche du Questionnaire -Surveydocument = Fiche du Questionnaire -SurveyDocumentsMin = fiches du questionnaire +Surveydocument = Fiche du questionnaire surveydocument = fiche du questionnaire surveydocument.odt = Fiche du Questionnaire -surveydocument_photo.odt = Fiche du Questionnaire avec photos surveydocument_photo = fiche du questionnaire avec photos +surveydocument_photo.odt = Fiche du Questionnaire avec photos diff --git a/view/control/control_card.php b/view/control/control_card.php index d1f5ab73..60df5940 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -75,7 +75,7 @@ // Initialize objects // Technical objets $object = new Control($db); -$controldet = new ControlLine($db); +$objectLine = new ControlLine($db); $document = new ControlDocument($db); $signatory = new SaturneSignature($db, 'digiquali'); $controlEquipment = new ControlEquipment($db); @@ -210,7 +210,7 @@ dol_set_user_param($db, $conf, $user, $tabParam); } - require_once __DIR__ . '/../../core/tpl/digiquali_control_answers_save_action.tpl.php'; + require_once __DIR__ . '/../../core/tpl/digiquali_answers_save_action.tpl.php'; // Actions builddoc, forcebuilddoc, remove_file. require_once __DIR__ . '/../../../saturne/core/tpl/documents/documents_action.tpl.php'; @@ -239,61 +239,6 @@ } } - // Action to set status STATUS_VALIDATED - if ($action == 'confirm_setValidated') { - $object->fetch($id); - if ( ! $error) { - $result = $object->validate($user, false); - if ($result > 0) { - $controldet = new ControlLine($db); - $sheet->fetch($object->fk_sheet); - $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); - $questionIds = $object->linkedObjectsIds; - foreach ($questionIds['digiquali_question'] as $questionId) { - $controldettmp = $controldet; - //fetch controldet avec le fk_question et fk_control, s'il existe on l'update sinon on le crée - $result = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); - - //sauvegarder réponse - $questionAnswer = GETPOST('answer'.$questionId); - if (!empty($questionAnswer)) { - $controldettmp->answer = $questionAnswer; - } - - //sauvegarder commentaire - $comment = GETPOST('comment'.$questionId); - if (dol_strlen($comment) > 0) { - $controldettmp->comment = $comment; - } - - if ($result > 0 && is_array($result)) { - $controldettmp = array_shift($result); - - $controldettmp->update($user); - } else { - if (empty($controldettmp->ref)) { - $controldettmp->ref = $controldettmp->getNextNumRef(); - } - $controldettmp->fk_control = $object->id; - $controldettmp->fk_question = $questionId; - $controldettmp->entity = $conf->entity; - - $controldettmp->insert($user); - } - } - // Set validated OK - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation - header('Location: ' . $urltogo); - exit; - } else { - // Set validated KO - if ( ! empty($object->errors)) setEventMessages(null, $object->errors, 'errors'); - else setEventMessages($object->error, null, 'errors'); - } - } - } - // Action to set status STATUS_REOPENED if ($action == 'confirm_setReopened') { $object->fetch($id); @@ -315,43 +260,8 @@ } } - // Action to set status STATUS_LOCKED - if ($action == 'confirm_lock') { - $object->fetch($id); - if (!$error) { - $result = $object->setLocked($user); - if ($result > 0) { - // Set Locked OK - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation - header('Location: ' . $urltogo); - exit; - } elseif (!empty($object->errors)) { // Set Locked KO. - setEventMessages('', $object->errors, 'errors'); - } else { - setEventMessages($object->error, [], 'errors'); - } - } - } - - // Action to set status STATUS_ARCHIVED. - if ($action == 'confirm_archive' && $permissiontoadd) { - $object->fetch($id); - if (!$error) { - $result = $object->setArchived($user); - if ($result > 0) { - // Set Archived OK. - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation. - header('Location: ' . $urltogo); - exit; - } elseif (!empty($object->errors)) { // Set Archived KO. - setEventMessages('', $object->errors, 'errors'); - } else { - setEventMessages($object->error, [], 'errors'); - } - } - } + // Actions confirm_lock, confirm_archive + require_once __DIR__ . '/../../../saturne/core/tpl/object/object_action_workflow.tpl.php'; // Actions to send emails $triggersendname = 'CONTROL_SENTBYMAIL'; @@ -383,7 +293,7 @@ $moreHtmlRight .= ''; print load_fiche_titre($langs->trans('NewControl'), $moreHtmlRight, 'object_' . $object->picto); - print '
'; + print ''; print ''; print ''; if ($backtopage) { @@ -474,7 +384,7 @@ print ''; print '
'; - print ''; + print '
'; print ''; - print ''; - $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; @@ -749,9 +735,25 @@ } } - $averagePercentageQuestions = ($questionCounter > 0) ? ($averagePercentageQuestions / $questionCounter) : 0; + $averagePercentageQuestions = ($percentQuestionCounter > 0) ? ($averagePercentageQuestions / $percentQuestionCounter) : 0; + + if ($percentQuestionCounter > 0) { + print ''; - if ($averagePercentageQuestions > 0) { print ''; +print ''; print ''; diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 5a431ca7..14f54860 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -336,6 +336,7 @@ GoToPublicAnswerPage = Voir l'interface publique de r 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 diff --git a/view/control/control_card.php b/view/control/control_card.php index 2a8202d9..7576d8c7 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -313,6 +313,13 @@ } if ($viewmode == 'images') { + if (getDolGlobalInt('DIGIQUALI_SHEET_MAIN_CATEGORIES_SET') <= 0) { + print ''; + } + print '
'; print '
' . $langs->trans('SheetCategories') . '
'; print '
'; From 5b19df7de33ddea44c9b7e5cace91c60e3477eaa Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 25 Jan 2024 17:34:59 +0100 Subject: [PATCH 31/64] #1700 [Control] fix: display 0% instead of % --- view/control/control_card.php | 2 +- view/sheet/sheet_card.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/view/control/control_card.php b/view/control/control_card.php index 33ad8591..b8578788 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -750,7 +750,7 @@ print '
'; print '
'; @@ -624,7 +534,7 @@ } $questionConfirmInfo .= '

' . $langs->trans('ConfirmValidateControl') . ''; - $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ValidateControl'), $questionConfirmInfo, 'confirm_setValidated', '', 'yes', 'actionButtonValidate', 250); + $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ValidateControl'), $questionConfirmInfo, 'confirm_validate', '', 'yes', 'actionButtonValidate', 250); } // SetReopened confirmation @@ -673,7 +583,7 @@ saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); - print '
'; + print '
'; print '
'; print ''."\n"; @@ -681,23 +591,23 @@ unset($object->fields['projectid']); // Hide field already shown in banner if (getDolGlobalInt('SATURNE_ENABLE_PUBLIC_INTERFACE')) { - $publicControlInterfaceUrl = dol_buildpath('custom/digiquali/public/control/public_control.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); - print ''; - print ''; print ''; - //Survey public interface + // Answer public interface print ''; print ''; } @@ -872,7 +782,7 @@ if (is_array($mandatoryArray) && !empty($mandatoryArray) && is_array($questionIds) && !empty($questionIds)) { foreach ($questionIds as $questionId) { if (in_array($questionId, $mandatoryArray)) { - $controldettmp = $controldet; + $controldettmp = $objectLine; $resultQuestion = $question->fetch($questionId); $resultAnswer = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); if (($resultAnswer > 0 && is_array($resultAnswer)) || !empty($controldettmp)) { @@ -898,7 +808,7 @@ id.'" id="saveControl" enctype="multipart/form-data">'; + print ''; print ''; print ''; @@ -1042,8 +952,8 @@ if (!$user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER || $answerCounter != $questionCounter) { print load_fiche_titre($langs->trans('LinkedQuestionsList'), '', ''); - print '
'; - require_once __DIR__ . '/../../core/tpl/digiquali_control_answers.tpl.php'; + print '
'; + require_once __DIR__ . '/../../core/tpl/digiquali_answers.tpl.php'; print '
'; } diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index a537e0a8..b5a24792 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -31,14 +31,7 @@ } // Load Dolibarr libraries -require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php'; -require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; -require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; -require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php'; -require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmdirectory.class.php'; require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/images.lib.php'; -require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; require_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php'; // Load Saturne libraries @@ -50,15 +43,15 @@ require_once __DIR__ . '/../../class/question.class.php'; require_once __DIR__ . '/../../class/answer.class.php'; require_once __DIR__ . '/../../class/digiqualidocuments/surveydocument.class.php'; +require_once __DIR__ . '/../../lib/digiquali_sheet.lib.php'; require_once __DIR__ . '/../../lib/digiquali_survey.lib.php'; require_once __DIR__ . '/../../lib/digiquali_answer.lib.php'; -require_once __DIR__ . '/../../lib/digiquali_sheet.lib.php'; // Global variables definitions global $conf, $db, $hookmanager, $langs, $user; // Load translation files required by the page -saturne_load_langs(['other', 'bills', 'orders']); +saturne_load_langs(); // Get parameters $id = GETPOST('id', 'int'); @@ -67,135 +60,135 @@ $subaction = GETPOST('subaction', 'aZ09'); $confirm = GETPOST('confirm', 'alpha'); $cancel = GETPOST('cancel', 'aZ09'); -$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'controlcard'; // To manage different context of search +$contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'surveycard'; // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // Initialize objects // Technical objets -$object = new Survey($db); -$controldet = new SurveyLine($db); -$document = new SurveyDocument($db); -$signatory = new SaturneSignature($db, 'digiquali'); -$product = new Product($db); -$sheet = new Sheet($db); -$question = new Question($db); -$answer = new Answer($db); -$usertmp = new User($db); -$thirdparty = new Societe($db); -$contact = new Contact($db); -$extrafields = new ExtraFields($db); -$ecmfile = new EcmFiles($db); -$ecmdir = new EcmDirectory($db); -$category = new Categorie($db); +$object = new Survey($db); +$objectLine = new SurveyLine($db); +$document = new SurveyDocument($db); +$signatory = new SaturneSignature($db, 'digiquali'); +$sheet = new Sheet($db); +$question = new Question($db); +$answer = new Answer($db); +$extraFields = new ExtraFields($db); +$category = new Categorie($db); // View objects $form = new Form($db); -$hookmanager->initHooks(array('surveycard', 'globalcard')); // Note that conf->hooks_modules contains array +$hookmanager->initHooks(['surveycard', 'globalcard']); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels -$extrafields->fetch_name_optionals_label($object->table_element); +$extraFields->fetch_name_optionals_label($object->table_element); -$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); +$search_array_options = $extraFields->getOptionalsFromPost($object->table_element, '', 'search_'); // Initialize array of search criterias $searchAll = GETPOST('search_all', 'alpha'); -$search = array(); +$search = []; foreach ($object->fields as $key => $val) { - if (GETPOST('search_'.$key, 'alpha')) $search[$key] = GETPOST('search_'.$key, 'alpha'); + if (GETPOST('search_' . $key, 'alpha')) { + $search[$key] = GETPOST('search_' . $key, 'alpha'); + } } -if (empty($action) && empty($id) && empty($ref)) $action = 'view'; +if (empty($action) && empty($id) && empty($ref)) { + $action = 'view'; +} // Load object -include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once. +require_once DOL_DOCUMENT_ROOT . '/core/actions_fetchobject.inc.php'; // Must be included, not include_once -$permissiontoread = $user->rights->digiquali->survey->read; -$permissiontoadd = $user->rights->digiquali->survey->write; // Used by the include of actions_addupdatedelete.inc.php and actions_lineupdown.inc.php -$permissiontodelete = $user->rights->digiquali->survey->delete || ($permissiontoadd && isset($object->status) && $object->status == $object::STATUS_DRAFT); -$upload_dir = $conf->digiquali->multidir_output[isset($object->entity) ? $object->entity : 1]; +$upload_dir = $conf->digiquali->multidir_output[$object->entity ?? 1]; // Security check - Protection if external user -saturne_check_access($permissiontoread, $object); +$permissionToRead = $user->rights->digiquali->survey->read; +$permissiontoadd = $user->rights->digiquali->survey->write; +$permissiontodelete = $user->rights->digiquali->survey->delete || ($permissiontoadd && isset($object->status) && $object->status == Survey::STATUS_DRAFT); +saturne_check_access($permissionToRead); /* * Actions */ $parameters = ['id' => $id]; -$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks. +$resHook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks if ($resHook < 0) { setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); } if (empty($resHook)) { - $error = 0; + $error = 0; - $backurlforlist = dol_buildpath('/digiquali/view/survey/survey_list.php', 1); + $backurlforlist = dol_buildpath('/digiquali/view/survey/survey_list.php', 1); - if (empty($backtopage) || ($cancel && empty($id))) { - if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { - if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) $backtopage = $backurlforlist; - else $backtopage = dol_buildpath('/digiquali/view/survey/survey_card.php', 1).'?id='.($id > 0 ? $id : '__ID__'); - } - } + if (empty($backtopage) || ($cancel && empty($id))) { + if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) { + if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) { + $backtopage = $backurlforlist; + } else { + $backtopage = dol_buildpath('/digiquali/view/survey/survey_card.php', 1) . '?id=' . ((!empty($id) && $id > 0) ? $id : '__ID__'); + } + } + } - // Action clone object - if ($action == 'confirm_clone' && $confirm == 'yes') { + // Action clone object + if ($action == 'confirm_clone' && $confirm == 'yes') { $options['attendants'] = GETPOST('clone_attendants'); $options['photos'] = GETPOST('clone_photos'); - if ($object->id > 0) { - $result = $object->createFromClone($user, $object->id, $options); - if ($result > 0) { - header("Location: " . $_SERVER['PHP_SELF'] . '?id=' . $result); - exit(); - } else { - setEventMessages($object->error, $object->errors, 'errors'); - $action = ''; - } - } - } - - if ($action == 'add' && !$cancel) { - $linkableElements = get_sheet_linkable_objects(); - $controlledObjectSelected = 0; - - if (!empty($linkableElements)) { - foreach ($linkableElements as $linkableElementType => $linkableElement) { - if (!empty(GETPOST($linkableElement['post_name'])) && GETPOST($linkableElement['post_name']) > 0) { - $controlledObjectSelected++; - } - } - } - - if (GETPOST('fk_sheet') > 0) { - if ($controlledObjectSelected == 0) { - setEventMessages($langs->trans('NeedObjectToControl'), [], 'errors'); - header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create&fk_sheet=' . GETPOST('fk_sheet')); - exit; - } - } else { - setEventMessages($langs->trans('NeedFkSheet'), [], 'errors'); - header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create'); - exit; - } - - } - - // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen - include DOL_DOCUMENT_ROOT.'/core/actions_addupdatedelete.inc.php'; + if ($object->id > 0) { + $result = $object->createFromClone($user, $object->id, $options); + if ($result > 0) { + header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $result); + exit(); + } else { + setEventMessages($object->error, $object->errors, 'errors'); + $action = ''; + } + } + } + + if ($action == 'add' && !$cancel) { + $linkableElements = get_sheet_linkable_objects(); + $linkedObjectSelected = 0; + + if (!empty($linkableElements)) { + foreach ($linkableElements as $linkableElementType => $linkableElement) { + if (!empty(GETPOST($linkableElement['post_name'])) && GETPOST($linkableElement['post_name']) > 0) { + $linkedObjectSelected++; + } + } + } + + if (GETPOST('fk_sheet') > 0) { + if ($linkedObjectSelected == 0) { + setEventMessages($langs->trans('NeedObjectToLink'), [], 'errors'); + header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create&fk_sheet=' . GETPOST('fk_sheet')); + exit; + } + } else { + setEventMessages($langs->trans('NeedFkSheet'), [], 'errors'); + header('Location: ' . $_SERVER['PHP_SELF'] . '?action=create'); + exit; + } + } + + // Actions cancel, add, update, update_extras, confirm_validate, confirm_delete, confirm_deleteline, confirm_clone, confirm_close, confirm_setdraft, confirm_reopen + require_once DOL_DOCUMENT_ROOT . '/core/actions_addupdatedelete.inc.php'; // Actions set_thirdparty, set_project require_once __DIR__ . '/../../../saturne/core/tpl/actions/banner_actions.tpl.php'; - if ($action == 'set_categories' && $permissiontoadd) { - if ($object->fetch($id) > 0) { - $result = $object->setCategories(GETPOST('categories', 'array')); - header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $id); - exit(); - } - } + if ($action == 'set_categories' && $permissiontoadd) { + if ($object->fetch($id) > 0) { + $result = $object->setCategories(GETPOST('categories', 'array')); + header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $id); + exit(); + } + } if ($action == 'show_only_questions_with_no_answer') { $data = json_decode(file_get_contents('php://input'), true); @@ -207,127 +200,16 @@ dol_set_user_param($db, $conf, $user, $tabParam); } - require_once __DIR__ . '/../../core/tpl/digiquali_control_answers_save_action.tpl.php'; + require_once __DIR__ . '/../../core/tpl/digiquali_answers_save_action.tpl.php'; - // Actions builddoc, forcebuilddoc, remove_file. + // Actions builddoc, forcebuilddoc, remove_file require_once __DIR__ . '/../../../saturne/core/tpl/documents/documents_action.tpl.php'; // Action to generate pdf from odt file require_once __DIR__ . '/../../../saturne/core/tpl/documents/saturne_manual_pdf_generation_action.tpl.php'; - // Action to set status STATUS_VALIDATED - if ($action == 'confirm_setValidated') { - $object->fetch($id); - if ( ! $error) { - $result = $object->validate($user, false); - if ($result > 0) { - $controldet = new ControlLine($db); - $sheet->fetch($object->fk_sheet); - $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); - $questionIds = $object->linkedObjectsIds; - foreach ($questionIds['digiquali_question'] as $questionId) { - $controldettmp = $controldet; - //fetch controldet avec le fk_question et fk_control, s'il existe on l'update sinon on le crée - $result = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); - - //sauvegarder réponse - $questionAnswer = GETPOST('answer'.$questionId); - if (!empty($questionAnswer)) { - $controldettmp->answer = $questionAnswer; - } - - //sauvegarder commentaire - $comment = GETPOST('comment'.$questionId); - if (dol_strlen($comment) > 0) { - $controldettmp->comment = $comment; - } - - if ($result > 0 && is_array($result)) { - $controldettmp = array_shift($result); - - $controldettmp->update($user); - } else { - if (empty($controldettmp->ref)) { - $controldettmp->ref = $controldettmp->getNextNumRef(); - } - $controldettmp->fk_control = $object->id; - $controldettmp->fk_question = $questionId; - $controldettmp->entity = $conf->entity; - - $controldettmp->insert($user); - } - } - // Set validated OK - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation - header('Location: ' . $urltogo); - exit; - } else { - // Set validated KO - if ( ! empty($object->errors)) setEventMessages(null, $object->errors, 'errors'); - else setEventMessages($object->error, null, 'errors'); - } - } - } - - // Action to set status STATUS_REOPENED - if ($action == 'confirm_setReopened') { - $object->fetch($id); - if ( ! $error) { - $result = $object->setDraft($user, false); - if ($result > 0) { - $object->verdict = null; - $result = $object->update($user); - // Set reopened OK - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation - header('Location: ' . $urltogo); - exit; - } else { - // Set reopened KO - if ( ! empty($object->errors)) setEventMessages(null, $object->errors, 'errors'); - else setEventMessages($object->error, null, 'errors'); - } - } - } - - // Action to set status STATUS_LOCKED - if ($action == 'confirm_lock') { - $object->fetch($id); - if (!$error) { - $result = $object->setLocked($user); - if ($result > 0) { - // Set Locked OK - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation - header('Location: ' . $urltogo); - exit; - } elseif (!empty($object->errors)) { // Set Locked KO. - setEventMessages('', $object->errors, 'errors'); - } else { - setEventMessages($object->error, [], 'errors'); - } - } - } - - // Action to set status STATUS_ARCHIVED. - if ($action == 'confirm_archive' && $permissiontoadd) { - $object->fetch($id); - if (!$error) { - $result = $object->setArchived($user); - if ($result > 0) { - // Set Archived OK. - $urltogo = str_replace('__ID__', $result, $backtopage); - $urltogo = preg_replace('/--IDFORBACKTOPAGE--/', $id, $urltogo); // New method to autoselect project after a New on another form object creation. - header('Location: ' . $urltogo); - exit; - } elseif (!empty($object->errors)) { // Set Archived KO. - setEventMessages('', $object->errors, 'errors'); - } else { - setEventMessages($object->error, [], 'errors'); - } - } - } + // Actions confirm_lock, confirm_archive + require_once __DIR__ . '/../../../saturne/core/tpl/object/object_action_workflow.tpl.php'; // Actions to send emails $triggersendname = 'SURVEY_SENTBYMAIL'; @@ -340,11 +222,10 @@ * View */ -$title = $langs->trans('Survey'); -$help_url = 'FR:Module_DigiQuali'; +$title = $langs->trans(ucfirst($object->element)); +$helpUrl = 'FR:Module_DigiQuali'; -saturne_header(1,'', $title, $help_url); -$object->fetch(GETPOST('id')); +saturne_header(1,'', $title, $helpUrl); $elementArray = get_sheet_linkable_objects(); @@ -357,7 +238,7 @@ print load_fiche_titre($langs->trans('New' . ucfirst($object->element)), '', 'object_' . $object->picto); - print ''; + print ''; print ''; print ''; if ($backtopage) { @@ -382,26 +263,26 @@ print ''; // Common attributes - require_once DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_add.tpl.php'; + require_once DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_add.tpl.php'; // Categories if (isModEnabled('categorie')) { print '
'; } print '
' . $langs->trans('PublicControl') . ' '; + $publicInterfaceUrl = dol_buildpath('custom/digiquali/public/control/public_control.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); + print ''; + print '
' . $langs->trans('PublicInterface') . ' '; print ' '; print ''; print '' . saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/qrcode/', 'small', 1, 0, 0, 0, 80, 80, 0, 0, 0, 'control/'. $object->ref . '/qrcode/', $object, '', 0, 0) . '
'; - $publicSurveyUrl = dol_buildpath('custom/digiquali/public/control/public_survey.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); - print $langs->trans('PublicSurvey'); - print ' '; - print showValueWithClipboardCPButton($publicSurveyUrl, 0, ' '); + $publicAnswerUrl = dol_buildpath('custom/digiquali/public/public_answer.php?track_id=' . $object->track_id . '&object_type=' . $object->element . '&entity=' . $conf->entity, 3); + print $langs->trans('PublicAnswer'); + print ' '; + print showValueWithClipboardCPButton($publicAnswerUrl, 0, ' '); print ''; - print ''. $langs->trans('GoToPublicSurveyPage') .' '; + print '' . $langs->trans('GoToPublicAnswerPage') . ' '; print '
' . $langs->trans('Categories') . ''; $categoriesArborescence = $form->select_all_categories($object->element, '', 'parent', 64, 0, 1); - print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $categoriesArborescence, GETPOST('categories', 'array'), '', 0, 'maxwidth500 widthcentpercentminusx'); - print ''; + print img_picto('', 'category', 'class="pictofixedwidth"').$form::multiselectarray('categories', $categoriesArborescence, GETPOST('categories', 'array'), '', 0, 'maxwidth500 widthcentpercentminusx'); + print ''; print '
'; print '
'; - print ''; + print '
'; print '
'; foreach($elementArray as $linkableElementType => $linkableElement) { - if (!empty($linkableElement['conf'] && preg_match('/"'. $linkableElementType .'":1/',$sheet->element_linked))) { + if (!empty($linkableElement['conf'] && preg_match('/"' . $linkableElementType . '":1/', $sheet->element_linked))) { $objectArray = []; $objectPostName = $linkableElement['post_name']; @@ -414,8 +295,8 @@ } else { $objectFilter = []; } - $objectList = saturne_fetch_all_object_type($linkableElement['className'], '', '', 0, 0, $objectFilter); + $objectList = saturne_fetch_all_object_type($linkableElement['className'], '', '', 0, 0, $objectFilter); if (is_array($objectList) && !empty($objectList)) { foreach($objectList as $objectSingle) { $objectName = ''; @@ -436,7 +317,7 @@ print '
'; } @@ -445,7 +326,7 @@ print ''; // Other attributes - require_once DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php'; + require_once DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_add.tpl.php'; print '
' . $langs->transnoentities($linkableElement['langs']) . ''; print img_picto('', $linkableElement['picto'], 'class="pictofixedwidth"'); - print $form->selectArray($objectPostName, $objectArray, $objectPost, $langs->trans('Select') . ' ' . strtolower($langs->trans($linkableElement['langs'])), 0, 0, '', 0, 0, dol_strlen(GETPOST('fromtype')) > 0 && GETPOST('fromtype') != $linkableElement['link_name'], '', 'maxwidth500 widthcentpercentminusxx'); + print $form::selectarray($objectPostName, $objectArray, $objectPost, $langs->trans('Select') . ' ' . strtolower($langs->trans($linkableElement['langs'])), 0, 0, '', 0, 0, dol_strlen(GETPOST('fromtype')) > 0 && GETPOST('fromtype') != $linkableElement['link_name'], '', 'maxwidth500 widthcentpercentminusxx'); print ''; print '
'; @@ -463,66 +344,65 @@ saturne_get_fiche_head($object, 'card', $title); saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); + $sheet->fetch($object->fk_sheet); + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $questionIds = $sheet->linkedObjectsIds['digiquali_question']; + + $questionCounter = 0; + if (!empty($questionIds)) { + $questionCounter = count($questionIds); + } + + $answerCounter = 0; + if (is_array($object->lines) && !empty($object->lines)) { + foreach($object->lines as $objectLine) { + if (dol_strlen($objectLine->answer) > 0) { + $answerCounter++; + } + } + } + $formConfirm = ''; - // SetValidated confirmation - if (($action == 'setValidated' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { - $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); - $questionIds = $sheet->linkedObjectsIds['digiquali_question']; - - if (!empty($questionIds)) { - $questionCounter = count($questionIds); - } else { - $questionCounter = 0; - } - - $object->fetchLines(); - $answerCounter = 0; - if (is_array($object->lines) && !empty($object->lines)) { - foreach($object->lines as $objectLine) { - if (dol_strlen($objectLine->answer) > 0) { - $answerCounter++; - } - } - } - - $questionConfirmInfo = $langs->trans('YouAnswered') . ' ' . $answerCounter . ' ' . $langs->trans('question(s)') . ' ' . $langs->trans('On') . ' ' . $questionCounter . '.'; - if ($questionCounter - $answerCounter != 0) { - $questionConfirmInfo .= '
' . $langs->trans('BewareQuestionsAnswered', $questionCounter - $answerCounter) . ''; - } - - $questionConfirmInfo .= '

' . $langs->trans('ConfirmValidateControl') . ''; - $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ValidateControl'), $questionConfirmInfo, 'confirm_setValidated', '', 'yes', 'actionButtonValidate', 250); - } - - // SetReopened confirmation - if (($action == 'setReopened' && (empty($conf->use_javascript_ajax) || ! empty($conf->dol_use_jmobile))) || ( ! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { - $formconfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ReOpenObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmReOpenObject', $langs->transnoentities('The' . ucfirst($object->element)), $langs->transnoentities('The' . ucfirst($object->element))) . '
' . $langs->trans('ConfirmReOpenControl', $object->ref), 'confirm_setReopened', '', 'yes', 'actionButtonReOpen', 350, 600); + // Validate confirmation + if (($action == 'validate' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + $questionConfirmInfo = $langs->trans('YouAnswered') . ' ' . $answerCounter . ' ' . $langs->trans('question(s)') . ' ' . $langs->trans('On') . ' ' . $questionCounter . '.'; + if ($questionCounter - $answerCounter != 0) { + $questionConfirmInfo .= '
' . $langs->trans('BewareQuestionsAnswered', $questionCounter - $answerCounter) . ''; + } + + $questionConfirmInfo .= '

' . $langs->trans('ConfirmValidateSurvey') . ''; + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('ValidateObject', $langs->transnoentities('The' . ucfirst($object->element))), $questionConfirmInfo, 'confirm_validate', '', 'yes', 'actionButtonValidate', 250); + } + + // Draft confirmation + if (($action == 'draft' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id . '&object_type=' . $object->element, $langs->trans('ReOpenObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmReOpenObject', $langs->transnoentities('The' . ucfirst($object->element)), $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_setdraft', '', 'yes', 'actionButtonInProgress', 350, 600); } // Lock confirmation if (($action == 'lock' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { - $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('LockObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmLockObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_set_Lock', '', 'yes', 'actionButtonLock', 350, 600); + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('LockObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmLockObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_lock', '', 'yes', 'actionButtonLock', 350, 600); } - // Clone confirmation - if (($action == 'clone' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { - // Define confirmation messages. - $formquestionclone = [ + // Clone confirmation + if (($action == 'clone' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { + // Define confirmation messages + $formQuestionClone = [ ['type' => 'checkbox', 'name' => 'clone_attendants', 'label' => $langs->trans('CloneAttendants'), 'value' => 1], - ['type' => 'checkbox', 'name' => 'clone_photos', 'label' => $langs->trans('ClonePhotos'), 'value' => 1] + ['type' => 'checkbox', 'name' => 'clone_photos', 'label' => $langs->trans('ClonePhotos'), 'value' => 1] ]; - $formconfirm .= $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('CloneObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmCloneObject', $langs->transnoentities('The' . ucfirst($object->element)), $object->ref), 'confirm_clone', $formquestionclone, 'yes', 'actionButtonClone', 350, 600); - } + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('CloneObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmCloneObject', $langs->transnoentities('The' . ucfirst($object->element)), $object->ref), 'confirm_clone', $formQuestionClone, 'yes', 'actionButtonClone', 350, 600); + } + // Delete confirmation if ($action == 'delete') { - $formConfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('DeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmDeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_delete', '', 'yes', 1); + $formConfirm .= $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('DeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), $langs->trans('ConfirmDeleteObject', $langs->transnoentities('The' . ucfirst($object->element))), 'confirm_delete', '', 'yes', 1); } // Call Hook formConfirm - $parameters = ['formConfirm' => $formConfirm, 'lineid' => $lineid]; + $parameters = ['formConfirm' => $formConfirm]; $resHook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($resHook)) { $formConfirm .= $hookmanager->resPrint; @@ -539,7 +419,7 @@ $onPhone = 0; } - print '
'; + print '
'; print '
'; print ''; @@ -547,113 +427,103 @@ unset($object->fields['projectid']); // Hide field already shown in banner if (getDolGlobalInt('SATURNE_ENABLE_PUBLIC_INTERFACE')) { - $publicControlInterfaceUrl = dol_buildpath('custom/digiquali/public/control/public_control.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); - print ''; - print ''; - print ''; - - //Survey public interface - print ''; - print ''; - } - - require_once DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; + $publicInterfaceUrl = dol_buildpath('custom/digiquali/public/survey/public_survey.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); + print ''; + print ''; + print ''; + + // Answer public interface + print ''; + print ''; + } + + require_once DOL_DOCUMENT_ROOT . '/core/tpl/commonfields_view.tpl.php'; // Categories - if ($conf->categorie->enabled) { - print ''; - if ($action != 'categories') { - print ''; - } - if ($permissiontoadd && $action == 'categories') { - $categoryArborescence = $form->select_all_categories('control', '', 'parent', 64, 0, 1); - $categoryArborescence = empty($categoryArborescence) ? [] : $categoryArborescence; - if (is_array($categoryArborescence)) { - // Categories - print ''; - } - } - print ''; - } - - $object->fetchObjectLinked('', '', $object->id, 'digiquali_control', 'OR', 1, 'sourcetype', 0); - - foreach($elementArray as $linkableElementType => $linkableElement) { - if ($linkableElement['conf'] > 0 && (!empty($object->linkedObjectsIds[$linkableElement['link_name']]))) { - $className = $linkableElement['className']; - $linkedObject = new $className($db); - - $linkedObjectKey = array_key_first($object->linkedObjectsIds[$linkableElement['link_name']]); - $linkedObjectId = $object->linkedObjectsIds[$linkableElement['link_name']][$linkedObjectKey]; - - $result = $linkedObject->fetch($linkedObjectId); - - if ($result > 0) { - print ''; - print ''; - } - } - } - - print ''; + if ($action != 'categories') { + print ''; + } + if ($permissiontoadd && $action == 'categories') { + $categoriesArborescence = $form->select_all_categories('survey', '', 'parent', 64, 0, 1); + if (is_array($categoriesArborescence) && !empty($categoriesArborescence)) { + print ''; + } + } + print ''; + } + + $object->fetchObjectLinked('', '', $object->id, 'digiquali_survey', 'OR', 1, 'sourcetype', 0); + + foreach($elementArray as $linkableElementType => $linkableElement) { + if ($linkableElement['conf'] > 0 && (!empty($object->linkedObjectsIds[$linkableElement['link_name']]))) { + $className = $linkableElement['className']; + $linkedObject = new $className($db); + + $linkedObjectKey = array_key_first($object->linkedObjectsIds[$linkableElement['link_name']]); + $linkedObjectId = $object->linkedObjectsIds[$linkableElement['link_name']][$linkedObjectKey]; + + $result = $linkedObject->fetch($linkedObjectId); + + if ($result > 0) { + print ''; + print ''; + } + } + } + + print ''; + ?> + status < Survey::STATUS_LOCKED) ? '' : 'style="display:none"' ?>> + + + + + + ref . '/photos/', $object, 'photo', $object->status < Survey::STATUS_LOCKED, $permissiontodelete && $object->status < Survey::STATUS_LOCKED); + print ''; // Other attributes. Fields from hook formObjectOptions and Extrafields require_once DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; @@ -662,24 +532,19 @@ print ''; print ''; - $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); - - $questionIds = $sheet->linkedObjectsIds['digiquali_question']; - $cantValidateControl = 0; - $mandatoryArray = json_decode($sheet->mandatory_questions, true); - + // @TODO pas opti + $cantValidateSurvey = 0; + $mandatoryArray = json_decode($sheet->mandatory_questions, true); if (is_array($mandatoryArray) && !empty($mandatoryArray) && is_array($questionIds) && !empty($questionIds)) { foreach ($questionIds as $questionId) { if (in_array($questionId, $mandatoryArray)) { - $controldettmp = $controldet; $resultQuestion = $question->fetch($questionId); - $resultAnswer = $controldettmp->fetchFromParentWithQuestion($object->id, $questionId); - if (($resultAnswer > 0 && is_array($resultAnswer)) || !empty($controldettmp)) { - $itemControlDet = !empty($resultAnswer) ? array_shift($resultAnswer) : $controldettmp; + $resultAnswer = $objectLine->fetchFromParentWithQuestion($object->id, $questionId); + if (($resultAnswer > 0 && is_array($resultAnswer)) || !empty($objectLine)) { + $itemSurveyDet = !empty($resultAnswer) ? array_shift($resultAnswer) : $objectLine; if ($resultQuestion > 0) { - if (empty($itemControlDet->comment) && empty($itemControlDet->answer)) { - $cantValidateControl++; + if (empty($itemSurveyDet->comment) && empty($itemSurveyDet->answer)) { + $cantValidateSurvey++; } } } @@ -689,83 +554,80 @@ print '
'; + print ''; + print ''; + print ''; + + // Buttons for actions + if ($action != 'presend') { + print '
'; + $parameters = []; + $resHook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if ($resHook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } - print ''; - print ''; - print ''; - - // Buttons for actions - if ($action != 'presend' && $action != 'editline') { - print '
'; - $parameters = array(); - $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook - if ($reshook < 0) { - setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); - } - - if (empty($reshook)) { - // Save question answer - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Save'); - if ($object->status == $object::STATUS_DRAFT) { - print '' . $displayButton . ' '; - } else { - print '' . $displayButton . ''; - } - - // Validate - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Validate'); - if ($object->status == $object::STATUS_DRAFT && empty($cantValidateControl) && !$equipmentOutdated) { - print '' . $displayButton . ''; - } else if ($cantValidateControl > 0) { - print '' . $displayButton . ''; - } else if ($equipmentOutdated) { - print '' . $displayButton . ''; - } elseif ($object->status < $object::STATUS_DRAFT) { - print '' . $displayButton . ''; - } + if (empty($resHook)) { + // Save question answer + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Save'); + if ($object->status == Survey::STATUS_DRAFT) { + print '' . $displayButton . ' '; + } else { + print '' . $displayButton . ''; + } + + // Validate + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Validate'); + if ($object->status == Survey::STATUS_DRAFT && empty($cantValidateSurvey)) { + print '' . $displayButton . ''; + } elseif ($cantValidateSurvey > 0) { + print '' . $displayButton . ''; + } elseif ($object->status < Survey::STATUS_DRAFT) { + print '' . $displayButton . ''; + } // ReOpen $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('ReOpenDoli'); - if ($object->status == $object::STATUS_VALIDATED) { - print '' . $displayButton . ''; - } elseif ($object->status > $object::STATUS_VALIDATED) { - print '' . $displayButton . ''; + if ($object->status == Survey::STATUS_VALIDATED) { + print '' . $displayButton . ''; + } elseif ($object->status > Survey::STATUS_VALIDATED) { + print '' . $displayButton . ''; } // Sign $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Sign'); - if ($object->status == $object::STATUS_VALIDATED && !$signatory->checkSignatoriesSignatures($object->id, $object->element)) { + if ($object->status == Survey::STATUS_VALIDATED && !$signatory->checkSignatoriesSignatures($object->id, $object->element)) { print '' . $displayButton . ''; } else { print '' . $displayButton . ''; } - // Lock - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Lock'); - if ($object->status == $object::STATUS_VALIDATED && $object->verdict != null && $signatory->checkSignatoriesSignatures($object->id, $object->element) && !$equipmentOutdated) { - print '' . $displayButton . ''; - } else { - print '' . $displayButton . ''; - } - - // Send email - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('SendMail') . ' '; - if ($object->status == $object::STATUS_LOCKED) { - $fileparams = dol_most_recent_file($upload_dir . '/' . $object->element . 'document' . '/' . $object->ref); - $file = $fileparams['fullname']; - if (file_exists($file) && !strstr($fileparams['name'], 'specimen')) { - $forcebuilddoc = 0; + // Lock + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Lock'); + if ($object->status == Survey::STATUS_VALIDATED && $signatory->checkSignatoriesSignatures($object->id, $object->element)) { + print '' . $displayButton . ''; + } else { + print '' . $displayButton . ''; + } + + // Send email + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('SendMail') . ' '; + if ($object->status == Survey::STATUS_LOCKED) { + $fileParams = dol_most_recent_file($upload_dir . '/' . $object->element . 'document' . '/' . $object->ref); + $file = $fileParams['fullname']; + if (file_exists($file) && !strstr($fileParams['name'], 'specimen')) { + $forceBuildDoc = 0; } else { - $forcebuilddoc = 1; + $forceBuildDoc = 1; } - print dolGetButtonAction($displayButton, '', 'default', $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&action=presend&forcebuilddoc=' . $forcebuilddoc . '&mode=init#formmailbeforetitle', '', $object->status == $object::STATUS_LOCKED); - } else { - print '' . $displayButton . ''; - } + print dolGetButtonAction($displayButton, '', 'default', $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&action=presend&forcebuilddoc=' . $forceBuildDoc . '&mode=init#formmailbeforetitle', '', $object->status == Survey::STATUS_LOCKED); + } else { + print '' . $displayButton . ''; + } // Archive - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Archive'); - if ($object->status == $object::STATUS_LOCKED) { + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Archive'); + if ($object->status == Survey::STATUS_LOCKED && !empty(dol_dir_list($upload_dir . '/'. $object->element . '/document/' . dol_sanitizeFileName($object->ref)))) { print '' . $displayButton . ''; } else { print '' . $displayButton . ''; @@ -775,256 +637,79 @@ $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('ToClone'); print '' . $displayButton . ''; - // Delete (need delete permission, or if draft, just need create/modify permission) - $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Delete'); - print dolGetButtonAction($displayButton, '', 'delete', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=delete&token='.newToken(), '', $permissiontodelete || ($object->status == $object::STATUS_DRAFT && $permissiontoadd)); - } - print '
'; - } - - // QUESTION LINES - print '
'; - - $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); - $questionIds = $sheet->linkedObjectsIds['digiquali_question']; - - if (is_array($questionIds) && !empty($questionIds)) { - ksort($questionIds); - } - if (!empty($questionIds)) { - $questionCounter = count($questionIds); - } else { - $questionCounter = 0; - } - - $object->fetchLines(); - $answerCounter = 0; - if (is_array($object->lines) && !empty($object->lines)) { - foreach($object->lines as $objectLine) { - if (dol_strlen($objectLine->answer) > 0) { - $answerCounter++; - } - } - } ?> + // Delete (need delete permission, or if draft, just need create/modify permission) + $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Delete'); + print dolGetButtonAction($displayButton, '', 'delete', $_SERVER['PHP_SELF'] . '?id=' . $object->id . '&action=delete&token=' . newToken(), '', $permissiontodelete || ($object->status == Survey::STATUS_DRAFT && $permissiontoadd)); + } + print '
'; + } + + // QUESTION LINES + print '
'; + + if (is_array($questionIds) && !empty($questionIds)) { + ksort($questionIds); + } ?>
- - conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? img_picto($langs->trans('Enabled'), 'switch_on', 'class="show-only-questions-with-no-answer marginrightonly"') : img_picto($langs->trans('Disabled'), 'switch_off', 'class="show-only-questions-with-no-answer marginrightonly"'); - print $form->textwithpicto($user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? '' : '', $langs->trans('ShowOnlyQuestionsWithNoAnswer')); - print '
'; - - if (!$user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER || $answerCounter != $questionCounter) { + conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? img_picto($langs->trans('Enabled'), 'switch_on', 'class="show-only-questions-with-no-answer marginrightonly"') : img_picto($langs->trans('Disabled'), 'switch_off', 'class="show-only-questions-with-no-answer marginrightonly"'); + print $form->textwithpicto($user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER ? '' : '', $langs->trans('ShowOnlyQuestionsWithNoAnswer')); + } else { + $user->conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER = 0; + } ?> +
+ + conf->DIGIQUALI_SHOW_ONLY_QUESTIONS_WITH_NO_ANSWER || $answerCounter != $questionCounter) { print load_fiche_titre($langs->trans('LinkedQuestionsList'), '', ''); - print '
'; - require_once __DIR__ . '/../../core/tpl/digiquali_control_answers.tpl.php'; + print '
'; + require_once __DIR__ . '/../../core/tpl/digiquali_answers.tpl.php'; print '
'; } print '
'; - print ''; - print dol_get_fiche_end(); - - $includedocgeneration = 1; - if ($includedocgeneration) { - print '
'; - - $objref = dol_sanitizeFileName($object->ref); - $dirFiles = $object->element . 'document/' . $objref; - $filedir = $upload_dir . '/' . $dirFiles; - $urlsource = $_SERVER['PHP_SELF'] . '?id=' . $id; - - $defaultmodel = 'controldocument_odt'; - $title = $langs->trans('WorkUnitDocument'); - - print saturne_show_documents('digiquali:ControlDocument', $dirFiles, $filedir, $urlsource, 1,1, '', 1, 0, 0, 0, 0, '', 0, '', empty($soc->default_lang) ? '' : $soc->default_lang, $object, 0, 'remove_file', (($object->status > $object::STATUS_DRAFT) ? 1 : 0), $langs->trans('ControlMustBeValidatedToGenerated')); - print '
'; - - print '
'; - - $maxEvent = 10; - - $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', dol_buildpath('/saturne/view/saturne_agenda.php', 1) . '?id=' . $object->id . '&module_name=DigiQuali&object_type=' . $object->element); - - // List of actions on element - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; - $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, $object->element.'@'.$object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $maxEvent, '', $morehtmlcenter); - - print '
'; - } - - //Select mail models is same action as presend - if (GETPOST('modelselected')) { - $action = 'presend'; - } - - if ($action == 'presend') { - $langs->load('mails'); - - $ref = dol_sanitizeFileName($object->ref); - $filelist = dol_dir_list($upload_dir . '/' . $object->element . 'document' . '/' . $ref, 'files', 0, '', '', 'date', SORT_DESC); - if (!empty($filelist) && is_array($filelist)) { - $filetype = ['controldocument' => 0]; - foreach ($filelist as $file) { - if (!strstr($file['name'], 'specimen')) { - if (strstr($file['name'], str_replace(' ', '_', $langs->transnoentities('controldocument'))) && $filetype['controldocument'] == 0) { - $files[] = $file['fullname']; - $filetype['controldocument'] = 1; - } - } - } - } - - // Define output language - $outputlangs = $langs; - $newlang = ''; - if (!empty($conf->global->MAIN_MULTILANGS) && empty($newlang)) { - $newlang = $object->thirdparty->default_lang; - if (GETPOST('lang_id', 'aZ09')) { - $newlang = GETPOST('lang_id', 'aZ09'); - } - } - - if (!empty($newlang)) { - $outputlangs = new Translate('', $conf); - $outputlangs->setDefaultLang($newlang); - } - - print '
'; - print '
'; - print '
'; - print load_fiche_titre($langs->trans('SendMail'), '', $object->picto); - - print dol_get_fiche_head(); - - // Create form for email. - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; - $formmail = new FormMail($db); + print ''; + print dol_get_fiche_end(); - $formmail->param['langsmodels'] = (empty($newlang) ? $langs->defaultlang : $newlang); - $formmail->fromtype = (GETPOST('fromtype') ?GETPOST('fromtype') : (!empty($conf->global->MAIN_MAIL_DEFAULT_FROMTYPE) ? $conf->global->MAIN_MAIL_DEFAULT_FROMTYPE : 'user')); + if ($action != 'presend') { + print '
'; + // Documents + $objRef = dol_sanitizeFileName($object->ref); + $dirFiles = $object->element . 'document/' . $objRef; + $fileDir = $upload_dir . '/' . $dirFiles; + $urlSource = $_SERVER['PHP_SELF'] . '?id=' . $object->id; - if ($formmail->fromtype === 'user') { - $formmail->fromid = $user->id; - } + print saturne_show_documents('digiquali:' . ucfirst($object->element) . 'Document', $dirFiles, $fileDir, $urlSource, $permissiontoadd, $permissiontodelete, $conf->global->DIGIQUALI_SURVEYDOCUMENT_DEFAULT_MODEL, 1, 0, 0, 0, 0, '', 0, '', $langs->defaultlang, $object, 0, 'remove_file', (($object->status > Survey::STATUS_DRAFT) ? 1 : 0), $langs->trans('ObjectMustBeValidatedToGenerate', ucfirst($langs->transnoentities('The' . ucfirst($object->element))))); - $formmail->withfrom = 1; + print '
'; - // Define $liste, a list of recipients with email inside <>. - $liste = []; - if (!empty($object->socid) && $object->socid > 0 && !is_object($object->thirdparty) && method_exists($object, 'fetch_thirdparty')) { - $object->fetch_thirdparty(); - } - if (is_object($object->thirdparty)) { - foreach ($object->thirdparty->thirdparty_and_contact_email_array(1) as $key => $value) { - $liste[$key] = $value; - } - } + $moreHtmlCenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-bars imgforviewmode', dol_buildpath('/saturne/view/saturne_agenda.php', 1) . '?id=' . $object->id . '&module_name=DigiQuali&object_type=' . $object->element); - if (!empty($conf->global->MAIN_MAIL_ENABLED_USER_DEST_SELECT)) { - $listeuser = []; - $fuserdest = new User($db); + // List of actions on element + require_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php'; + $formActions = new FormActions($db); + $formActions->showactions($object, $object->element . '@' . $object->module, 0, 1, '', 10, '', $moreHtmlCenter); - $result = $fuserdest->fetchAll('ASC', 't.lastname', 0, 0, ['customsql' => "t.statut = 1 AND t.employee = 1 AND t.email IS NOT NULL AND t.email <> ''"], 'AND', true); - if ($result > 0 && is_array($fuserdest->users) && count($fuserdest->users) > 0) { - foreach ($fuserdest->users as $uuserdest) { - $listeuser[$uuserdest->id] = $uuserdest->user_get_property($uuserdest->id, 'email'); - } - } elseif ($result < 0) { - setEventMessages(null, $fuserdest->errors, 'errors'); - } - if (count($listeuser) > 0) { - $formmail->withtouser = $listeuser; - $formmail->withtoccuser = $listeuser; - } - } + print '
'; + } - //$arrayoffamiliestoexclude=array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...); - if (!isset($arrayoffamiliestoexclude)) { - $arrayoffamiliestoexclude = null; - } + //Select mail models is same action as presend + if (GETPOST('modelselected')) { + $action = 'presend'; + } - // Make substitution in email content. - if ($object) { - // First we set ->substit (useless, it will be erased later) and ->substit_lines. - $formmail->setSubstitFromObject($object, $langs); - } - $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, $arrayoffamiliestoexclude, $object); - $substitutionarray['__TYPE__'] = $langs->trans(ucfirst($object->element)); - $substitutionarray['__THETYPE__'] = $langs->trans('The' . ucfirst($object->element)); - - $parameters = ['mode' => 'formemail']; - complete_substitutions_array($substitutionarray, $outputlangs, $object, $parameters); - - // Find all external contact addresses - $tmpobject = $object; - $contactarr = []; - $contactarr = $tmpobject->liste_contact(-1); - - if (is_array($contactarr) && count($contactarr) > 0) { - require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php'; - require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; - $contactstatic = new Contact($db); - $tmpcompany = new Societe($db); - - foreach ($contactarr as $contact) { - $contactstatic->fetch($contact['id']); - // Complete substitution array - $substitutionarray['__CONTACT_NAME_' . $contact['code'] . '__'] = $contactstatic->getFullName($outputlangs, 1); - $substitutionarray['__CONTACT_LASTNAME_' . $contact['code'] . '__'] = $contactstatic->lastname; - $substitutionarray['__CONTACT_FIRSTNAME_' . $contact['code'] . '__'] = $contactstatic->firstname; - $substitutionarray['__CONTACT_TITLE_' . $contact['code'] . '__'] = $contactstatic->getCivilityLabel(); - - // Complete $liste with the $contact - if (empty($liste[$contact['id']])) { // If this contact id not already into the $liste. - $contacttoshow = ''; - if (isset($object->thirdparty) && is_object($object->thirdparty)) { - if ($contactstatic->fk_soc != $object->thirdparty->id) { - $tmpcompany->fetch($contactstatic->fk_soc); - if ($tmpcompany->id > 0) { - $contacttoshow .= $tmpcompany->name . ': '; - } - } - } - $contacttoshow .= $contactstatic->getFullName($outputlangs, 1); - $contacttoshow .= ' <' . ($contactstatic->email ?: $langs->transnoentitiesnoconv('NoEMail')) . '>'; - $liste[$contact['id']] = $contacttoshow; - } - } - } + //@todo a check + // Presend form + $modelmail = $object->element; + $defaulttopic = 'InformationMessage'; + $diroutput = $conf->digiquali->dir_output; + $trackid = $object->element . $object->id; - $formmail->withto = $liste; - $formmail->withtofree = (GETPOSTISSET('sendto') ? (GETPOST('sendto', 'alphawithlgt') ? GETPOST('sendto', 'alphawithlgt') : '1') : '1'); - $formmail->withtocc = $liste; - $formmail->withtoccc = getDolGlobalString('MAIN_EMAIL_USECCC'); - $formmail->withtopic = $outputlangs->trans('SendMailSubject', '__REF__'); - $formmail->withfile = 2; - $formmail->withbody = 1; - $formmail->withdeliveryreceipt = 1; - $formmail->withcancel = 1; - - // Array of substitutions. - $formmail->substit = $substitutionarray; - - // Array of other parameters. - $formmail->param['action'] = 'send'; - $formmail->param['models'] = 'saturne'; - $formmail->param['models_id'] = GETPOST('modelmailselected', 'int'); - $formmail->param['id'] = $object->id; - $formmail->param['returnurl'] = $_SERVER['PHP_SELF'] . '?id=' . $object->id; - $formmail->param['fileinit'] = $files; - $formmail->trackid = 'control' . $object->id; - - // Show form. - print $formmail->get_form(); - - print dol_get_fiche_end(); - } + require_once DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php'; } // End of page diff --git a/view/survey/survey_list.php b/view/survey/survey_list.php index a4e03711..207671c2 100644 --- a/view/survey/survey_list.php +++ b/view/survey/survey_list.php @@ -36,23 +36,25 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php'; -require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcategory.class.php'; -require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - -require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/contact.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/usergroups.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/invoice.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/order.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/contract.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/stock.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/sendings.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/propal.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/supplier_proposal.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php'; +if (isModEnabled('categorie')) { + require_once DOL_DOCUMENT_ROOT . '/core/class/html.formcategory.class.php'; + require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php'; +} + +require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/contact.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/usergroups.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/invoice.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/order.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/contract.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/stock.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/sendings.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/propal.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/supplier_proposal.lib.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/fourn.lib.php'; // Load Saturne libraries require_once __DIR__ . '/../../../saturne/class/saturnesignature.class.php'; @@ -61,9 +63,7 @@ require_once __DIR__ . '/../../lib/digiquali_sheet.lib.php'; require_once __DIR__ . '/../../class/survey.class.php'; -require_once __DIR__ . '/../../core/boxes/digiqualiwidget1.php'; require_once __DIR__ . '/../../class/sheet.class.php'; -require_once __DIR__ . '/../../class/control.class.php'; // Global variables definitions global $conf, $db, $hookmanager, $langs, $user; @@ -71,37 +71,39 @@ // Load translation files required by the page saturne_load_langs(['other', 'bills', 'projects', 'orders', 'companies', 'product', 'productbatch', 'task', 'contracts']); +// Get list parameters $action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ... $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) -$show_files = GETPOST('show_files', 'int'); // Show files area generated by bulk actions ? -$confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation -$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button -$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list -$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'controllist'; // To manage different context of search +$show_files = GETPOST('show_files', 'int'); // Show files area generated by bulk actions ? +$confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation +$cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button +$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'surveylist'; // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page -$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') -$fromtype = GETPOST('fromtype', 'alpha'); // element type -$fromid = GETPOST('fromid', 'int'); //element id +$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') +$fromtype = GETPOST('fromtype', 'alpha'); // Element type +$fromid = GETPOST('fromid', 'int'); // Element id -// Load variable for pagination +// Get pagination parameters $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; $sortfield = GETPOST('sortfield', 'aZ09comma'); $sortorder = GETPOST('sortorder', 'aZ09comma'); -$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int'); -if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { $page = 0; } // If $page is not defined, or '' or -1 or if we click on clear filters +$page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST('page', 'int'); + +if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // If $page is not defined, or '' or -1 or if we click on clear filters. + $page = 0; +} + $offset = $limit * $page; $pageprev = $page - 1; $pagenext = $page + 1; // Initialize objects // Technical objets -$object = new Control($db); +$object = new Survey($db); $signatory = new SaturneSignature($db, 'digiquali', $object->element); -$box = new digiqualiwidget1($db); -$categorystatic = new Categorie($db); $sheet = new Sheet($db); $extrafields = new ExtraFields($db); -$controlstatic = new Control($db); $userTmp = new User($db); if (isModEnabled('societe')) { $thirdparty = new Societe($db); @@ -111,14 +113,13 @@ // View objects $form = new Form($db); -$hookmanager->initHooks(array('controllist')); // Note that conf->hooks_modules contains array +$hookmanager->initHooks(['surveylist']); // Note that conf->hooks_modules contains array // Fetch optionals attributes and labels $extrafields->fetch_name_optionals_label($object->table_element); -//$extrafields->fetch_name_optionals_label($object->table_element_line); -if (!empty($conf->categorie->enabled)) { - $search_category_array = GETPOST("search_category_control_list", "array"); +if (isModEnabled('category')) { + $search_category_array = GETPOST('search_category_survey_list', 'array'); } $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_'); @@ -162,46 +163,48 @@ } // Initialize array of search criterias -$searchAll = GETPOST('search_all', 'alphanohtml') ? GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml'); -$search = array(); +$search_all = GETPOST('search_all') ? GETPOST('search_all') : GETPOST('sall'); +$search = []; foreach ($object->fields as $key => $val) { - if (GETPOST('search_'.$key, 'alpha') !== '') $search[$key] = GETPOST('search_'.$key, 'alpha'); + if (GETPOST('search_' . $key, 'alpha') !== '') { + $search[$key] = GETPOST('search_' . $key, 'alpha'); + } + if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { + $search[$key . '_dtstart'] = dol_mktime(0, 0, 0, GETPOST('search_' . $key . '_dtstartmonth', 'int'), GETPOST('search_' . $key . '_dtstartday', 'int'), GETPOST('search_' . $key . '_dtstartyear', 'int')); + $search[$key . '_dtend'] = dol_mktime(23, 59, 59, GETPOST('search_' . $key . '_dtendmonth', 'int'), GETPOST('search_' . $key . '_dtendday', 'int'), GETPOST('search_' . $key . '_dtendyear', 'int')); + } } if(!empty($fromtype)) { - $search_key = array_search($fromtype, $elementElementFields); - $search[$search_key] = $fromid; - switch ($fromtype) { - case 'fk_sheet': - $search['fk_sheet'] = $fromid; - break; - case 'user': - $search['fk_user_controller'] = $fromid; - break; - } + $search_key = array_search($fromtype, $elementElementFields); + $search[$search_key] = $fromid; + if ($fromtype == 'fk_sheet') { + $search['fk_sheet'] = $fromid; + } } // List of fields to search into when doing a "search in all" -$fieldstosearchall = array(); +$fieldstosearchall = []; foreach ($object->fields as $key => $val) { - if ($val['searchall']) $fieldstosearchall['t.'.$key] = $val['label']; + if (!empty($val['searchall'])) { + $fieldstosearchall['t.' . $key] = $val['label']; + } } // Definition of array of fields for columns -$arrayfields = array(); +$arrayfields = []; foreach ($object->fields as $key => $val) { - // If $val['visible']==0, then we never show the field - if (!empty($val['visible'])) { - $visible = (int) dol_eval($val['visible'], 1); - $arrayfields['t.'.$key] = array( - 'label'=>$val['label'], - 'checked'=>(($visible < 0) ? 0 : 1), - 'enabled'=>($visible != 3 && dol_eval($val['enabled'], 1)), - 'position'=>$val['position'], - 'help'=>$val['help'], - 'css' => $val['css'] - ); - } + // If $val['visible']==0, then we never show the field + if (!empty($val['visible'])) { + $visible = (int)dol_eval($val['visible'], 1); + $arrayfields['t.' . $key] = [ + 'label' => $val['label'], + 'checked' => (($visible < 0) ? 0 : 1), + 'enabled' => ($visible != 3 && dol_eval($val['enabled'], 1)), + 'position' => $val['position'], + 'help' => $val['help'] ?? '' + ]; + } } // Extra fields @@ -210,99 +213,58 @@ $object->fields = dol_sort_array($object->fields, 'position'); $arrayfields = dol_sort_array($arrayfields, 'position'); -$permissiontoread = $user->rights->digiquali->control->read; -$permissiontoadd = $user->rights->digiquali->control->write; -$permissiontodelete = $user->rights->digiquali->control->delete; - -// Security check -saturne_check_access($permissiontoread, $object); +// Security check (enable the most restrictive one) - Protection if external user +$permissiontoread = $user->rights->digiquali->survey->read; +$permissiontoadd = $user->rights->digiquali->survey->write; +$permissiontodelete = $user->rights->digiquali->survey->delete; +saturne_check_access($permissiontoread); /* * Actions */ -if (GETPOST('cancel', 'alpha')) { $action = 'list'; $massaction = ''; } -if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction = ''; } -$parameters = array(); -$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks -if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +$parameters = []; +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} if (empty($reshook)) { - // Selection of new fields - include DOL_DOCUMENT_ROOT . '/core/actions_changeselectedfields.inc.php'; - - // Purge search criteria - if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers - foreach ($object->fields as $key => $val) { - $search[$key] = ''; - $_POST[$key] = ''; - } - $toselect = ''; - $search_array_options = array(); - $search_category_array = array(); - } - if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') - || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) - { - $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation - } - - // Mass actions - $objectclass = 'Control'; - $objectlabel = 'Control'; - $uploaddir = $conf->digiquali->dir_output; - - if (!$error && ($massaction == 'delete' || ($action == 'delete' && $confirm == 'yes')) && $permissiontodelete) { - $db->begin(); - - $objecttmp = new $objectclass($db); - $nbok = 0; - $TMsg = array(); - foreach ($toselect as $toselectid) { - $result = $objecttmp->fetch($toselectid); - if ($result > 0) { - $result = $objecttmp->delete($user); - - if (empty($result)) { // if delete returns 0, there is at least one object linked - $TMsg = array_merge($objecttmp->errors, $TMsg); - } elseif ($result < 0) { // if delete returns is < 0, there is an error, we break and rollback later - setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); - $error++; - break; - } else { - $nbok++; - } - } else { - setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); - $error++; - break; - } - } - - if (empty($error)) { - // Message for elements well deleted - if ($nbok > 1) { - setEventMessages($langs->trans("RecordsDeleted", $nbok), null, 'mesgs'); - } elseif ($nbok > 0) { - setEventMessages($langs->trans("RecordDeleted", $nbok), null, 'mesgs'); - } - - // Message for elements which can't be deleted - if (!empty($TMsg)) { - sort($TMsg); - setEventMessages('', array_unique($TMsg), 'warnings'); - } - - $db->commit(); - } else { - $db->rollback(); - } - - //var_dump($listofobjectthirdparties);exit; - } - -// include DOL_DOCUMENT_ROOT . '/core/actions_massactions.inc.php'; + if (GETPOST('cancel', 'alpha')) { + $action = 'list'; + $massaction = ''; + } + if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { + $massaction = ''; + } + + // Selection of new fields + include DOL_DOCUMENT_ROOT . '/core/actions_changeselectedfields.inc.php'; + + // Purge search criteria. + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers + foreach ($object->fields as $key => $val) { + $search[$key] = ''; + if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { + $search[$key.'_dtstart'] = ''; + $search[$key.'_dtend'] = ''; + } + } + $toselect = []; + $search_array_options = []; + $search_category_array = []; + } + if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') + || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) { + $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation + } + + // Mass actions delete + $objectclass = 'Survey'; + $objectlabel = 'Survey'; + $uploaddir = $conf->digiquali->dir_output; + require_once DOL_DOCUMENT_ROOT . '/core/actions_massactions.inc.php'; // Mass actions archive require_once __DIR__ . '/../../../saturne/core/tpl/actions/list_massactions.tpl.php'; @@ -312,56 +274,19 @@ * View */ -$now = dol_now(); -$help_url = ''; -$title = $langs->trans("ControlList"); +$title = $langs->trans('SurveyList'); +$helpUrl = ''; -saturne_header(0,'', $title, $help_url); +saturne_header(0,'', $title, $helpUrl); if (!empty($fromtype)) { - print saturne_get_fiche_head($objectLinked, 'control', $langs->trans("Control")); + print saturne_get_fiche_head($objectLinked, 'survey', $langs->trans('Survey')); - $linkback = ''.$langs->trans("BackToList").''; + $linkBack = '' . $langs->trans('BackToList') . ''; - saturne_banner_tab($objectLinked, 'fromtype=' . $fromtype . '&fromid', '', 1, 'rowid', ($fromtype == 'productbatch' ? 'batch' : 'ref')); + saturne_banner_tab($objectLinked, 'fromtype=' . $fromtype . '&fromid', '', 1, 'rowid', ($fromtype == 'productbatch' ? 'batch' : 'ref')); } -if ($fromid) { - print '
'; - print '
'; - print '
'; - $controls = $controlstatic->fetchAll(); - - if (is_array($controls) && !empty($controls)) { - foreach ($controls as $control) { - $control->fetchObjectLinked('','', $control->id, 'digiquali_' . $control->element, 'OR', 1, 'sourcetype', 0); - if (!empty($control->linkedObjectsIds)) { - if (array_key_exists($fromtype, $control->linkedObjectsIds)) { - $linkedObjectsIds = array_values($control->linkedObjectsIds[$fromtype]); - if (in_array($fromid, $linkedObjectsIds)) { - $categories = $categorystatic->getListForItem($control->id, $control->element); - if (is_array($categories) && !empty($categories)) { - foreach ($categories as $category) { - $nbBox[$category['label']] = 1; - } - } - } - } - } - } - - if (is_array($nbBox) || is_object($nbBox)) { - $box->loadBox(); - for ($i = 0; $i < count($nbBox); $i++) { - $box->showBox($i,$i); - } - } - } - print '
'; -} - -$newcardbutton = dolGetButtonTitle($langs->trans('NewControl'), '', 'fa fa-plus-circle', dol_buildpath('/digiquali/view/control/control_card.php', 1).'?action=create', '', $permissiontoadd); - -include_once '../../core/tpl/digiquali_control_list.tpl.php'; +require_once __DIR__ . '/../../core/tpl/digiquali_survey_list.tpl.php'; // End of page llxFooter(); From 1883af249303fbdf037a3cc645778082c3e5efd7 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Thu, 11 Jan 2024 10:23:41 +0100 Subject: [PATCH 19/64] #1662 [Card] fix: change tpl actions --- view/control/control_card.php | 2 +- view/survey/survey_card.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/view/control/control_card.php b/view/control/control_card.php index 60df5940..2a8202d9 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -261,7 +261,7 @@ } // Actions confirm_lock, confirm_archive - require_once __DIR__ . '/../../../saturne/core/tpl/object/object_action_workflow.tpl.php'; + require_once __DIR__ . '/../../../saturne/core/tpl/actions/object_workflow_actions.tpl.php'; // Actions to send emails $triggersendname = 'CONTROL_SENTBYMAIL'; diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index b5a24792..d82d8e21 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -209,7 +209,7 @@ require_once __DIR__ . '/../../../saturne/core/tpl/documents/saturne_manual_pdf_generation_action.tpl.php'; // Actions confirm_lock, confirm_archive - require_once __DIR__ . '/../../../saturne/core/tpl/object/object_action_workflow.tpl.php'; + require_once __DIR__ . '/../../../saturne/core/tpl/actions/object_workflow_actions.tpl.php'; // Actions to send emails $triggersendname = 'SURVEY_SENTBYMAIL'; From 303dec5eee18d13ec177584bf8ee0f41d22e2c41 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 12 Jan 2024 13:11:09 +0100 Subject: [PATCH 20/64] #1662 [Public] fix: missing name hook --- .../interface_99_modDigiQuali_DigiQualiTriggers.class.php | 2 +- public/public_answer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php index 411bfea7..f7d2dea5 100644 --- a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php +++ b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php @@ -332,7 +332,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf break; case 'OBJECT_SAVEANSWER' : - $actioncomm->code = 'AC_' . strtoupper($object->element) . 'SAVEANSWER'; + $actioncomm->code = 'AC_' . strtoupper($object->element) . '_SAVEANSWER'; $actioncomm->label = $langs->transnoentities('AnswerSaveTrigger'); $actioncomm->create($user); break; diff --git a/public/public_answer.php b/public/public_answer.php index 7615ba02..82b74363 100644 --- a/public/public_answer.php +++ b/public/public_answer.php @@ -84,7 +84,7 @@ $question = new Question($db); $answer = new Answer($db); -$hookmanager->initHooks(['publicsurvey']); // Note that conf->hooks_modules contains array +$hookmanager->initHooks(['publicanswer']); // Note that conf->hooks_modules contains array if (!isModEnabled('multicompany')) { $entity = $conf->entity; From 17f4a8134fd1747bd4b31bb37aafe6e854a429ef Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 12 Jan 2024 14:10:04 +0100 Subject: [PATCH 21/64] #1662 [TPL] fix: rework save action for remove unnecessary fetch --- .../tpl/digiquali_answers_save_action.tpl.php | 114 +++++++++--------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/core/tpl/digiquali_answers_save_action.tpl.php b/core/tpl/digiquali_answers_save_action.tpl.php index aa92083b..1c4d3860 100644 --- a/core/tpl/digiquali_answers_save_action.tpl.php +++ b/core/tpl/digiquali_answers_save_action.tpl.php @@ -23,7 +23,7 @@ /** * The following vars must be defined: - * Global : $conf, $db, $langs, $user + * Global : $conf, $langs, $user * Parameters : $action * Objects : $object, $objectLine, $sheet */ @@ -32,68 +32,70 @@ $data = json_decode(file_get_contents('php://input'), true); $sheet->fetch($object->fk_sheet); - $object->fetchObjectLinked($sheet->id, 'digiquali_sheet', '', '', 'OR', 1, 'sourcetype', 0); - $questionIds = $object->linkedObjectsIds['digiquali_question']; - foreach ($questionIds as $questionId) { - $objectLineTmp = $objectLine->fetchFromParentWithQuestion($object->id, $questionId); - if (is_array($objectLineTmp) && !empty($objectLineTmp)) { - $objectLineTmp = array_shift($objectLineTmp); + $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + if (!empty($sheet->linkedObjects['digiquali_question'])) { + foreach ($sheet->linkedObjects['digiquali_question'] as $question) { + if (!empty($object->lines)) { + foreach ($object->lines as $line) { + if ($line->fk_question === $question->id) { + // Save answer value + if ($data['autoSave'] && $question->id == $data['questionId']) { + $questionAnswer = $data['answer']; + } else { + $questionAnswer = GETPOST('answer' . $question->id); + } + if (!empty($questionAnswer)) { + $line->answer = $questionAnswer; + } - // Save answer value - if ($data['autoSave'] && $questionId == $data['questionId']) { - $questionAnswer = $data['answer']; - } else { - $questionAnswer = GETPOST('answer' . $questionId); - } - if (!empty($questionAnswer)) { - $objectLineTmp->answer = $questionAnswer; - } + // Save answer comment + if ($data['autoSave'] && $question->id == $data['questionId']) { + $comment = $data['comment']; + } else { + $comment = GETPOST('comment' . $question->id); + } + if (dol_strlen($comment) > 0) { + $line->comment = $comment; + } - // Save answer comment - if ($data['autoSave'] && $questionId == $data['questionId']) { - $comment = $data['comment']; - } else { - $comment = GETPOST('comment' . $questionId); - } - if (dol_strlen($comment) > 0) { - $objectLineTmp->comment = $comment; + $line->update($user); + } + } } else { - $objectLine->comment = ''; - } + $objectLine->ref = $objectLine->getNextNumRef(); + $fk_element = 'fk_'. $object->element; + $objectLine->$fk_element = $object->id; + $objectLine->fk_question = $question->id; - $objectLineTmp->update($user); - } else { - $objectLine->ref = $objectLine->getNextNumRef(); - $fk_element = 'fk_'. $object->element; - $objectLine->$fk_element = $object->id; - $objectLine->fk_question = $questionId; + // Save answer value + if ($data['autoSave'] && $question->id == $data['questionId']) { + $questionAnswer = $data['answer']; + } else { + $questionAnswer = GETPOST('answer' . $question->id); + } + if (!empty($questionAnswer)) { + $objectLine->answer = $questionAnswer; + } else { + $objectLine->answer = ''; + } - // Save answer value - if ($data['autoSave'] && $questionId == $data['questionId']) { - $questionAnswer = $data['answer']; - } else { - $questionAnswer = GETPOST('answer' . $questionId); - } - if (!empty($questionAnswer)) { - $objectLine->answer = $questionAnswer; - } + // Save answer comment + if ($data['autoSave'] && $question->id == $data['questionId']) { + $comment = $data['comment']; + } else { + $comment = GETPOST('comment' . $question->id); + } + if (dol_strlen($comment) > 0) { + $objectLine->comment = $comment; + } else { + $objectLine->comment = ''; + } - // Save answer comment - if ($data['autoSave'] && $questionId == $data['questionId']) { - $comment = $data['comment']; - } else { - $comment = GETPOST('comment' . $questionId); - } - if (dol_strlen($comment) > 0) { - $objectLine->comment = $comment; - } else { - $objectLine->comment = ''; - } - - $objectLine->entity = $conf->entity; - $objectLine->status = 1; + $objectLine->entity = $conf->entity; + $objectLine->status = 1; - $objectLine->create($user); + $objectLine->create($user); + } } } From a60a72439842ffe3a13ac56a43e3dc1c8afde24d Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 12 Jan 2024 14:10:27 +0100 Subject: [PATCH 22/64] #1662 [Control] fix: rework controlLine --- class/control.class.php | 557 +++++++++++++--------------------------- 1 file changed, 173 insertions(+), 384 deletions(-) diff --git a/class/control.class.php b/class/control.class.php index 9d0cb757..0101da8d 100644 --- a/class/control.class.php +++ b/class/control.class.php @@ -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. * @@ -1093,398 +1081,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 From d3a7678313a5a2d8255356adc9d266e9556bf623 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 13 Jan 2024 03:14:39 +0100 Subject: [PATCH 23/64] #1660 [ODT] fix: missing remove text --- .../template_surveydocument.odt | Bin 28293 -> 28358 bytes .../template_surveydocument_photo.odt | Bin 28146 -> 28642 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/documents/doctemplates/surveydocument/template_surveydocument.odt b/documents/doctemplates/surveydocument/template_surveydocument.odt index 2e21fa4995c562cc77c904d0de099da76809b30d..5caac53d61b633d5d5626a692fe7e4977d5257ce 100644 GIT binary patch delta 24893 zcmZ6xV{j(m5;YjxoY>aHwl%SBClfnwY}>YN+kRtAY}v>Z)@( zWE*sJ8x&Dd790Wt1Ox^IWB`>R0Z|70Kag|_N|Jt{WqO3F7m%B zL{Ogpp+6|+|4;^$^?%s;fAAKR{eS*{f19^~NRkIb`LEw(2*v~YzeEl=K7fBS_JGUn z<_%6X3bs+mRQA4SQ7vi&B8P3B|176=dli&nfnzC=`c#?m#n}H-wAZ-LOZ$o{t>)%pKqnJ-7h=hgIZ2XNrJ#1!hbDQL4ME<=f|HM_&uj z{!!-c(nTTA3tMg=#x;9I4-gFH!V^B?>WcUqYhObe<3K$r<%V7aW5L-05JktYqUFTA zM!b>m!9ql?VVIadcQ~8xjMK7osJL397Uo_LZZW40aeMa6-IAlMu06GsTiPCW&R!Su zQD2KAiRepx4I47@Nq5q9@6lJ_QZ0b;i%7$;8gBpt1d2{eO;KL20dlByXO|0x_Qu~) zoIcZ$Yo`A`bqYr!iC(bi&c}K^S^e&QXPdt;NA4OB^;^RkKDkli@wmm;s>`ADjJrKa z!k}WCkrgPK`Ag$y+Edpa&cQ`6O`lwpnlG$@+%xGVB^|4RxP2r3qI9Zp%(aCPKv_4M z>_?`pz@<#Xa@w-E25`}MPi0^*{>Ax9wl3%=_gCf@Oi1^Jd`(Cx;fwMf4|-_Pt*93i z>>{ic!!kbFKmki=v3^EJe8+s}So{WGx_PmIhQ>LA)>Jpe95PWk+!3m6jS^spcC z>ur~ESEFsFJFxx7bI_3E!9VGp{4x2xl3OTJHH)omB`1@l4a0nl;l6>uP;mNs2ljy> zN^+mojIpk3LiH^dv6i%;jrm;0M%m6!t)~Np>aIl}cgP%{6 zMH!f#zH!-vdrS!VXc!dFn!Ooki?ZZ|;QFrt^f1uqzNHD`t)I$ULFGQCu|1%`t4jY6 znuEG8W-^$k9*AWvnUy#}5`7?a-@|}Ktb<{mDGs2d7SG;gw|7PvYEw#3S%HShfJlc^ z#i0CHr1{IjK_gMW4~B{=sDXlY!m^QW1L-Yw<)hLhjs~4R!eBd4i7bIyVHwp z!>Z1@PQg>u#hytz)E}BIo#M`zLZJXZ?}$pFyu!~)WiE%C1H}Y+J~Dvw24%P^>!gnr z2noQlgcjdS#(qVqIi_Lz_&!Or;0h~W{DxyiBC5+TTqr>#A≶Kv^jPi;6YtYIK17 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 delta 24859 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 K3~al9TK*5xk5(rD From bee9b0bc12e3fa46bdf52bd458aacb0a51798100 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 13 Jan 2024 04:01:09 +0100 Subject: [PATCH 24/64] #1662 [Survey] fix: review request changes --- class/control.class.php | 15 ++++++++------- class/digiqualidashboard.class.php | 2 +- class/survey.class.php | 6 +++--- langs/fr_FR/digiquali.lang | 14 +++++++++----- view/survey/survey_card.php | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/class/control.class.php b/class/control.class.php index 0101da8d..ca414410 100644 --- a/class/control.class.php +++ b/class/control.class.php @@ -895,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' ] ]; diff --git a/class/digiqualidashboard.class.php b/class/digiqualidashboard.class.php index 560cbae7..8fc8f6d2 100644 --- a/class/digiqualidashboard.class.php +++ b/class/digiqualidashboard.class.php @@ -53,7 +53,7 @@ public function load_dashboard(): array require_once __DIR__ . '/survey.class.php'; $control = new Control($this->db); - $survey = new Survey($this->db); + $survey = new Survey($this->db); $array['control'] = $control->load_dashboard(); $array['survey'] = $survey->load_dashboard(); diff --git a/class/survey.class.php b/class/survey.class.php index e43fb68f..e8d78c97 100644 --- a/class/survey.class.php +++ b/class/survey.class.php @@ -528,15 +528,15 @@ public function getNbSurveysByMonth(): array $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' ] ]; diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 3b4aa22c..7124410d 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -371,11 +371,15 @@ controldocument_photo = fiche de controle avec photos # # Data - Donnée -Survey = Questionnaire -Surveys = Questionnaires -SurveyList = Liste des questionnaires -NewSurvey = Nouveau questionnaire -TheSurvey = le questionnaire +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 à lié +ConfirmValidateSurvey = Êtes-vous sûr de vouloir valider le questionnaire ? diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index d82d8e21..7e790f04 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -627,7 +627,7 @@ // Archive $displayButton = $onPhone ? '' : '' . ' ' . $langs->trans('Archive'); - if ($object->status == Survey::STATUS_LOCKED && !empty(dol_dir_list($upload_dir . '/'. $object->element . '/document/' . dol_sanitizeFileName($object->ref)))) { + if ($object->status == Survey::STATUS_LOCKED && !empty(dol_dir_list($upload_dir . '/'. $object->element . 'document/' . dol_sanitizeFileName($object->ref)))) { print '' . $displayButton . ''; } else { print '' . $displayButton . ''; From 29921ee0c5a9f21ba3fe40af2e104ae60bbe34aa Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 13 Jan 2024 05:37:08 +0100 Subject: [PATCH 25/64] #1668 [Sheet] add: manage sheet type --- class/sheet.class.php | 12 +--- core/modules/modDigiQuali.class.php | 15 +++++ core/tpl/digiquali_control_list.tpl.php | 2 +- core/tpl/digiquali_survey_list.tpl.php | 2 +- langs/fr_FR/digiquali.lang | 86 ++++++++++++------------- view/sheet/sheet_card.php | 18 ++++-- view/survey/survey_card.php | 2 +- 7 files changed, 78 insertions(+), 59 deletions(-) diff --git a/class/sheet.class.php b/class/sheet.class.php index 749ddd11..38560865 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. @@ -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"; diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 71a10dd5..8934dca2 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -198,6 +198,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'], @@ -757,6 +758,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/tpl/digiquali_control_list.tpl.php b/core/tpl/digiquali_control_list.tpl.php index 7901d975..a7004726 100644 --- a/core/tpl/digiquali_control_list.tpl.php +++ b/core/tpl/digiquali_control_list.tpl.php @@ -310,7 +310,7 @@ 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'); + 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); diff --git a/core/tpl/digiquali_survey_list.tpl.php b/core/tpl/digiquali_survey_list.tpl.php index dfeebbfd..7a15ec98 100644 --- a/core/tpl/digiquali_survey_list.tpl.php +++ b/core/tpl/digiquali_survey_list.tpl.php @@ -326,7 +326,7 @@ 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'); + 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); diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 7124410d..cf0ed735 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -59,8 +59,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 de le 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 @@ -78,7 +78,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 une ou plusieurs modèles. ErrorNoQuestionSelected = Aucune question sélectionnée AddQuestionIntoCategory = Assigner cette catégorie à la question QuestionType = Type de question @@ -113,37 +113,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 = Tous 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 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. 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 @@ -229,33 +229,33 @@ 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 @@ -303,7 +303,7 @@ ExpiredSince = Expiré depuis RemainingDays = Jours avant expiration NoEquipmentLinked = Aucun moyen de contrôle lié 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 @@ -403,13 +403,13 @@ surveydocument_photo.odt = Fiche du Questionnaire avec photos # 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) diff --git a/view/sheet/sheet_card.php b/view/sheet/sheet_card.php index 7a6ae255..ba4d929f 100644 --- a/view/sheet/sheet_card.php +++ b/view/sheet/sheet_card.php @@ -347,6 +347,11 @@ $doleditor->Create(); print ''; + // Type -- Type + print '
'; + //FK Element $linkableObject = 0; foreach ($elementArray as $key => $element) { @@ -429,7 +434,12 @@ $doleditor->Create(); print ''; - //FK Element + // Type -- Type + print ''; + + //FK Element $elementLinked = json_decode($object->element_linked); foreach ($elementArray as $key => $element) { @@ -611,11 +621,11 @@ } if (empty($reshook) && $permissiontoadd) { - // Create control + // Create object depending on sheet type if ($object->status == $object::STATUS_LOCKED) { - print ' ' . $langs->trans('CreateControl') . ''; + print ' ' . $langs->trans('New' . ucfirst($object->type)) . ''; } else { - print ' ' . $langs->trans('CreateControl') . ''; + print ' ' . $langs->trans('New' . ucfirst($object->type)) . ''; } // Modify diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index 7e790f04..be67ed5d 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -258,7 +258,7 @@ //FK SHEET print ''; From 3c4b1b8a00b5588572d422d128f976fd019508c1 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 13 Jan 2024 14:37:20 +0100 Subject: [PATCH 26/64] #1671 [Admin] add: config answerPublicInterfaceTitle --- admin/control.php | 27 ---- admin/publicinterface.php | 134 ++++++++++++++++++ core/modules/modDigiQuali.class.php | 3 +- .../substitutions/functions_digiquali.lib.php | 38 +++++ core/substitutions/index.php | 2 + langs/fr_FR/digiquali.lang | 8 +- lib/digiquali.lib.php | 5 + public/public_answer.php | 5 +- 8 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 admin/publicinterface.php create mode 100644 core/substitutions/functions_digiquali.lib.php create mode 100644 core/substitutions/index.php diff --git a/admin/control.php b/admin/control.php index f6b86024..4a6b50d9 100644 --- a/admin/control.php +++ b/admin/control.php @@ -99,15 +99,6 @@ setEventMessage('SavedConfig'); } -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 */ @@ -232,20 +223,6 @@ print ''; print ''; -print '
'; -print ''; -print ''; - -print '
'; - -print ''; - print '
' . $langs->trans('PublicControl') . ' '; - print ' '; - print ''; - print '' . saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/control/' . $object->ref . '/qrcode/', 'small', 1, 0, 0, 0, 80, 80, 0, 0, 0, 'control/'. $object->ref . '/qrcode/', $object, '', 0, 0) . '
'; - $publicSurveyUrl = dol_buildpath('custom/digiquali/public/control/public_survey.php?track_id=' . $object->track_id . '&entity=' . $conf->entity, 3); - print $langs->trans('PublicSurvey'); - print ' '; - print showValueWithClipboardCPButton($publicSurveyUrl, 0, ' '); - print ''; - print ''. $langs->trans('GoToPublicSurveyPage') .' '; - print '
' . $langs->trans('PublicInterface') . ' '; + print ' '; + print ''; + print '' . saturne_show_medias_linked('digiquali', $conf->digiquali->multidir_output[$conf->entity] . '/survey/' . $object->ref . '/qrcode/', 'small', 1, 0, 0, 0, 80, 80, 0, 0, 0, 'survey/'. $object->ref . '/qrcode/', $object, '', 0, 0) . '
'; + $publicAnswerUrl = dol_buildpath('custom/digiquali/public/public_answer.php?track_id=' . $object->track_id . '&object_type=' . $object->element . '&entity=' . $conf->entity, 3); + print $langs->trans('PublicAnswer'); + print ' '; + print showValueWithClipboardCPButton($publicAnswerUrl, 0, ' '); + print ''; + print '' . $langs->trans('GoToPublicAnswerPage') . ' '; + print '
' . $langs->trans('Categories') . '' . ($object->status < Control::STATUS_LOCKED ? '' . img_edit($langs->trans('Modify')) . '' : ''); - print $form->showCategories($object->id, 'control', 1) . ''; - print ''; - print ''; - print ''; - - $cats = $category->containing($object->id, 'control'); - $arrayselected = array(); - foreach ($cats as $cat) { - $arrayselected[] = $cat->id; - } - - print img_picto('', 'category') . $form->multiselectarray('categories', $categoryArborescence, $arrayselected, '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0); - print ''; - print ''; - print '
'; - print $langs->trans($linkableElement['langs']); - print ''; - - print $linkedObject->getNomUrl(1); - - if ($linkedObject->array_options['options_qc_frequency'] > 0) { - print ' '; - print ''; - print $langs->transnoentities('QcFrequency') . ' : ' . $linkedObject->array_options['options_qc_frequency']; - print ''; - } - - print '
'; - $pathPhotos = $conf->digiquali->multidir_output[$conf->entity] . '/control/'. $object->ref . '/photos/'; + if (isModEnabled('categorie')) { + print '
' . $langs->trans('Categories') . '' . ($object->status < Survey::STATUS_LOCKED ? '' . img_edit($langs->trans('Modify')) . '' : ''); + print $form->showCategories($object->id, 'survey', 1) . ''; + print '
'; + print ''; + print ''; + + $cats = $category->containing($object->id, 'survey'); + $arraySelected = []; + if (is_array($cats)) { + foreach ($cats as $cat) { + $arraySelected[] = $cat->id; + } + } + print img_picto('', 'category') . $form::multiselectarray('categories', $categoriesArborescence, (GETPOSTISSET('categories') ? GETPOST('categories', 'array') : $arraySelected), '', 0, 'quatrevingtpercent widthcentpercentminusx'); + print ''; + print '
'; + print '
'; + print $langs->trans($linkableElement['langs']); + print ''; + + print $linkedObject->getNomUrl(1); + print '
'; + $pathPhotos = $conf->digiquali->multidir_output[$conf->entity] . '/survey/'. $object->ref . '/photos/'; $fileArray = dol_dir_list($pathPhotos, 'files'); - ?> - status < Control::STATUS_LOCKED) ? '' : 'style="display:none"' ?>> - - - - - - ref . '/photos/', $object, 'photo', $object->status < Control::STATUS_LOCKED, $permissiontodelete && $object->status < Control::STATUS_LOCKED); - print '
' . $langs->trans('Type') . ''; + print $form::selectarray('type', $object->fields['type']['arrayofkeyval'], GETPOST('type')); + print '
' . $langs->trans('Type') . ''; + print $form::selectarray('type', $object->fields['type']['arrayofkeyval'], $object->type); + print '
' . $langs->trans('Sheet') . ''; - print img_picto('', $sheet->picto, 'class="pictofixedwidth"') . $sheet->selectSheetList(GETPOST('fk_sheet') ?: $sheet->id); + print img_picto('', $sheet->picto, 'class="pictofixedwidth"') . $sheet->selectSheetList(GETPOST('fk_sheet') ?: $sheet->id, 'fk_sheet', 's.type = ' . '"' . $object->element . '"'); print ''; print '
'; -print $langs->trans('PublicSurveyTitle'); -print ''; -print $langs->trans('PublicSurveyTitleDescription'); -print ''; -print ''; -print '
'; print $langs->trans('EnablePublicControlHistory'); print ''; @@ -281,10 +258,6 @@ print '
'; -print '
'; - -print ''; - print load_fiche_titre($langs->trans('ControlReminder'), '', ''); print '
'; diff --git a/admin/publicinterface.php b/admin/publicinterface.php new file mode 100644 index 00000000..c3af7dcb --- /dev/null +++ b/admin/publicinterface.php @@ -0,0 +1,134 @@ + + * + * 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 ''; + +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 $form->buttonsSaveCancel('Save', ''); +print ''; + +// Page end +print dol_get_fiche_end(); +llxFooter(); +$db->close(); diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 8934dca2..044453aa 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -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) @@ -271,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'], diff --git a/core/substitutions/functions_digiquali.lib.php b/core/substitutions/functions_digiquali.lib.php new file mode 100644 index 00000000..97bfdd47 --- /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|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 $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 @@ + (par défaut : navigateur) controlled = contrôlé(e) PublicAnswer = Interface publique de réponse -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 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 diff --git a/lib/digiquali.lib.php b/lib/digiquali.lib.php index 74cf7c89..76795eb8 100644 --- a/lib/digiquali.lib.php +++ b/lib/digiquali.lib.php @@ -63,6 +63,11 @@ function digiquali_admin_prepare_head(): array $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/public/public_answer.php b/public/public_answer.php index 82b74363..614ca420 100644 --- a/public/public_answer.php +++ b/public/public_answer.php @@ -125,7 +125,10 @@ print ''; ?>
- ' . $conf->global->DIGIQUALI_PUBLIC_SURVEY_TITLE . ''; + transnoentities($conf->global->DIGIQUALI_ANSWER_PUBLIC_INTERFACE_TITLE), $substitutionArray); + print '

' . (dol_strlen($answerPublicInterfaceTitle) > 0 ? $answerPublicInterfaceTitle : $langs->transnoentities('AnswerPublicInterface')) . '

'; print '
'; $publicInterface = true; $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); From f5eed5f3ab5e0f3c82398b12032e5cba337b32d0 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Wed, 24 Jan 2024 17:33:29 +0100 Subject: [PATCH 27/64] #1687 [Trans] fix: traduction --- langs/fr_FR/digiquali.lang | 22 +++++++++++----------- manifest.json.php | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 1043084e..5a431ca7 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -47,8 +47,8 @@ DisplayMediasSampleDescription = Afficher les médias d'exemple des questions da ExtrafieldsSurveyManagement = Gestion des attributs supplémentaires des questionnaires # Public interface - Interface publique -AnswerPublicInterface = Interface publique de réponse au questions pour __OBJECT_ELEMENT_REF__ -AnswerPublicInterfaceTitle = Titre de l'interface publique de réponse au questions +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__ @@ -66,7 +66,7 @@ NewQuestion = Nouvelle question ModifyQuestion = Modifier une question LinkedQuestionsList = Liste des questions liées addQuestionLink = Question ajoutée au modèle : -removeQuestionLink = Question retirée de le 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 @@ -84,7 +84,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. +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 @@ -126,7 +126,7 @@ SheetList = Liste des modèles NewSheet = Nouveau modèle ModifySheet = Modifier un modèle AddSheet = Ajouter un modèle -AllQuestionsMustHaveLocked = Tous les questions doivent être verrouillées pour verrouiller le 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 @@ -138,10 +138,10 @@ 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. 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 -ErrorSheetUsedInControl = Le modèle %s ne peut pas être supprimé, il est utilisé dans un ou plusieurs contrôles. +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 AddSheetIntoCategory = Assigner cette catégorie au modèle @@ -299,7 +299,7 @@ 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 @@ -382,7 +382,7 @@ 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 à lié +NeedObjectToLink = Vous devez sélectionner un objet à lier ConfirmValidateSurvey = Êtes-vous sûr de vouloir valider le questionnaire ? @@ -423,8 +423,8 @@ 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 diff --git a/manifest.json.php b/manifest.json.php index a3262248..dbaa7db2 100644 --- a/manifest.json.php +++ b/manifest.json.php @@ -103,7 +103,7 @@ $manifest->display_override = ['window-controls-overlay']; $manifest->scope = dol_buildpath('/custom/digiquali/', 1); $manifest->theme_color = '#ffffff'; -$manifest->description = 'DigiQualiDescription'; +$manifest->description = 'Système de management de la qualité pour Dolibarr'; $img = new stdClass(); $img->src = dol_buildpath('/custom/digiquali/img/digiquali_color_512.png', 1); From 963934bfb5d0444b8d9da3ea002401602e1dbb2f Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 25 Jan 2024 16:28:00 +0100 Subject: [PATCH 28/64] #1698 [JS/CSS] fix: regenerate min files --- css/digiquali.min.css | 2 +- js/digiquali.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/css/digiquali.min.css b/css/digiquali.min.css index fb1f6f5b..33acca0a 100644 --- a/css/digiquali.min.css +++ b/css/digiquali.min.css @@ -1 +1 @@ -.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}.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/js/digiquali.min.js b/js/digiquali.min.js index 8e5208f7..e32ab982 100644 --- a/js/digiquali.min.js +++ b/js/digiquali.min.js @@ -1 +1 @@ -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 i=void 0,t=void 0;for(i in window.digiquali)for(t in window.digiquali[i].init&&window.digiquali[i].init(),window.digiquali[i])window.digiquali[i]&&window.digiquali[i][t]&&window.digiquali[i][t].init&&window.digiquali[i][t].init();window.digiquali.scriptsLoaded=!0}},window.digiquali.refresh=function(){var i=void 0,t=void 0;for(i in window.digiquali)for(t in window.digiquali[i].refresh&&window.digiquali[i].refresh(),window.digiquali[i])window.digiquali[i]&&window.digiquali[i][t]&&window.digiquali[i][t].refresh&&window.digiquali[i][t].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)},window.digiquali.control.getAnswerCounter=function(i){let t=0;jQuery("#tablelines").children().each(function(){0<$(this).find(".answer.active").length&&(t+=1)}),document.cookie="answerCounter="+t},window.digiquali.control.showSelectObjectLinked=function(){var i=$(this).val(),t=window.saturne.toolbox.getToken(),o=window.saturne.toolbox.getQuerySeparator(document.URL),o=document.URL+o+"fk_sheet="+i+"&token="+t;window.saturne.loader.display($(".linked-objects")),$.ajax({url:o,type:"POST",processData:!1,contentType:!1,success:function(i){$(".linked-objects").replaceWith($(i).find(".linked-objects"))},error:function(){}})},window.digiquali.control.copyToClipboard=function(i){var t=$(".copy-to-clipboard").attr("value");navigator.clipboard.writeText(t).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(i){var t=document.getElementById("add_control_equipment"),t=new FormData(t),o=window.saturne.toolbox.getToken(),t=t.get("productId"),o=document.URL+"&token="+o;o+="&fk_product="+t,window.saturne.loader.display($(".product-lot")),$.ajax({url:o,type:"POST",processData:!1,contentType:!1,success:function(i){$(".product-lot").replaceWith($(i).find(".product-lot"))},error:function(){}})},window.digiquali.control.switchPublicControlView=function(i){var t=$(this).find(".public-control-view").val(),o=window.saturne.toolbox.getToken();let e=document.URL+"&token="+o;e+=0==t?"&show_control_list=1":"&show_last_control=1",window.saturne.loader.display($(".signature-container")),$.ajax({url:e,type:"POST",processData:!1,contentType:!1,success:function(i){$("#publicControlHistory").replaceWith($(i).find("#publicControlHistory"))},error:function(){}})},window.digiquali.control.showOnlyQuestionsWithNoAnswer=function(){var i=window.saturne.toolbox.getQuerySeparator(document.URL),t=window.saturne.toolbox.getToken();let o;o=$(this).hasClass("fa-toggle-off")?1:0,window.saturne.loader.display($(this)),$.ajax({url:document.URL+i+"action=show_only_questions_with_no_answer&token="+t,type:"POST",processData:!1,data:JSON.stringify({showOnlyQuestionsWithNoAnswer:o}),contentType:!1,success:function(i){$(".progress-info").replaceWith($(i).find(".progress-info")),$(".question-answer-container").replaceWith($(i).find(".question-answer-container"))},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 i=document.getElementById("createObjectForm"),i=new FormData(i),t=$(this).attr("id");0<=i.get(t)?$(".object-table.linked-objects").find("select").not("#"+t).attr("disabled",1):$(".object-table.linked-objects").find("select").not("#"+t).removeAttr("disabled")},window.digiquali.object.selectAnswer=function(){var t=$(this).closest(".select-answer.answer-cell"),i=t.attr("data-questionId"),o=$(this).closest(".table-id-"+i).attr("data-publicInterface"),e=$(this).closest(".table-id-"+i).attr("data-autoSave");let n="";var a=$(this).hasClass("answer")?$(this).attr("value"):$(this).val(),s=$(this).closest(".table-id-"+i).find("#comment"+i).val();if($(this).closest(".table-cell").hasClass("select-answer")){if($(this).hasClass("multiple-answers")){$(this).closest("span").toggleClass("active");let i=[];t.find(".multiple-answers.active").each(function(){i.push($(this).attr("value"))}),n=i}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")?(t=$(this).closest(".answer-cell").find(".answer-color-"+$(this).attr("value")).val(),$(this).attr("style",$(this).attr("style")+" background:"+t+";")):$(this).attr("style",$(this).attr("style")+" background:#fff;"),$(this).closest(".answer-cell").find(".question-answer").val(n)}o||1!=e||$(this).hasClass("multiple-answers")?window.digiquali.object.updateButtonsStatus():window.digiquali.object.saveAnswer(i,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(i,t,o){var e=window.saturne.toolbox.getToken(),n=window.saturne.toolbox.getQuerySeparator(document.URL);window.saturne.loader.display($(".table-id-"+i)),$.ajax({url:document.URL+n+"action=save&token="+e,type:"POST",data:JSON.stringify({autoSave:!0,questionId:i,answer:t,comment:o}),processData:!1,contentType:!1,success:function(i){$(".fiche").replaceWith($(i).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(i){$(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(i){$("#dialogforpopup").attr("style","border:")},window.digiquali.question.showPhoto=function(){var i=$(this).closest(".question-table").find(".linked-medias");i.hasClass("hidden")?(i.attr("style",""),i.removeClass("hidden")):(i.attr("style","display:none"),i.addClass("hidden"))},window.digiquali.question.selectAnswerPicto=function(i){var t=$(this).closest(".wpeo-dropdown");$(this).closest(".content").removeClass("active"),t.find(".dropdown-toggle span").hide(),t.find(".dropdown-toggle.button-picto").html($(this).closest(".wpeo-tooltip-event").html()),t.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 From eee78a9787778f228989af897fb42ed02be6fea0 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 25 Jan 2024 16:51:39 +0100 Subject: [PATCH 29/64] #1700 [Control] fix: no display success_score if no percentage questions And divide averagePercentageQuestions by percentQuestionCounter and not by questionCounter --- view/control/control_card.php | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/view/control/control_card.php b/view/control/control_card.php index 2a8202d9..33ad8591 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -720,28 +720,14 @@ print saturne_show_medias_linked('digiquali', $pathPhotos, 'small', 0, 0, 0, 0, $onPhone ? 40 : 50, $onPhone ? 40 : 50, 0, 0, 0, 'control/'. $object->ref . '/photos/', $object, 'photo', $object->status < Control::STATUS_LOCKED, $permissiontodelete && $object->status < Control::STATUS_LOCKED); print '
'; - print $form->editfieldkey('SuccessScore', 'success_rate', $object->success_rate, $object, $permissiontoadd && $object->status < Control::STATUS_LOCKED, 'string', '', 0, 0,'id', $langs->trans('PercentageValue')); - print ''; - if ($action == 'editsuccess_rate') { - print '
'; - print ''; - print ''; - print ''; - print '
'; - print '
'; - print '
'; - } else { - print price2num($object->success_rate) . ' %'; - } - print '
'; + print $form->editfieldkey('SuccessScore', 'success_rate', $object->success_rate, $object, $permissiontoadd && $object->status < Control::STATUS_LOCKED, 'string', '', 0, 0,'id', $langs->trans('PercentageValue')); + print ''; + if ($action == 'editsuccess_rate') { + print '
'; + print ''; + print ''; + print ''; + print '
'; + print '
'; + print '
'; + } else { + print price2num($object->success_rate) . ' %'; + } + print '
'; print $langs->trans('AveragePercentageQuestions'); print ''; From f49d956771b928e23e3387c98d6f0de6c898fdd1 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 25 Jan 2024 09:38:53 +0100 Subject: [PATCH 30/64] #1688 [Control] add: warning if no categories generated on image view --- admin/sheet.php | 2 +- langs/fr_FR/digiquali.lang | 1 + view/control/control_card.php | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/admin/sheet.php b/admin/sheet.php index 29042bee..58a70520 100644 --- a/admin/sheet.php +++ b/admin/sheet.php @@ -225,7 +225,7 @@ print ''; print ''; -print '
' . $langs->trans('GenerateCategories') . '
' . $langs->trans('GenerateCategories') . ''; print $conf->global->DIGIQUALI_SHEET_TAGS_SET ? $langs->trans('AlreadyGenerated') : $langs->trans('NotCreated'); print '
'; print ''; } else { - print price2num($object->success_rate) . ' %'; + print (!empty($object->success_rate) ? price2num($object->success_rate) : 0) . ' %'; } print ''; diff --git a/view/sheet/sheet_card.php b/view/sheet/sheet_card.php index ba4d929f..44c9f16e 100644 --- a/view/sheet/sheet_card.php +++ b/view/sheet/sheet_card.php @@ -548,7 +548,7 @@ print ''; print ''; } else { - print price2num($object->success_rate) . ' %'; + print (!empty($object->success_rate) ? price2num($object->success_rate) : 0) . ' %'; } print ''; From 3ad5757f68eb0a684b951f23534d1daf7b39d78d Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 25 Jan 2024 18:02:12 +0100 Subject: [PATCH 32/64] #1688 [Control] fix: better warning --- admin/sheet.php | 4 ++-- view/control/control_card.php | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/admin/sheet.php b/admin/sheet.php index 58a70520..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 ''; @@ -225,7 +225,7 @@ print ''; print ''; -print ''; +print ''; print ''; diff --git a/view/control/control_card.php b/view/control/control_card.php index 7576d8c7..f1465ffe 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -313,11 +313,15 @@ } if ($viewmode == 'images') { - if (getDolGlobalInt('DIGIQUALI_SHEET_MAIN_CATEGORIES_SET') <= 0) { + if (!getDolGlobalInt('DIGIQUALI_SHEET_MAIN_CATEGORIES_SET')) { print ''; + print '' . '
'.$langs->trans('GenerateSheetTags') . ' : ' . $langs->trans('ConfigSheet') . '
'; + print ''; + print '
' . $langs->trans('GenerateCategories') . '
' . $langs->trans('GenerateCategories') . ''; print $conf->global->DIGIQUALI_SHEET_TAGS_SET ? $langs->trans('AlreadyGenerated') : $langs->trans('NotCreated'); print '
'; + print dol_get_fiche_end(); + print ''; + exit; } print '
'; From 505199e509492ccc7f59bbb79941616a66c3900d Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 26 Jan 2024 09:34:41 +0100 Subject: [PATCH 33/64] #1669 [PublicSurvey] add: public survey page --- langs/fr_FR/digiquali.lang | 4 +- public/survey/index.php | 2 + public/survey/public_survey.php | 101 ++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 public/survey/index.php create mode 100644 public/survey/public_survey.php diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 14f54860..c473ab01 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -385,7 +385,8 @@ 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 # @@ -429,3 +430,4 @@ Contact = Contact/Adresse Remain = reste InProgressAndLocked = -- (En cours + verrouillé) -- NoObservations = Pas d'observations + diff --git a/public/survey/index.php b/public/survey/index.php new file mode 100644 index 00000000..eea59b98 --- /dev/null +++ b/public/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 . + */ + +/** + * \file public/survey/public_survey.php + * \ingroup digiquali + * \brief Public page to view survey + */ + +if (!defined('NOREQUIREUSER')) { + define('NOREQUIREUSER', '1'); +} +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', '1'); +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', 1); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOLOGIN')) { // This means this output page does not require to be logged. + define('NOLOGIN', '1'); +} +if (!defined('NOCSRFCHECK')) { // We accept to go on this page from external website. + define('NOCSRFCHECK', '1'); +} +if (!defined('NOIPCHECK')) { // Do not check IP defined into conf $dolibarr_main_restrict_ip. + define('NOIPCHECK', '1'); +} +if (!defined('NOBROWSERNOTIF')) { + define('NOBROWSERNOTIF', '1'); +} + +// 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 DigiQuali libraries +require_once __DIR__ . '/../../../digiquali/class/survey.class.php'; + +// Global variables definitions +global $conf, $db, $hookmanager, $langs; + +// Load translation files required by the page +saturne_load_langs(); + +// Get parameters +$track_id = GETPOST('track_id', 'alpha'); +$entity = GETPOST('entity'); + +// Initialize technical objects +$object = new Survey($db); + +$hookmanager->initHooks(['publicsurvey']); // Note that conf->hooks_modules contains array + +if (!isModEnabled('multicompany')) { + $entity = $conf->entity; +} + +$conf->setEntityValues($db, $entity); + +// Load object +$object->fetch(0, '', ' AND track_id = ' . "'" . $track_id . "'"); + +/* + * View + */ + +$title = $langs->trans('PublicSurvey'); + +$conf->dol_hide_topmenu = 1; +$conf->dol_hide_leftmenu = 1; + +saturne_header(0, '', $title); + +print '
'; +print '
' . $langs->trans('PublicSurveyToCome') . '
'; +print '
'; + +llxFooter('', 'public'); +$db->close(); From 3094b95dbb92744e81fddbaadaed7f067f656e37 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 26 Jan 2024 10:15:45 +0100 Subject: [PATCH 34/64] #1690 [Survey] add: success_rate field --- class/survey.class.php | 6 +++ ...9_modDigiQuali_DigiQualiTriggers.class.php | 11 +++++ sql/survey/llx_digiquali_survey.sql | 1 + sql/update.sql | 4 +- view/survey/survey_card.php | 41 +++++++++++++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/class/survey.class.php b/class/survey.class.php index e8d78c97..81d2bfb0 100644 --- a/class/survey.class.php +++ b/class/survey.class.php @@ -122,6 +122,7 @@ class Survey extends SaturneObject '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' => 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'], @@ -184,6 +185,11 @@ class Survey extends SaturneObject */ public ?string $photo = ''; + /** + * @var float|string|null Success rate + */ + public $success_rate; + /** * @var string|null TrackID */ diff --git a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php index f7d2dea5..4c9a8c73 100644 --- a/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php +++ b/core/triggers/interface_99_modDigiQuali_DigiQualiTriggers.class.php @@ -164,6 +164,17 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf 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)) { diff --git a/sql/survey/llx_digiquali_survey.sql b/sql/survey/llx_digiquali_survey.sql index 2eec6816..c7bf6e11 100644 --- a/sql/survey/llx_digiquali_survey.sql +++ b/sql/survey/llx_digiquali_survey.sql @@ -26,6 +26,7 @@ CREATE TABLE llx_digiquali_survey( note_private text, type varchar(128), photo text, + success_rate double(24,8), track_id varchar(128) NOT NULL, fk_user_creat integer NOT NULL, fk_user_modif integer, diff --git a/sql/update.sql b/sql/update.sql index 915ad074..c212c626 100644 --- a/sql/update.sql +++ b/sql/update.sql @@ -116,8 +116,10 @@ UPDATE `llx_ecm_directories` SET label = REPLACE(label, 'dolismq', 'digiquali') ALTER TABLE `llx_digiquali_control_equipment` ADD `fk_lot` integer AFTER `fk_product`; ALTER TABLE `llx_digiquali_control` ADD `control_date` DATETIME AFTER `next_control_date`; - -- 1.10.0 ALTER TABLE `llx_digiquali_sheet` ADD `photo` TEXT NULL AFTER `element_linked`; ALTER TABLE `llx_digiquali_sheet` ADD `success_rate` DOUBLE(24,8) NULL AFTER `photo`; ALTER TABLE `llx_digiquali_control` ADD `success_rate` DOUBLE(24,8) NULL AFTER `next_control_date`; + +-- 1.11.0 +ALTER TABLE `llx_digiquali_survey` ADD `success_rate` DOUBLE(24,8) NULL AFTER `photo`; diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index be67ed5d..ef09c8d7 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -525,6 +525,47 @@ print saturne_show_medias_linked('digiquali', $pathPhotos, 'small', 0, 0, 0, 0, $onPhone ? 40 : 50, $onPhone ? 40 : 50, 0, 0, 0, 'survey/' . $object->ref . '/photos/', $object, 'photo', $object->status < Survey::STATUS_LOCKED, $permissiontodelete && $object->status < Survey::STATUS_LOCKED); print ''; + $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) { + print ''; + print $form->editfieldkey('SuccessScore', 'success_rate', $object->success_rate, $object, $permissiontoadd && $object->status < Survey::STATUS_LOCKED, 'string', '', 0, 0,'id', $langs->trans('PercentageValue')); + print ''; + if ($action == 'editsuccess_rate') { + print '
'; + print ''; + print ''; + print ''; + print '
'; + print '
'; + print '
'; + } else { + print (!empty($object->success_rate) ? price2num($object->success_rate) : 0) . ' %'; + } + print ''; + + print ''; + print $langs->trans('AveragePercentageQuestions'); + print ''; + print '' . price2num($averagePercentageQuestions) . ' %
'; + print ''; + } + // Other attributes. Fields from hook formObjectOptions and Extrafields require_once DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php'; From 046e1cb484cbac570d4fb29e392f4dac6066aa81 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 26 Jan 2024 10:34:06 +0100 Subject: [PATCH 35/64] #1706 [Sheet] fix: status field search doesn't work --- view/sheet/sheet_list.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/sheet/sheet_list.php b/view/sheet/sheet_list.php index 4a410ae5..6b381875 100644 --- a/view/sheet/sheet_list.php +++ b/view/sheet/sheet_list.php @@ -278,7 +278,7 @@ $sql .= natural_search($key, implode(',', $newStatus), 2); } continue; - } elseif ($key == 'status' && $val == -1) { + } elseif (($key == 'status' || $key == 'type') && $val == -1) { continue; } $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0); From fedaec31f551a423c5f54d25a86ebc3517d12e72 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Fri, 26 Jan 2024 11:47:46 +0100 Subject: [PATCH 36/64] #1693 [Conf] fix: rework control configuration page --- admin/control.php | 167 +++--------------------------- admin/survey.php | 4 +- class/actions_digiquali.class.php | 42 ++++++++ 3 files changed, 57 insertions(+), 156 deletions(-) diff --git a/admin/control.php b/admin/control.php index 4a6b50d9..e5d2ddc7 100644 --- a/admin/control.php +++ b/admin/control.php @@ -30,21 +30,19 @@ 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'); @@ -59,15 +57,14 @@ } // 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); -$error = 0; //Error counter +$hookmanager->initHooks(['controladmin', '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->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'); @@ -124,140 +109,10 @@ 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 '
'; - $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 '
' . $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('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 load_fiche_titre($langs->trans('ControlReminder'), '', ''); print '
'; @@ -270,6 +125,7 @@ print '' . $langs->trans('Value') . ''; print ''; +// Enable control reminder print ''; print $langs->trans('ControlReminder'); print ''; @@ -280,6 +136,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 ''; @@ -290,6 +147,7 @@ print ''; print ''; +// Define control reminder type print ''; print $langs->trans('ControlReminderType'); print ''; @@ -309,6 +167,7 @@ //Extrafields control management print load_fiche_titre($langs->trans('ExtrafieldsControlManagement'), '', ''); +$textobject = dol_strtolower($langs->transnoentities('Control')); require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; // Buttons diff --git a/admin/survey.php b/admin/survey.php index 129ac62e..d0e426fd 100644 --- a/admin/survey.php +++ b/admin/survey.php @@ -97,6 +97,8 @@ 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 */ @@ -105,8 +107,6 @@ 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'; - // Extrafields survey management print load_fiche_titre($langs->trans('ExtrafieldsSurveyManagement'), '', ''); diff --git a/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index e048f8fb..64c21aa2 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -416,6 +416,48 @@ public function saturneAdminObjectConst(array $parameters): int return 1; } + if (strpos($parameters['context'], 'controladmin') !== false) { + $constArray['digiquali'] = [ + '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; + return 1; + } + return 0; // or return 1 to replace standard code. } } From fa953bb9721217adaca055c3cfb785700504e823 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Fri, 26 Jan 2024 12:26:11 +0100 Subject: [PATCH 37/64] #1709 [SQL] fix: sheet type not null and update empty value with control --- sql/sheet/llx_digiquali_sheet.sql | 2 +- sql/update.sql | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sql/sheet/llx_digiquali_sheet.sql b/sql/sheet/llx_digiquali_sheet.sql index 5feefe74..0575754d 100644 --- a/sql/sheet/llx_digiquali_sheet.sql +++ b/sql/sheet/llx_digiquali_sheet.sql @@ -22,7 +22,7 @@ CREATE TABLE llx_digiquali_sheet( tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, import_key varchar(14), status integer DEFAULT 1 NOT NULL, - type varchar(128), + type varchar(128) NOT NULL, label varchar(255) NOT NULL, description text, element_linked text, diff --git a/sql/update.sql b/sql/update.sql index 915ad074..4e3535f5 100644 --- a/sql/update.sql +++ b/sql/update.sql @@ -121,3 +121,7 @@ ALTER TABLE `llx_digiquali_control` ADD `control_date` DATETIME AFTER `next_cont ALTER TABLE `llx_digiquali_sheet` ADD `photo` TEXT NULL AFTER `element_linked`; ALTER TABLE `llx_digiquali_sheet` ADD `success_rate` DOUBLE(24,8) NULL AFTER `photo`; ALTER TABLE `llx_digiquali_control` ADD `success_rate` DOUBLE(24,8) NULL AFTER `next_control_date`; + + +-- 1.11.0 +ALTER TABLE llx_digiquali_sheet CHANGE `type` `type` VARCHAR(128) NOT NULL; From edfd08d8967aee77c9b1b57e39ddbe87d7dad36d Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 27 Jan 2024 22:41:07 +0100 Subject: [PATCH 38/64] #1689 [ODT] fix: setAttendantsSegment function missing parameters --- .../controldocument/doc_controldocument_odt.modules.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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..ac2eb775 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. @@ -397,8 +403,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); } From ce0b718bfa2627539c57f3633273270164835249 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 27 Jan 2024 22:50:36 +0100 Subject: [PATCH 39/64] #1690 [Survey] add: ODT change average calcul --- .../doc_surveydocument_odt.modules.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index da6ccd30..dacb62be 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -304,24 +304,28 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); - $questionCounter = count($sheet->linkedObjectsIds['digiquali_question']); - - $average = 0; + $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) { - $average += $line->answer; + $averagePercentageQuestions += $line->answer; } } } - $average = ($questionCounter > 0) ? ($average / $questionCounter) : 0; + $averagePercentageQuestions = ($percentQuestionCounter > 0) ? ($averagePercentageQuestions / $percentQuestionCounter) : 0; - $tmpArray['average'] = price2num($average) . ' %'; + 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; From 88168c2f974333279de1de82cd9f0de88ecbeab5 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Sat, 27 Jan 2024 23:11:56 +0100 Subject: [PATCH 40/64] #1693 [Admin] fix: clean control conf --- admin/control.php | 42 ++++++++++++++++------------- class/actions_digiquali.class.php | 15 +---------- core/modules/modDigiQuali.class.php | 4 +-- langs/fr_FR/digiquali.lang | 11 +++++--- 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/admin/control.php b/admin/control.php index e5d2ddc7..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,16 +18,16 @@ /** * \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'); } // Load Dolibarr libraries @@ -53,7 +53,7 @@ $tmptype2label = ExtraFields::$type2label; $type2label = ['']; foreach ($tmptype2label as $key => $val) { - $type2label[$key] = $langs->transnoentitiesnoconv($val); + $type2label[$key] = $langs->transnoentitiesnoconv($val); } // Initialize objects @@ -61,7 +61,7 @@ $hookmanager->initHooks(['controladmin', 'globalcard']); // Note that conf->hooks_modules contains array -$elementtype = $moduleNameLowerCase . '_' . $object->element; // Must be the $table_element of the class that manage extrafield. +$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; @@ -82,6 +82,8 @@ dolibarr_set_const($db, 'DIGIQUALI_CONTROL_REMINDER_TYPE', $reminderType, 'chaine', 0, '', $conf->entity); setEventMessage('SavedConfig'); + header('Location: ' . $_SERVER['PHP_SELF']); + exit; } /* @@ -99,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 @@ -109,10 +111,15 @@ require __DIR__ . '/../../saturne/core/tpl/admin/object/object_const_view.tpl.php'; +/* + * Numbering module line + */ + $object = new ControlLine($db); require __DIR__ . '/../../saturne/core/tpl/admin/object/object_numbering_module_view.tpl.php'; +// Control reminder print load_fiche_titre($langs->trans('ControlReminder'), '', ''); print ''; @@ -164,33 +171,32 @@ print ''; print ''; -//Extrafields control management +// Extrafields control management print load_fiche_titre($langs->trans('ExtrafieldsControlManagement'), '', ''); $textobject = dol_strtolower($langs->transnoentities('Control')); -require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php'; +require DOL_DOCUMENT_ROOT . '/core/tpl/admin_extrafields_view.tpl.php'; // Buttons if ($action != 'create' && $action != 'edit') { - 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/class/actions_digiquali.class.php b/class/actions_digiquali.class.php index 64c21aa2..3d2fd62f 100644 --- a/class/actions_digiquali.class.php +++ b/class/actions_digiquali.class.php @@ -381,24 +381,11 @@ public function saturneAdminDocumentData(array $parameters): int */ public function saturneAdminObjectConst(array $parameters): int { - // Do something only for the current context -// if ($parameters['currentcontext'] == 'digiqualiadmindocuments') { -// $constArray['digiquali'] = [ -//// 'controldocument' => [ -//// 'name' => 'ControlDocumentDisplayMedias', -//// 'description' => 'ControlDocumentDisplayMediasDescription', -//// 'code' => 'DIGIQUALI_CONTROLDOCUMENT_DISPLAY_MEDIAS' -//// ] -// ]; -// $this->results = $constArray; -// return 1; -// } - if (strpos($parameters['context'], 'surveyadmin') !== false) { $constArray['digiquali'] = [ 'DisplayMedias' => [ 'name' => 'DisplayMediasSample', - 'description' => 'DisplayMediasSampleDescription', + 'description' => 'DisplaySurveyMediasSampleDescription', 'code' => 'DIGIQUALI_SURVEY_DISPLAY_MEDIAS', ], 'UseLargeSizeMedia' => [ diff --git a/core/modules/modDigiQuali.class.php b/core/modules/modDigiQuali.class.php index 044453aa..797ae70e 100644 --- a/core/modules/modDigiQuali.class.php +++ b/core/modules/modDigiQuali.class.php @@ -127,7 +127,8 @@ public function __construct($db) 'digiqualiadmindocuments', 'projecttaskscard', 'main', - 'surveyadmin' + 'controladmin', + 'surveyadmin', ], // Set this to 1 if features of module are opened to external users 'moduleforexternal' => 0, @@ -234,7 +235,6 @@ 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 diff --git a/langs/fr_FR/digiquali.lang b/langs/fr_FR/digiquali.lang index 14f54860..24e13ce9 100644 --- a/langs/fr_FR/digiquali.lang +++ b/langs/fr_FR/digiquali.lang @@ -41,10 +41,14 @@ 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 -DisplayMediasSample = Afficher les médias d'exemple des questions -DisplayMediasSampleDescription = Afficher les médias d'exemple des questions dans le questionnaire -ExtrafieldsSurveyManagement = Gestion des attributs supplémentaires des questionnaires +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__ @@ -266,7 +270,6 @@ NoLotDefined = Pas de numéro de lot/série tr 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 From a988685acc8d7e619d2c649a5514c5ec670df025 Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Thu, 1 Feb 2024 20:58:41 +0100 Subject: [PATCH 41/64] #1601 [Sheet] fix: error manage question position --- class/sheet.class.php | 95 +------------------ .../doc_controldocument_odt.modules.php | 6 +- .../doc_surveydocument_odt.modules.php | 4 +- .../tpl/digiquali_answers_save_action.tpl.php | 2 +- public/public_answer.php | 2 +- view/control/control_card.php | 6 +- view/digiqualitools.php | 4 +- view/sheet/sheet_card.php | 6 +- view/sheet/sheet_export.php | 2 +- view/sheet/sheet_list.php | 2 +- view/survey/survey_card.php | 2 +- 11 files changed, 21 insertions(+), 110 deletions(-) diff --git a/class/sheet.class.php b/class/sheet.class.php index 38560865..db658e5d 100644 --- a/class/sheet.class.php +++ b/class/sheet.class.php @@ -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; @@ -467,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/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php index ac2eb775..30fecb8f 100644 --- a/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/controldocument/doc_controldocument_odt.modules.php @@ -133,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'])) { @@ -201,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); } diff --git a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php index dacb62be..af0f7ab0 100644 --- a/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php +++ b/core/modules/digiquali/digiqualidocuments/surveydocument/doc_surveydocument_odt.modules.php @@ -118,7 +118,7 @@ public function fillTagsLines(Odf $odfHandler, Translate $outputLangs, array $mo $sheet = new Sheet($this->db); $answer = new Answer($this->db); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $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) { @@ -302,7 +302,7 @@ public function write_file(SaturneDocuments $objectDocument, Translate $outputLa $tmpArray['actioncomm_creation_date'] = dol_print_date($actionComms[0]->datec, 'dayhour', 'tzuser'); } - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element); $averagePercentageQuestions = 0; $percentQuestionCounter = 0; diff --git a/core/tpl/digiquali_answers_save_action.tpl.php b/core/tpl/digiquali_answers_save_action.tpl.php index 1c4d3860..8e77ce11 100644 --- a/core/tpl/digiquali_answers_save_action.tpl.php +++ b/core/tpl/digiquali_answers_save_action.tpl.php @@ -32,7 +32,7 @@ $data = json_decode(file_get_contents('php://input'), true); $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element); if (!empty($sheet->linkedObjects['digiquali_question'])) { foreach ($sheet->linkedObjects['digiquali_question'] as $question) { if (!empty($object->lines)) { diff --git a/public/public_answer.php b/public/public_answer.php index 614ca420..191e2003 100644 --- a/public/public_answer.php +++ b/public/public_answer.php @@ -131,7 +131,7 @@ print '

' . (dol_strlen($answerPublicInterfaceTitle) > 0 ? $answerPublicInterfaceTitle : $langs->transnoentities('AnswerPublicInterface')) . '

'; print '
'; $publicInterface = true; - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position'); require_once __DIR__ . '/../core/tpl/digiquali_answers.tpl.php'; print '
'; print '
'; diff --git a/view/control/control_card.php b/view/control/control_card.php index a0aec9fb..462becc5 100644 --- a/view/control/control_card.php +++ b/view/control/control_card.php @@ -520,7 +520,7 @@ // SetValidated confirmation if (($action == 'setValidated' && (empty($conf->use_javascript_ajax) || !empty($conf->dol_use_jmobile))) || (!empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))) { $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position', 0); $questionIds = $sheet->linkedObjectsIds['digiquali_question']; if (!empty($questionIds)) { @@ -786,7 +786,7 @@ print '
'; $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element); $questionIds = $sheet->linkedObjectsIds['digiquali_question']; $cantValidateControl = 0; @@ -931,7 +931,7 @@ print '
'; $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position'); $questionIds = $sheet->linkedObjectsIds['digiquali_question']; if (is_array($questionIds) && !empty($questionIds)) { diff --git a/view/digiqualitools.php b/view/digiqualitools.php index 3b04a0fc..17d7ab7f 100644 --- a/view/digiqualitools.php +++ b/view/digiqualitools.php @@ -87,7 +87,7 @@ $digiqualiExportArray['sheets'][$sheetSingle->id] = $sheetExportArray; - $sheetSingle->fetchQuestionsLinked($sheetSingle->id, 'digiquali_sheet', null, '', 'OR', 1, 'sourcetype', 0); + $sheetSingle->fetchObjectLinked($sheetSingle->id, 'digiquali_' . $sheetSingle->element, null, '', 'OR', 1, 'position', 0); $questionsLinked = $sheetSingle->linkedObjectsIds['digiquali_question']; if (is_array($questionsLinked) && !empty($questionsLinked)) { foreach ($questionsLinked as $questionId) { @@ -292,7 +292,7 @@ $question->add_object_linked('digiquali_sheet', $newSheetId); $sheet->fetch($newSheetId); - $questionsLinked = $sheet->fetchQuestionsLinked($newSheetId, 'digiquali_sheet', null, '', 'OR', 1, 'sourcetype', 0); + $questionsLinked = $sheet->fetchObjectLinked($newSheetId, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position', 0); $questionIds = $sheet->linkedObjectsIds['digiquali_question']; $sheet->updateQuestionsPosition($questionIds); diff --git a/view/sheet/sheet_card.php b/view/sheet/sheet_card.php index 44c9f16e..748a592d 100644 --- a/view/sheet/sheet_card.php +++ b/view/sheet/sheet_card.php @@ -117,7 +117,7 @@ $question->fetch($questionId); $test = $question->add_object_linked('digiquali_' . $object->element,$id); - $questionsLinked = $object->fetchQuestionsLinked($id, 'digiquali_' . $object->element); + $questionsLinked = $object->fetchObjectLinked($id, 'digiquali_' . $object->element, null, '', 'OR', 1, 'position', 0); $questionIds = $object->linkedObjectsIds['digiquali_question']; $object->updateQuestionsPosition($questionIds); @@ -137,7 +137,7 @@ $question->element = 'digiquali_'.$question->element; $question->deleteObjectLinked($id, 'digiquali_' . $object->element); - $questionsLinked = $object->fetchQuestionsLinked($id, 'digiquali_' . $object->element); + $questionsLinked = $object->fetchObjectLinked($id, 'digiquali_' . $object->element, null, '', 'OR', 1, 'position', 0); $questionIds = $object->linkedObjectsIds['digiquali_question']; $object->updateQuestionsPosition($questionIds); @@ -604,7 +604,7 @@ print '
'; - $object->fetchQuestionsLinked($id, 'digiquali_' . $object->element); + $object->fetchObjectLinked($id, 'digiquali_' . $object->element); $questionIds = $object->linkedObjectsIds['digiquali_question']; if (is_array($questionIds) && !empty($questionIds)) { ksort($questionIds); diff --git a/view/sheet/sheet_export.php b/view/sheet/sheet_export.php index 35f5dabf..ea769ad5 100644 --- a/view/sheet/sheet_export.php +++ b/view/sheet/sheet_export.php @@ -108,7 +108,7 @@ $digiqualiExportArray['sheets'][$object->id] = $sheetExportArray; - $object->fetchQuestionsLinked($object->id, 'digiquali_sheet'); + $object->fetchObjectLinked($object->id, 'digiquali_' . $object->element); $questionsLinked = $object->linkedObjects['digiquali_question']; if (is_array($questionsLinked) && !empty($questionsLinked)) { diff --git a/view/sheet/sheet_list.php b/view/sheet/sheet_list.php index 6b381875..2a3f5f52 100644 --- a/view/sheet/sheet_list.php +++ b/view/sheet/sheet_list.php @@ -518,7 +518,7 @@ if ($key == 'status') print $object->getLibStatut(5); elseif ($key == 'ref') print $object->getNomUrl(1); elseif ($key == 'nb_questions') { - $object->fetchQuestionsLinked($object->id, 'digiquali_sheet', null, '', 'OR', 1, 'sourcetype', 0); + $object->fetchObjectLinked($object->id, 'digiquali_' . $object->element, null, '', 'OR', 1, 'position', 0); if (isset($object->linkedObjectsIds['digiquali_question']) && is_array($object->linkedObjectsIds['digiquali_question'])) { print count($object->linkedObjectsIds['digiquali_question']); } else { diff --git a/view/survey/survey_card.php b/view/survey/survey_card.php index ef09c8d7..d18a3aee 100644 --- a/view/survey/survey_card.php +++ b/view/survey/survey_card.php @@ -345,7 +345,7 @@ saturne_banner_tab($object, 'ref', '', 1, 'ref', 'ref', '', !empty($object->photo)); $sheet->fetch($object->fk_sheet); - $sheet->fetchQuestionsLinked($object->fk_sheet, 'digiquali_' . $sheet->element); + $sheet->fetchObjectLinked($object->fk_sheet, 'digiquali_' . $sheet->element, null, '', 'OR', 1, 'position'); $questionIds = $sheet->linkedObjectsIds['digiquali_question']; $questionCounter = 0; From d4cc157a82c23e7b2f76693358f091452a15b9f3 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Wed, 7 Feb 2024 15:44:32 +0100 Subject: [PATCH 42/64] #1717 [Lib] fix: remove object type --- core/substitutions/functions_digiquali.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/substitutions/functions_digiquali.lib.php b/core/substitutions/functions_digiquali.lib.php index 97bfdd47..dabef883 100644 --- a/core/substitutions/functions_digiquali.lib.php +++ b/core/substitutions/functions_digiquali.lib.php @@ -32,7 +32,7 @@ * @return void The entry parameter $substitutionarray is modified * @throws Exception */ -function digiquali_completesubstitutionarray(array &$substitutionarray, Translate $langs, ?object $object) +function digiquali_completesubstitutionarray(array &$substitutionarray, Translate $langs, $object) { $substitutionarray['__OBJECT_ELEMENT_REF__'] = $langs->transnoentities('The' . ucfirst($object->element)) . ' ' . $object->ref; } From ee8d35a67ec0ae6429fb84d41d0c2f4dad8b16f3 Mon Sep 17 00:00:00 2001 From: evarisk-micka Date: Thu, 8 Feb 2024 16:39:42 +0100 Subject: [PATCH 43/64] #1725 [Control] add: track_id in control list --- class/control.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class/control.class.php b/class/control.class.php index ca414410..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'], From 62c8c91b8694aa1c4c89daa42833081aa654619f Mon Sep 17 00:00:00 2001 From: Nicolas Domenech Date: Fri, 9 Feb 2024 10:57:04 +0100 Subject: [PATCH 44/64] #1730 [Public] fix: missing temp directory in documents --- documents/temp/index.php | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 documents/temp/index.php diff --git a/documents/temp/index.php b/documents/temp/index.php new file mode 100644 index 00000000..cd6990e2 --- /dev/null +++ b/documents/temp/index.php @@ -0,0 +1,2 @@ + Date: Wed, 14 Feb 2024 12:37:16 +0100 Subject: [PATCH 45/64] #1732 [Question] fix: button picto dropdown content --- lib/digiquali_answer.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 .= '