gestione gare pubbliche
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

1658 righe
65 KiB

<?php
trait eFormsManager_render {
use BS4FormGenerator;
function printField($field) {
// Field
$this->printFieldHeader($field );
echo $this->{$field["type"]}($field);
$this->printFieldFooter();
}
/**
* Stampa una sezione basandosi sul modello concettuale
*
* @param ?String $name nome della sezione
* @param Array $section struttura della sezione
* @return void
*/
function printSection(?String $name, Array $section) {
// WORKAROUND se un field si trova nella root
if(str_starts_with($name, "eforms2")) {
return $this->printField($section);
}
// Repeatable
if(!isset($section["__meta"])) {
foreach($section as $key => $member) {
$this->printSection($key, $member);
}
return;
}
extract($section["__meta"]);
// Extract __meta fields as variables
// Clear them from the array
unset($section["__meta"]);
$auto_collapse = str_contains(strtolower($name), "unpublish");
$collapsed = $auto_collapse ? "collapsed" : "";
$collapse_button = $this->button(
[
"name" => "button",
"required" => false,
"size" => "pr-2",
"type" => "button",
"title" => "<span class=\"fa fa-sm fa-window-minimize\"></span><span class=\"fa fa-sm fa-plus\"></span>",
"attrs" => [
"data-toggle" => "collapse",
"data-target" => "#collapse-{$unique_identifier}",
"class" => "btn btn-sm btn-block btn-outline-secondary mr-2 {$collapsed}",
]
]);
// Display headers
if($display_type == "SECTION") {
$this->printSectionHeader($this->translate($label), $id, $unique_identifier, $collapse_button);
} else if($display_type == "GROUP") {
$add_button = $delete_button = "";
// First iteration
if($repeatable) {
if($iteration == 0) {
$add_button = $this->createAddButton($unique_identifier, $section_object, $this->notice, $this->info['codice'], $prefix);
} elseif($preventDelete === false) {
$delete_button = $this->createDeleteButton($unique_identifier);
}
}
$this->printGroupHeader(
$this->translate($label),
$id,
$unique_identifier,
$repeatable,
$iteration,
$total_iterations,
$add_button,
$delete_button,
$collapse_button,
$tabNameProvider ?? $this->translate($label) . " - " . ($iteration + 1)
);
}
// For each member
foreach($section as $key => $member) {
// Print iteration
if($display_type == "SECTION") {
}
if(isset($member["name"])) {
$this->printField($member);
} else {
// Section
$this->printSection($key, $member);
}
}
if($display_type == "SECTION") {
$this->printSectionFooter();
}
if($display_type == "GROUP") {
$this->printGroupFooter(
$this->translate($label),
$id,
$unique_identifier,
$repeatable,
$iteration,
$total_iterations,
$add_button,
$delete_button,
$collapse_button
);
}
}
/**
* Print GUUE FORM
*
* @param mixed $section
* @return void
*/
public function printForm(Array $section, Int $subsection_index) : void {
$full_structure = [];
$section_structure = $this->createFormStructure($section, $subsection_index, true, $full_structure);
if($_SESSION["utente"]->isSupportoOrRoot()) {
// Print settings toggles
?>
<div class="card mb-3">
<div class="card-header">
<p class="h5 mb-0 text-muted">Strumenti di debug</p>
</div>
<div class="card-body">
<div class="row mx-0">
<?php
//$this->printNoEFXButton($this->enable_efx_evaluation);
$this->printShowLabelsButton($this->hide_labels);
$this->printDebugEfxButton($this->advanced_debug);
$this->printForcedSuggestionsButton($this->enable_forced_suggestions);
// Print debug area
if($this->mode === "edit" && $this->advanced_debug) {
?> <div class="col-12 mt-3"> <?php
$this->printDebugArea($full_structure);
?> </div> <?php
}
?>
</div>
</div>
</div>
<?php
}
// Renderizziamo le integrazioni floating
$this->renderIntegrationHelpers("floating");
echo "<input type='hidden' name='section-repetition' value='{$subsection_index}'>";
$ready = empty($this->info["codice"]) ? "" : "data-ready=1";
echo "<fieldset id='master-fieldset' disabled {$ready}>";
foreach($section_structure as $key => $section) {
if($key === "__meta") continue;
$this->printSection($key, $section);
}
// Prepare autosave URL
if($this->enable_efx_evaluation) {
$autosave_url = "/backend/guue/save.php?";
// Check for valid section
if(preg_match("/GR-([A-Za-z0-9-_])+/i", $_GET["section"] ?? "") === 1) {
$autosave_url .= "section=" . $_GET["section"];
// Check for valid subsection
if(is_numeric($_GET["subsection_index"] ?? "")) {
$autosave_url .= "&subsection_index=" . $_GET["subsection_index"];
}
}
?>
<script type="text/javascript">
$(document).ready(() => {
eForms2.update_uri = "<?= $autosave_url ?>";
eForms2.setup();
// Popoliamo i nomi interni
eForms2.makeInternalLabels();
// Popoliamo i tasti di pin
eForms2.addIntegrationPinButtons();
//eForms2.onFormChange(null);
});
</script>
<style>
button[data-toggle] svg[data-icon='window-minimize'] {
display: inline-block;
}
button[data-toggle].collapsed svg[data-icon='window-minimize'] {
display: none;
}
button[data-toggle] svg[data-icon='plus'] {
display: none;
}
button[data-toggle].collapsed svg[data-icon='plus'] {
display: inline-block;
}
code { user-select: all;}
/* Workaround for grow columns */
@media (min-width: 1200px) {
.col-xl-6 {
flex: 1 0 50%;
max-width: 100%;
}
}
/* Workaround for excessive padding */
.form-group.col-sm-12 {
padding: 0 !important;
}
/* Workaround for tab-pane row */
.tab-pane .row .active {
display: flex !important;
}
.tab-content > .active.row {
display: flex !important;
}
.btn-xs {
padding: 0 0.4rem;
font-size: 0.8rem;
}
#master-fieldset > .col-12 {
padding-left: 0 !important; padding-right: 0 !important;
}
</style>
<?php
}
// Prepare ADD2GUUE
?>
<script type="text/javascript">
$(document).ready(() => {
eForms2.codice = <?= $this->info["codice"] ?>;
});
</script>
<?php
$this->insertJavascript();
}
protected function printIntegrationSectionHeader(string $label, string $unique_identifier, string $collapse_button, string $position) {
?>
<div class="row mb-3 <?= $position === "floating" ? "" : "col-12" ?> integration-section-header" id='<?= $unique_identifier ?>'>
<div class="col-12 ">
<div class="card">
<div class="card-header">
<div class="row d-flex align-items-center">
<div class="col">
<p class="h5 mb-0 text-muted">
<span class="badge bg-primary text-white">
<span class="fa fa-lightbulb fa-sm"></span>
<span><?= __guue("Suggerimenti") ?></span>
</span>
<span><?= $label ?></span>
</p>
</div>
<?= $collapse_button ?>
</div>
</div>
<div class="card-body">
<div class="collapse show" id="collapse-<?= $unique_identifier ?>">
<?
}
protected function printSectionHeader($label, $name, $unique_identifier, $collapse_button) {
?>
<div class="row mb-3 section-header" id='<?= $unique_identifier ?>'>
<div class="col-12 ">
<div class="card">
<div class="card-header">
<div class="row d-flex align-items-center">
<div class="col">
<p class="h2 border-bottom text-primary"><?= $label ?> <?php if(!$this->hide_labels): ?> <small>(<?= $name ?>)</small> <?php endif; ?></p>
</div>
<?= $collapse_button ?>
</div>
</div>
<div class="card-body px-1">
<div class="collapse show" id="collapse-<?= $unique_identifier ?>">
<?
$this->renderIntegrationHelpers($name);
}
protected function printGroupHeader(?String $label, ?String $name, String $unique_identifier , bool $repeatable, int $iteration, int $total_iterations, string $add_button, string $delete_button, string $collapse_button, string $tabName) : void {
$auto_collapse = str_contains(strtolower($name), "unpublish");
if($repeatable) {
?>
<?php if ($iteration === 0) : ?>
<!-- Header d'iterazione -->
<div class="col-12">
<div class="card repeatable-card mb-3">
<div class="card-header">
<div class="row d-flex align-items-center">
<div class="col">
<p class="h4 mb-0 text-muted"><?= $label ?> <?php if(!$this->hide_labels): ?> <small>(<?= $name ?>)</small> <?php endif; ?> </p>
</div>
<?= $add_button ?>
<?= $collapse_button ?>
</div>
</div>
<div class="card-body px-1">
<div class="collapse show" id="collapse-<?= $unique_identifier ?>">
<?= $this->renderIntegrationHelpers($name); ?>
<div class="repeatable_container" id="repeatable_<?= $unique_identifier ?>">
<?php endif; ?>
<div id='<?= $unique_identifier ?>' data-tabname="<?= $tabName ?>" class="group-header pr-0 col-12">
<?php if($iteration !== 0) : ?>
<?= $delete_button ?>
<?php endif; ?>
<?
} else {
?>
<div id='<?= $unique_identifier ?>' class="group-header col-12 mb-3">
<div class="card">
<div class="card-header">
<div class="row d-flex align-items-center">
<div class="col">
<p class="h4 mb-0 text-muted"><?= $label ?> <?php if(!$this->hide_labels): ?> <small>(<?= $name ?>)</small> <?php endif; ?> </p>
</div>
<?= $collapse_button ?>
</div>
</div>
<div class="card-body px-1">
<div class="collapse row mx-0 <?php if(!$auto_collapse): ?>show <?php endif; ?>" id="collapse-<?= $unique_identifier ?>">
<? $this->renderIntegrationHelpers($name);
}
}
protected function printGroupFooter(?String $label, ?String $name, String $unique_identifier = "", bool $repeatable, int $iteration, int $total_iterations, string $add_button, string $delete_button, string $collapse_button) : void {
if($repeatable) {
$close_repeatable_container = $repeatable && ($iteration >= ($total_iterations-1));
?>
</div> <!-- group-header -->
<?php if ($close_repeatable_container) : ?>
</div> <!-- repeatable -->
</div> <!-- collapse -->
</div> <!-- card-body -->
</div> <!-- card -->
</div> <!-- col -->
<?php endif; ?>
<?
} else {
?>
</div> <!-- collapse -->
</div> <!-- card body -->
</div> <!-- card -->
</div> <!-- group-header -->
<?
}
}
protected function printFieldHeader($field)
{
$unique_identifier = "wrapper_{$field['name']}";
$forbidden = $field["forbidden"] ?? false;
$hidden = str_contains($field["type"] ?? "", "hidden");
?>
<div id="<?= $unique_identifier ?>" class="field-wrapper col-12 col-xl-6 <?= $forbidden || $hidden ? "d-none" : "" ?>" >
<?
}
protected function printFieldFooter()
{
?>
</div>
<?
}
protected function printSectionFooter()
{
?>
</div>
</div>
</div>
</div>
</div>
<?
}
protected function printIntegrationSectionFooter()
{
?>
</div>
</div>
</div>
</div>
</div>
<?
}
/**
* Make add2GUUE object for ajax request or div
*
* @param Array $settings
* @return String
*/
public static function makeAdd2GUUEObject(Array $settings) : String
{
return base64_encode(gzdeflate(json_encode($settings)));
}
/**
* Creates the remove repetition button
*
* @param string $unique_identifier
* @return void
*/
protected function createDeleteButton(string $unique_identifier) : string {
return $this->button(
[
"name" => "button",
"required" => false,
"size" => "pr-2",
"type" => "button",
"title" => "<span class=\"fa fa-sm fa-times\"></span>",
"attrs" => [
"class" => "btn btn-xs btn-danger shadow-none ml-3 align-middle guue-delete-button",
"style" => "float: right; margin-top: 0.2em;",
"onClick" => "eForms2.deleteRepeatable('{$unique_identifier}'); return false;"
]
]);
}
protected function createAddButton($unique_identifier, $section, $notice, $codice, $prefix) {
$object = $this->makeAdd2GUUEObject(
[
"section" => $section,
"notice" => $notice,
"codice" => $codice,
"prefix" => $prefix
]);
$button = [
"name" => "button",
"required" => false,
"size" => "pr-2",
"type" => "button",
"title" => "<span class=\"fa fa-plus\"></span> <span class='ml-1'> " . __guue("Aggiungi") . "</span>",
"attrs" => [
"onClick" => "eForms2.add2GUUE('#{$unique_identifier}', '{$object}');",
"class" => "btn btn-sm btn-block btn-primary guue-add-button"
]
];
return $this->button($button);
}
protected function printShowLabelsButton(bool $hide_labels) {
?>
<div class="col-4 custom-control custom-switch custom-switch-lg py-2 ml-3">
<input <?= !$hide_labels ? "checked" : "" ?> type="checkbox" class="custom-control-input" id="label_toggle" >
<label style="transform-origin: center left; transform: scale(1.25);" class="custom-control-label" for="label_toggle"><?= __guue("Mostra nomi interni") ?></label>
</div>
<script>
const label_toggle = document.getElementById("label_toggle");
if(label_toggle) {
label_toggle.addEventListener("change", () =>{
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("hide_labels", <?= $hide_labels ? "0" : "1" ?>);
window.location.search = searchParams.toString();
});
}
</script>
<?
}
/**
* Prints the button that enables/disables EFX interpretation
*
* @param boolean $efx Current status
* @return void
*/
protected function printNoEFXButton(bool $efx) {
?>
<div class="col-4 custom-control custom-switch custom-switch-lg py-2 ml-3">
<input <?= $efx ? "checked" : "" ?> type="checkbox" class="custom-control-input" id="efx_toggle" >
<label style="transform-origin: center left; transform: scale(1.25);" class="custom-control-label" for="efx_toggle"><?= __guue("Mostra solo rilevanti") ?></label>
</div>
<script>
const efx_toggle = document.getElementById("efx_toggle");
if(efx_toggle) {
efx_toggle.addEventListener("change", () =>{
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("efx_mode", <?= $efx ? "0" : "1" ?>);
window.location.search = searchParams.toString();
});
}
</script>
<?
}
/**
* Prints the button that enables/disables EFX debug
*
* @param boolean $efx Current status
* @return void
*/
protected function printDebugEfxButton(bool $advanced_debug) {
?>
<div class="col-4 custom-control custom-switch custom-switch-lg py-2 ml-3">
<input <?= $advanced_debug ? "checked" : "" ?> type="checkbox" class="custom-control-input" id="advanced_debug_toggle" >
<label style="transform-origin: center left; transform: scale(1.25);" class="custom-control-label" for="advanced_debug_toggle"><?= __guue("Modalità debug") ?></label>
</div>
<script>
const advanced_debug_toggle = document.getElementById("advanced_debug_toggle");
if(advanced_debug_toggle) {
advanced_debug_toggle.addEventListener("change", () =>{
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("advanced_debug", <?= $advanced_debug ? "0" : "1" ?>);
window.location.search = searchParams.toString();
});
}
</script>
<?
}
/**
* Prints the button that enables/disables EFX debug
*
* @param boolean $efx Current status
* @return void
*/
protected function printForcedSuggestionsButton(bool $forced_suggestion) {
?>
<div class="custom-control custom-switch custom-switch-lg py-2 ml-3">
<input <?= $forced_suggestion ? "checked" : "" ?> type="checkbox" class="custom-control-input" id="forced_suggestion_toggle" >
<label style="transform-origin: center left; transform: scale(1.25);" class="custom-control-label" for="forced_suggestion_toggle"><?= __guue("Suggerimenti forzati") ?></label>
</div>
<script>
const forced_suggestion_toggle = document.getElementById("forced_suggestion_toggle");
if(forced_suggestion_toggle) {
forced_suggestion_toggle.addEventListener("change", () =>{
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("forced_suggestion", <?= $forced_suggestion ? "0" : "1" ?>);
window.location.search = searchParams.toString();
});
}
</script>
<?
}
protected function printDebugArea($full_structure) {
?>
<div class='my-2' >
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#physical_model_collapse">
Modello fisico
</button>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#data_source_collapse">
Sorgente dati
</button>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#conceptual_model_collapse">
Modello concettuale
</button>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#settings_collapse">
Impostazioni
</button>
<?php if(!empty($this->suggestions)): ?>
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#suggestions_collapse">
Integrazioni
</button>
<?php endif; ?>
<div class="collapse my-2" id="physical_model_collapse">
<a class="btn btn-primary d-block" href='#' onclick="downloadElementContents(document.getElementById('debug_physical_model'), 'bozza_modello_fisico.xml')">
Download XML (bozza)
</a>
<pre id='debug_physical_model' style='width:100%; max-height: 300px; overflow-y: scroll'><?= print_r(htmlspecialchars($this->xml->saveXML()), true) ?></pre>
</div>
<div class="collapse my-2" id="conceptual_model_collapse">
<?php dump($full_structure); ?>
</div>
<div class="collapse my-2" id="data_source_collapse">
<?php
$data_source = $this->info["valori"]["eforms2"] ?? null;
if($data_source) {
dump($data_source);
}
?>
</div>
<div class="collapse my-2" id="settings_collapse">
<?php
if($this->info["valori"]["settings"] ?? false ) {
dump($this->info["valori"]["settings"]);
}
if($_SESSION["notice" . $this->notice] ?? false ) {
dump($_SESSION["notice" . $this->notice]);
}
dump($this->completed_subsections);
?>
</div>
<?php if(!empty($this->suggestions)): ?>
<div class="collapse my-2" id="suggestions_collapse">
<?php dump($this->suggestions) ?>
<?php dump($this->integrationHelperData) ?>
</div>
<?php endif; ?>
</div>
<?
}
// TODO Move in personal.js when everything is stable
protected function insertJavascript() {
?>
<script type="text/javascript">
//download debug contents
function downloadElementContents(element) {
xmlContent = element.textContent;
const blob = new Blob([xmlContent], {type: 'text/xml'})
const filename = 'debug.xml'
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob,filename);
} else {
const elem = window.document.createElement('a');
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
// eForms 2 EFX Autosave Code
if (typeof window.eForms2 === "undefined"){
window.eForms2 = {
codice : 0,
prevent_update : 0,
update_uri : "",
setup : function() {
if(eForms2.update_uri) {
$("#guue-form #master-fieldset :input").change(eForms2.onFormChange);
}
// Nascondi gruppi vuoti
eForms2.collapseEmpty();
eForms2.expandFull();
eForms2.groupRepeatables();
// Creiamo la navigazione interna
eForms2.makeNavigator();
// Inizializzazione completa
var masterFieldset = document.getElementById("master-fieldset");
if(!masterFieldset.dataset.ready) {
//masterFieldset.style.pointerEvents = "none";
}
masterFieldset.disabled = false;
},
toggleFixed : function(id, firstRun) {
const stickyContainer = document.getElementById("guue-sticky-container");
if(!stickyContainer) return;
const target = document.getElementById(id);
let cloneTarget = document.getElementById("sticky__" + id);
if(target) {
let sticky = false;
if(cloneTarget) {
cloneTarget.remove();
target.style.pointerEvents = null;
target.style.userSelect = null;
target.style.opacity = 1;
} else {
cloneTarget = target.cloneNode(true);
cloneTarget.id = "sticky__" + id;
stickyContainer.appendChild(cloneTarget);
target.style.pointerEvents = "none";
target.style.userSelect = "none";
target.style.opacity = "0.2";
sticky = true;
}
if(!firstRun) {
let savedSticky = JSON.parse(localStorage.getItem("eforms2-sticky-suggestions")) || {};
savedSticky[target.id] = sticky;
localStorage.setItem("eforms2-sticky-suggestions", JSON.stringify(savedSticky));
}
}
},
deleteRepeatable : function(id) {
Swal({
title: js_dict["sei-sicuro"],
text: js_dict["msg-conferma"],
type: "error",
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: js_dict.conferma,
cancelButtonText: js_dict.annulla,
closeOnConfirm: true
}).then((result) => {
if (result.value) {
document.getElementById(id).remove();
eForms2.onFormChange(null, function() {window.location.reload()});
}
});
},
addIntegrationPinButtons : function() {
const stickyContainer = document.createElement("div");
stickyContainer.id = "guue-sticky-container";
stickyContainer.style.position = "fixed";
stickyContainer.style.bottom = "2em";
stickyContainer.style.right = "1em";
stickyContainer.style.width = "80%";
stickyContainer.style.zIndex = 999;
stickyContainer.style.maxWidth = "1024px";
document.body.appendChild(stickyContainer);
document.querySelectorAll(".integration-section-header").forEach(function(integrationSection) {
// Cerchiamo il collapse button come riferimento
const collapseButton = integrationSection.querySelector(".guue-collapse-button");
if(collapseButton) {
const pinButtonContainer = document.createElement("div");
pinButtonContainer.classList.add("pr-2");
const pinButton = document.createElement("button");
pinButton.classList.add("btn", "btn-sm", "btn-secondary");
// Cambiamo icona
const newIcon = document.createElement("span");
newIcon.classList.add("fa", "fa-sm", "fa-thumbtack");
pinButton.appendChild(newIcon);
pinButtonContainer.appendChild(pinButton);
// Settiamo l'evento
pinButton.onclick = null;
pinButton.setAttribute("onclick", `event.preventDefault(); eForms2.toggleFixed(\"${integrationSection.id}\", false);`);
// Aggiungiamo l'elemento
collapseButton.parentElement.parentElement.insertBefore(pinButtonContainer, collapseButton.parentElement);
}
})
let savedSticky = JSON.parse(localStorage.getItem("eforms2-sticky-suggestions") ) || {};
for(let [id, value] of Object.entries(savedSticky)) {
if(value) {
eForms2.toggleFixed(id, true);
}
}
},
addOrganization : function(ragione, codice, pec) {
visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
let found = false;
if(ragione) {
// Ragione sociale
found = false;
document.querySelectorAll('input[name*="BT-500-Organization-Company"]:not([readonly])').forEach(function(el) {
if(found) return;
if(visible(el)) {
if(el.value) return;
found = true;
el.value = ragione;
}
});
if(!found) {
Swal({
title : "Attenzione",
html : "Per inserire un'orgranizzazione si prega di aggiungerne una nuova con il tasto \"Aggiungi\" e selezionarla.",
confirmButtonText : "Ok",
})
return;
}
}
if(codice) {
// Ragione sociale
let found = false;
document.querySelectorAll('input[name*="BT-501-Organization-Company"]:not([readonly])').forEach(function(el) {
if(found) return;
if(visible(el)) {
found = true;
el.value = codice;
}
});
}
if(pec) {
// Ragione sociale
let found = false;
document.querySelectorAll('input[name*="BT-506-Organization-Company"]:not([readonly])').forEach(function(el) {
if(found) return;
if(visible(el)) {
found = true;
el.value = pec;
}
});
}
eForms2.onFormChange(null);
},
makeInternalLabels : function() {
document.querySelectorAll("[data-internalname]").forEach(function(e) {
let node = e;
let label = null;
while(!label) {
node = node.parentNode;
label = node.querySelector("label");
}
if(label && !label.dataset.hasInternalLabel) {
const small = document.createElement("small");
const code = document.createElement("code");
label.classList.add("mr-2");
small.innerHTML = "&nbsp;";
small.appendChild(code);
code.classList.add("text-secondary");
code.classList.add("text-nowrap");
small.classList.add("mb-2");
small.classList.add("d-inline-block");
code.innerText = `${e.dataset.internalname}`;
label.dataset.hasInternalLabel = true;
label.after(small);
}
})
},
onFormChange : function(e, callback) {
if (e && (e.target.value, e.target.getAttribute("name") == null)) {
return;
}
if(e) {
// Verifichiamo la validazione del campo attuale
let validation = valida($(e.target));
// Se non è valido non lanciamo il refresh inutilmente
if(Array.isArray(validation) && validation.length > 0) {
console.error(valida($(e.target)));
if(e.target.value) {
return;
}
}
}
// Preveniamo update accavallati
if(eForms2.prevent_update > Date.now()) { console.error("Preventing update"); return; }
eForms2.prevent_update = Date.now() + 10000;
// Selezioniamo il form per farne il submit
const form = document.getElementById("guue-form");
const formData = new FormData(form);
formData.set("action", "autosave");
formData.set("draft", "S");
// Mostriamo l'attesa e disattiviamo tutto temporaneamente
$("#wait_div").show();
$("#guue-form #master-fieldset").prop("disabled", true);
// Facciamo una richiesta post all'autosave
fetch(
eForms2.update_uri,
{
method: 'post',
body: formData
}
)
// TODO Check if response is JSON
.then(response => response.json() )
.then(response => {
// Se la risposta chiede un redirect, obbediamo
if(response.redirect) {
window.location.href = response.redirect;
form.style.display = 'hidden';
return;
}
if(response.errors) {
// Todo, magari mostrare errors solo in debug
Swal({
title : "Qualcosa è andato storto",
html : response.errors,
confirmButtonText : "Ok",
})
}
// Traverse response recursively
eForms2.parseResponse(response);
// Settiamo la sezione attuale come draft visivamente
const activeButton = document.querySelector("#guue-sections > .list-group > .active");
if(activeButton.classList.contains("list-group-item-success")) {
activeButton.classList.remove("list-group-item-success");
activeButton.classList.add("list-group-item-warning");
const iconContainer = activeButton.querySelector("span");
const icon = iconContainer.querySelector("svg");
if(icon) {
icon.remove();
}
let newIcon = document.createElement("i");
newIcon.classList.add("fas");
newIcon.classList.add("fa-times-circle");
newIcon.classList.add("mr-2");
iconContainer.prepend(newIcon);
}
})
.catch(ex => {
swal(js_dict["error-retry"]);
console.error(ex);
})
.finally(() => {
if(typeof callback === "function") {
callback();
}
eForms2.collapseEmpty();
eForms2.makeNavigator();
$("#guue-form #master-fieldset").prop("disabled", false);
$("#wait_div").slideUp('fast');
// Selezioniamo il prossimo elemento per l'utente
/*if(e) {
eForms2.focusNextElement(e.target);
}*/
eForms2.updateTabNames();
eForms2.prevent_update = 0;
});
},
focusNextElement : function(activeElement) {
//add all elements we want to include in our selection
var focussableElements =
'input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
if (activeElement && activeElement.form) {
var focussable = Array.prototype.filter.call(
activeElement.form.querySelectorAll(focussableElements),
function (element) {
//check for visibility while always include the current activeElement
return (
element.offsetWidth > 0 ||
element.offsetHeight > 0 ||
element === activeElement
);
}
);
var index = focussable.indexOf(activeElement);
if (index > -1) {
var nextElement = focussable[index + 1] || focussable[0];
nextElement.focus();
}
}
},
generateRandomID : function() {
var S4 = function() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
},
updateTabNames : function() {
// Cerchiamo il nome della tab
document.querySelectorAll(".nav-link[data-bindtarget]").forEach(function(tabButton) {
const child = document.querySelector(`[data-bind="${tabButton.dataset.bindtarget}"]`);
const tabText = tabButton.querySelector(".nav-link-text");
if(!child || !tabText) return;
if(child.dataset.tabname[0] == "#") {
const candidates = child.dataset.tabname.split("|");
for(i in candidates) {
const candidate = candidates[i];
const toFind = candidate.split("#")[1];
const provider = child.querySelector(`input[name*="${toFind}"]`);
if(provider) {
tabText.innerText = provider.value.length > 0 ? provider.value : "Nuova scheda";
break;
} else {
tabText.innerText = candidate;
}
}
} else {
tabText.innerText = child.dataset.tabname;
}
});
},
groupRepeatables : function() {
document.querySelectorAll(".repeatable_container").forEach(function(el) {
let i = 0;
let tabs = null;
let tabHeaders = null;
let hasTabs = el.dataset.hasTabs;
let selectedBind = "";
let toSelect = null;
// Aggiunta o rimozione
if(hasTabs) {
// Otteniamo la tab correntemente selezionata
selectedBind = el.querySelector(".nav-link.active").dataset.bindtarget;
// Otteniamo, puliamo e rimuoviamo i vecchi header
tabHeaders = el.removeChild(document.getElementById(el.dataset.tabHeadersId));
tabHeaders.innerHTML = "";
// Otteniamo e rimuoviamo le vechie tab
tabs = el.removeChild(document.getElementById(el.dataset.tabsId));
// I vecchi elementi in tab vengono trasposti dentro il repeatable nell'ordine corretto
tempContainer = [];
index = 0;
tabs.childNodes.forEach(function(oldChild) {
if(oldChild.nodeType !== Node.ELEMENT_NODE)
return;
tempContainer.push(oldChild);
});
// Iteriamo i figli nuovi e verifichiamo che ce ne
// sia solo uno nuovo.
el.childNodes.forEach(function(newChild) {
if(newChild.id) {
if(toSelect === null) {
if(newChild.id) {
toSelect = newChild;
}
} else {
// Ne abbiamo più di uno, non selezioneremo nulla
toSelect = false;
}
}
});
tempContainer.forEach(function(oldChild){
el.insertBefore(oldChild, el.children[index++]);
});
// In questo modo abbiamo vecchi figli e poi i nuovi figli e possiamo far ricreare header e tutto
} else {
// Creiamo l'header
tabHeaders = document.createElement("ul");
tabHeaders.classList.add("nav");
tabHeaders.classList.add("nav-tabs");
tabHeaders.classList.add("px-4");
tabHeaders.setAttribute("role", "tablist");
tabHeaders.id = eForms2.generateRandomID();
// Creiamo il contenitore di tabs
tabs = document.createElement("div");
tabs.classList.add("tab-content");
tabs.id = eForms2.generateRandomID();
// Assegnamo id casuali per poterli riferire dopo
el.dataset.hasTabs = true;
el.dataset.tabHeadersId = tabHeaders.id;
el.dataset.tabsId = tabs.id;
i = 0;
}
let straightToTabs = [];
el.childNodes.forEach(function(child) {
if(child.nodeType !== Node.ELEMENT_NODE) {
return;
}
// Header della tab
let tabLi = document.createElement("li");
tabLi.classList.add("nav-item");
tabLi.setAttribute("role", "presentation");
let tabButton = document.createElement("button");
tabButton.classList.add("nav-link");
tabButton.dataset.toggle="tab";
tabButton.dataset.target=`#${child.id}`;
tabButton.style.minWidth = "90px";
tabButton.style.textAlign = "left";
tabLi.appendChild(tabButton);
tabHeaders.appendChild(tabLi);
// Creo un ID univoco per associare facilmente tab button e tab
if(child.dataset.bind) { // Riportiamo quello esistente se già c'è
// Nessuna azione necessaria
} else {
child.dataset.bind = eForms2.generateRandomID();
}
tabButton.dataset.bindtarget = child.dataset.bind;
tabText = document.createElement("span");
tabText.classList.add("nav-link-text");
tabButton.appendChild(tabText);
tabText.innerText = i+1;
// Tab in se
child.classList.add("tab-pane");
child.classList.add("fade");
child.classList.add("row");
straightToTabs.push(child);
// Cerchiamo di recuperare il tasto di eliminazione
if( i > 0) {
let deleteButton = child.querySelector(".guue-delete-button");
if(deleteButton) {
deleteButton.style.display = "inline";
tabButton.appendChild(deleteButton.cloneNode(true));
deleteButton.style.display = "none";
}
}
i++;
});
tabHeaders.style.display = "flex";
tabHeaders.classList.add("mb-3");
straightToTabs.forEach(function(child) {
tabs.appendChild(child);
});
el.dataset.iterations = i;
el.appendChild(tabHeaders);
el.appendChild(tabs);
// Se abbiamo un solo elemento non mostriamo le tab
if(i == 1) {
tabHeaders.style.display = "none";
}
// Una volta creati andiamo a caccia dell'attivo
let toBeActive = el.querySelector(`.nav-link[data-bindtarget]`)
// Cerchiamo di ripristinare quello che era già selezionato ove possibile
if(selectedBind.length > 0) {
let selectedActive = el.querySelector(`.nav-link[data-bindtarget="${selectedBind}"]`);
if(selectedActive) {
toBeActive = selectedActive;
}
}
// Settiamo attive sia tab button che child
if(toBeActive) {
// Però se abbiamo invece da selezionare uno nuovo
// perchè è quello appena selezionato
if(toSelect) {
// toBeActive viene deselezionato
toBeActive.classList.remove("active");
// Cerchiamo la sua tab
const child = document.querySelector(`[data-bind="${toBeActive.dataset.bindtarget}"]`);
if(child) {
child.classList.remove("show")
child.classList.remove("active");
}
// e quindi toSelect diventa l'effettivo toBeActive
toBeActive = document.querySelector(`[data-bindtarget="${toSelect.dataset.bind}"]`);
}
toBeActive.classList.add("active");
// Cerchiamo la sua tab
const child = document.querySelector(`[data-bind="${toBeActive.dataset.bindtarget}"]`);
if(child) {
child.classList.add("show")
child.classList.add("active");
}
}
});
eForms2.updateTabNames();
},
collapseEmpty : function() {
document.querySelectorAll(".group-header, .repeatable-card, .section-header").forEach(function(el) {
const visible = el.querySelectorAll(".field-wrapper:not(.d-none)");
if(visible.length == 0) {
el.classList.add("d-none");
} else {
el.classList.remove("d-none");
}
})
},
expandFull: function() {
$(".collapse").not(".show").each(function(){
let empty = true;
$(this).find("input").each(function(){
if ($(this).val() !== "" && $(this).val() !== undefined) {
empty = false;
}
})
$(this).find("select").each(function(){
if ($(this).val() !== "" && $(this).val() !== undefined) {
empty = false
}
})
if (!empty) {
const id = $(this).attr("id");
$(`[data-target="#${id}"]`).click()
}
})
},
parseResponse : function(arr) {
var k;
if (arr instanceof Object) {
for (k in arr){
if(k.startsWith("eforms2")) {
const data = arr[k];
// Find the wrapper
const wrapper = document.getElementById(`wrapper_${k}`);
if(wrapper) {
// Update children
wrapper.querySelectorAll("input, select, label:not(.form-check-label), .valida").forEach(function(el) {
eForms2.updateElement(el, data, k);
});
// Show / hide
if(data["forbidden"]) {
wrapper.classList.add("d-none");
} else {
wrapper.classList.remove("d-none");
}
}
} else if (arr.hasOwnProperty(k)){
//recursive call to scan property
eForms2.parseResponse( arr[k] );
}
}
}
},
// add2GUUE
add2GUUE : function (target, data) {
repeatable_target = "repeatable_" + target.substr(1);
let container = document.getElementById(repeatable_target);
let iterations = container.childElementCount;
if(container.dataset.hasTabs) {
iterations = container.dataset.iterations;
}
$.ajax({
type: "POST",
url: `/backend/guue/add2GUUE.php?codice=${eForms2.codice}&iterations=${iterations}`,
data: {data: data},
dataType: "html",
beforeSend: function() {
$("#wait_div").show();
}
})
.fail(function(response) {
swal(js_dict["error-retry"]);
})
.done(function(response) {
$("#" + repeatable_target).append(response);
// Make new elements respond
eForms2.setup();
eForms2.makeInternalLabels();
f_ready();
})
.always(function() {
$("#wait_div").slideUp('fast');
});
},
// Update element after autosave
updateElement : function(el, data, key) {
nodeName = el.nodeName.toLowerCase();
// If forbidden
if(data["forbidden"]) {
// We disable the element
el.setAttribute("disabled", '');
// We remove the rel attribute
if(el.hasAttribute("rel")) {
el.setAttribute("_rel", el.getAttribute("rel"));
}
// We clear its value
// If not forbidden
} else {
// Aggiorniamo le options
if(data["options"] && nodeName == "select") {
// Marchiamo quelle da rimuovere (tutte tranne quelle vuote)
el.querySelectorAll("option").forEach(function(current_option) {
if(current_option.value) {
current_option.dataset.todelete = true;
}
});
// Iteriamo le opzioni attuali
for (const [value, name] of Object.entries(data["options"])) {
// Cerchiamo se c'è una corrispondente
current_option = el.querySelector(`option[value='${value}']`);
// Se c'è, la aggiorniamo e la non marchiamo come da cancellare
if(current_option) {
current_option.innerText = name;
delete current_option.dataset.todelete;
}
// Se non c'è la creiamo
else {
new_option = document.createElement("option");
new_option.value = value;
new_option.innerText = name;
el.appendChild(new_option);
}
}
// Cancelliamo quelle non più presenti
el.querySelectorAll("option[data-todelete=true]").forEach(function(to_delete) {
el.removeChild(to_delete);
});
}
// Inseriamo le assertion
if(data["assert_fail"] && (nodeName == "input" || nodeName == "select")) {
input = $(el);
input.addClass('is-invalid');
input.parents(".form-group").first().addClass("text-danger");
noteid = "note_" + key;
const note = document.getElementById(noteid);
if(note)
note.innerHTML = data["assert_fail"];
else
input.parents(".form-group").first().append("<div class=\"invalid-feedback\" id=\"" + noteid + "\">" + data["assert_fail"] + "</div>");
}
if(el.hasAttribute("_rel")) {
el.setAttribute("rel", "_rel");
el.removeAttribute("_rel");
}
el.removeAttribute("disabled");
// Update data attributes
for(let key in data.attrs) {
if(key.toLowerCase() != "class")
el.setAttribute(key, data.attrs[key]);
}
// Push data on readonly fields (autopopulate LOT/ORG/GLO ecc)
if(el.getAttribute("readonly") && !el.dataset.dummy) {
let val = data.val
if(typeof val === 'object') {
if(el.name && el.name.includes("[type]")) {
val = val.type;
} else {
val = val.value;
}
}
el.value = val;
}
// Update important rel parts
if(el.getAttribute("rel") && data.rel) {
let current_rel = el.getAttribute("rel").split(";");
current_rel[2] = data.rel[2];
current_rel[0] = data.rel[0];
el.setAttribute("rel", current_rel.join(";"));
if(data.rel[0] == "S") {
el.setAttribute("required", "1");
} else {
el.removeAttribute("required");
}
}
// Update label
if(el.nodeName.toLowerCase() == "label" && data.rel) {
const asterisk = el.querySelector(".text-asterisk");
if(data.rel[0] == "S") {
if(!asterisk) {
el.innerHTML += " <?= TedEsender::MANDATORY_ASTERISK ?>";
}
} else {
if(asterisk) {
asterisk.remove();
}
}
}
}
$(el).trigger("chosen:updated");
},
//scrollToSection
scrollToSection : function(id) {
document.querySelector(`#${id}`).scrollIntoView({block: 'start', behavior: 'smooth'})
},
guueFixedObserver : null,
guueNaviObserver : null,
navigationObservers : [],
//makeNavigator
makeNavigator: function() {
// Inizializziamo il toggle per mostrare o meno la barra di navigazione piccola
if(eForms2.guueNaviObserver == null) {
const bigNav = document.getElementById("guue-big-nav");
const smallNav = document.getElementById("guue-small-nav");
eForms2.guueNaviObserver = new IntersectionObserver(function(entries) {
entries.forEach((entry) => {
if(entry.isIntersecting) {
smallNav.style.height = "0";
smallNav.style.opacity = "0";
} else {
smallNav.style.display = "flex";
smallNav.style.height = "40px";
smallNav.style.opacity = "1";
}
});
});
eForms2.guueNaviObserver.observe(bigNav);
}
// Inizializziamo il toggle per mostrare o meno la barra di navigazione piccola
if(eForms2.guueFixedObserver == null) {
const commands = document.getElementById("guue-commands");
const metadata = document.getElementById("form-metadata");
eForms2.guueFixedObserver = new IntersectionObserver(function(entries) {
entries.forEach((entry) => {
if(entry.isIntersecting) {
commands.classList.remove("fixed");
} else {
commands.classList.add("fixed");
}
});
}, {'threshold': 0.25 });
eForms2.guueFixedObserver.observe(metadata);
}
// Cancelliamo i vecchi
eForms2.navigationObservers.forEach(function(observer) {
observer.disconnect();
});
eForms2.navigationObservers = [];
document.querySelectorAll(".guue-internal-navigation").forEach(function(oldLink) {
oldLink.remove();
})
const firstLevelHeaders = document.querySelectorAll("#master-fieldset > .section-header");
const activeButton = document.querySelector("#guue-sections > .list-group > .active");
let links = [];
firstLevelHeaders.forEach(function(element){
if (!element.classList.contains("d-none")) {
const headerContent = element.querySelector(".col-12 > .card > .card-header .row > .col > p");
let linkContainer = document.createElement("button");
linkContainer.classList.add("list-group-item");
linkContainer.classList.add("guue-internal-navigation");
linkContainer.classList.add("text-left");
linkContainer.onclick = function(e) { window.eForms2.scrollToSection(element.id); e.preventDefault(); };
let link = document.createElement("span");
link.classList.add("ml-4");
// Creiamo un observer
let observer = new IntersectionObserver(function(entries) {
entries.forEach((entry) => {
if(entry.isIntersecting) {
linkContainer.classList.add("active");
} else {
linkContainer.classList.remove("active");
}
});
}, {'threshold': element.clientHeight > 1000 ? 0.05 : 0.15 });
observer.observe(element);
eForms2.navigationObservers.push(observer);
link.innerText = headerContent.textContent;
linkContainer.append(link)
links = [linkContainer].concat(links);
}
})
links.forEach(function(link) {
activeButton.after(link);
})
}
}
}
</script>
<?
}
public function getSectionByFieldID(array $section, string $id = "", array $return_section = [], bool $first_run = true) {
// Iteriamo i contenuti
foreach(($section["content"] ?? []) as $subsection) {
// Se troviamo una macrosezione, popoliamo la variabile
if($first_run && str_starts_with($subsection["id"], "GR-")) {
$return_section = $subsection;
}
$subsection_id = $subsection["id"] ?? null;
// Se corrisponde alla ricerca lo ritorniamo
if($subsection_id === $id ) {
// Conserviamo anche un riferimento ai metadata dell'elemento
$return_section["field"] = $subsection;
return $return_section;
}
// Se corrisponde il BTID invece
$is_bt = substr_count($id, "-") === 1;
if($is_bt && str_starts_with($subsection_id, $id)) {
// Conserviamo anche un riferimento ai metadata dell'elemento
$return_section["field"] = $subsection;
return $return_section;
}
// Altrimenti cerchiamo a fondo
$depth_search = $this->getSectionByFieldID($subsection, $id, $return_section, false);
if($depth_search !== null)
return $depth_search;
}
return null;
}
public function processErrorTagForField($field, $error) {
ob_start();
// Prendiamo la sezione alla quale appartiene
$section = $this->getSectionByFieldID($this->fields, $field);
if($section !== null) {
$repetition = 0;
// Se la sezione è ripetibile, arriviamo alla ripetizione tramite un workaround
if($section["_repeatable"] ?? false) {
$location = $error["@location"] ?? null;
if($location) {
// Troviamo la prima quadra della ripetizione visto che è sempre una macrosezione
$repetition_candidate = substr($location, strpos($location, "[") + 1 );
// E prendiamo fino alla chiusura della quadra
$repetition_candidate = substr($repetition_candidate, 0, strpos($repetition_candidate, "]") );
if(is_numeric($repetition_candidate)) {
$repetition = (int)$repetition_candidate - 1;
}
}
}
$current_section = $_GET["section"];
$current_repetition = $_GET["subsection_index"];
// Se l'errore è in una sezione diversa dalla nostra
if($current_section !== $section["id"] || $repetition != $current_repetition ) {
?>
<a onclick="switchSection('<?= $section['id'] ?>', <?= $repetition ?>, true)" href="#">
<span class="badge badge-warning">
<span class="fa fa-sm fa-link"></span>
<span><?= $this->translate($section['_label'])?> <?= $repetition > 0 ? $repetition + 1 : ""; ?> > <?= $field ?></span>
</span>
</a>
<?
} else {
// Se abbiamo un riferimento completo al campo
$section_field = $section["field"] ?? null;
if($section_field) {
?>
<a onclick="scrollToField('<?= $section_field['id'] ?>')" href="#">
<span class="badge badge-info" href="#">
<span class="fa fa-sm fa-search"></span>
<b><?= $field ?></b>
</span>
</a>
<?
} else {
return $field;
}
}
} else {
return $field;
}
return ob_get_clean();
}
public function processErrorForDisplay($error) {
$error["label"] = $error["label"] ?? __guue("Errore sconosciuto");
// Se abbiamo il riferimento ad un field
if($see = ($error["svrl:diagnostic-reference"]["@see"] ?? false)) {
// field:BT-1234-Lot
if(str_starts_with($see, "field:")) {
// Prendiamo il campo
$see = str_replace("field:" , "", $see);
$processed_field = $this->processErrorTagForField($see, $error);
if($processed_field != $see) {
echo $processed_field;
}
}
}
// Processiamo i riferimenti ai campi nel label
$matches = [];
preg_match_all('/(BT|OPT|OPP)-[\d]+(\((BT|OPT|OPP)-[\d]+\))*-+[a-zA-Z0-9]+/', $error["label"], $matches);
$matches = array_unique($matches[0]) ?? [];
if(preg_match("/\((BT-19[5678])\)/i", $error["label"], $unpublishField) === 1) {
$labelAddition = __("Il campo {$unpublishField[1]} si riferisce alle sezioni 'Pubblicato successivamente'");
$error["label"] .= "<br>Info : <strong>$labelAddition</strong>";
}
foreach($matches as $match) {
$replaced = $this->processErrorTagForField($match, $error);
if($replaced != $match) {
$error["label"] = str_replace($match, $replaced, $error["label"]);
}
}
echo($error["label"]);
if($this->advanced_debug) {
unset($error["label"]);
echo("<pre>");
print_r($error);
echo("</pre>");
}
}
public function renderIntegrationHelpers(string $position) {
$integrationHelperData = $this->integrationHelperData[$this->current_section_id][$this->current_subsection] ?? [];
// Print integration data
foreach($integrationHelperData as $integration) {
if($integration["position"] !== $position) continue;
// Cerchiamo il modulo di integrazione corretto
global $beRoot;
// Proviamo prima il path specifico
$path = $beRoot."{$integration["modulo"]}/guue/render/{$integration["sottomodulo"]}.php";
// Path generico
if(!file_exists($path)) {
$path = $beRoot."guue/integration/{$integration["sottomodulo"]}.php";
}
if (file_exists($path)) {
$unique_identifier = "integration_{$integration['modulo']}_{$integration['sottomodulo']}";
$collapse_button = $this->button(
[
"name" => "button",
"required" => false,
"size" => "pr-2",
"type" => "button",
"title" => "<span class=\"fa fa-sm fa-window-minimize\"></span><span class=\"fa fa-sm fa-plus\"></span>",
"attrs" => [
"data-toggle" => "collapse",
"data-target" => "#collapse-{$unique_identifier}",
"class" => "btn btn-sm btn-block btn-outline-secondary mr-2 guue-collapse-button",
]
]);
$this->printIntegrationSectionHeader($integration["title"], $unique_identifier, $collapse_button, $position);
// Wrappo in una funzione perchè PHP è una cacca e non capisce il concetto di scope
// delle variabili
$callIntegration = function ($path) use ($integration) {
// Se l'array è una lista, renderizziamo l'helper per ogni entry
require($path);
};
$callIntegration($path);
$this->printIntegrationSectionFooter();
}
}
}
}