Compare commits

...

8 Commits

Author SHA1 Message Date
Dasc3er bd6889be4a Revisione JS datatables per inclusione footer in esportazione 2021-07-27 16:17:32 +02:00
Dasc3er 8a8b1479d0 Merge branch 'oauth2' 2021-07-27 14:57:33 +02:00
Dasc3er 4a93422974 Correzioni di stile 2021-07-27 14:55:59 +02:00
loviuz 3fbd42e0e4 Fix per css personalizzato 2021-07-27 14:55:20 +02:00
Dasc3er 43203e5385 Miglioramento supporto OAuth2 2021-07-27 14:49:18 +02:00
loviuz 6b3f05b666 Aggiunta esportazione fatture FE passive in PDF 2021-07-27 14:44:45 +02:00
Dasc3er dfb5734d9b Correzioni JS e stile del codice
Correzioni JS sulla visualizzazione della barra laterale dei plugin e sulla stampa automatica delle tabelle.
2021-07-27 11:37:04 +02:00
Dasc3er 5135a2c04c Introduzione procedura di autenticazione OAuth2 per account email 2021-07-27 10:16:10 +02:00
22 changed files with 785 additions and 330 deletions

View File

@ -1033,13 +1033,13 @@ div.tip {
margin-right: 230px;
}
.control-sidebar-shown {
.control-sidebar-open {
right: 0;
display: block !important;
}
.control-sidebar.control-sidebar-shown,
.control-sidebar.control-sidebar-shown + .control-sidebar-bg {
.control-sidebar.control-sidebar-open,
.control-sidebar.control-sidebar-open + .control-sidebar-bg {
right: 0;
}

View File

@ -32,11 +32,11 @@ $(document).ready(function () {
$(this).find('ul').stop().slideUp();
});
$menulist = $('.treeview-menu > li.active');
for (i = 0; i < $menulist.length; i++) {
$list = $($menulist[i]);
$list.parent().show().parent().addClass('active');
$list.parent().parent().find('i.fa-angle-left').removeClass('fa-angle-left').addClass('fa-angle-down');
const elenco_menu = $('.treeview-menu > li.active');
for (i = 0; i < elenco_menu.length; i++) {
const elemento = $(elenco_menu[i]);
elemento.parent().show().parent().addClass('active');
elemento.parent().parent().find('i.fa-angle-left').removeClass('fa-angle-left').addClass('fa-angle-down');
}
// Menu ordinabile
@ -46,7 +46,7 @@ $(document).ready(function () {
cursor: "move",
dropOnEmpty: true,
scroll: true,
})[0].addEventListener("sortupdate", function(e) {
})[0].addEventListener("sortupdate", function (e) {
let order = $(".sidebar-menu > .treeview[data-id]").toArray().map(a => $(a).data("id"))
$.post(globals.rootdir + "/actions.php", {
@ -57,19 +57,52 @@ $(document).ready(function () {
});
}
$(".sidebar-toggle").click(function () {
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
}, 350);
});
// Mostra/nasconde sidebar sx
// Mostra/nasconde sidebar del menu principale
$(".sidebar-toggle").on("click", function () {
if ($("body").hasClass("sidebar-collapse")) {
session_set("settings,sidebar-collapse", 0, 1, 0);
} else {
session_set("settings,sidebar-collapse", 1, 0, 0);
}
setTimeout(function () {
window.dispatchEvent(new Event('resize'));
}, 350);
});
// Barra plugin laterale
const pluginToggle = $(".control-sidebar-toggle");
const largeScreen = screen.width > 1200;
// Gestione click sul pulsante per il toggle
pluginToggle.on("click", function () {
$("aside.content-wrapper, .main-footer").toggleClass("with-control-sidebar");
toggleControlSidebar();
});
// Gestione click sulla sidebar per evitare chiusura
$(".control-sidebar").on("click", function (e) {
if (largeScreen && e.target.tagName !== 'H4' && $(".main-footer").hasClass("with-control-sidebar")) {
toggleControlSidebar();
}
});
// Barra plugin laterale disabilitata per schermi piccoli
if (largeScreen && !globals.collapse_plugin_sidebar) {
pluginToggle.click();
}
});
/**
* Funzione dedicata alla gestione del toggle della sidebar.
*/
function toggleControlSidebar() {
const sidebar = $(".control-sidebar");
sidebar.toggleClass("control-sidebar-open");
if (sidebar.hasClass("control-sidebar-open")) {
sidebar.show();
}
}

View File

@ -38,34 +38,32 @@ function start_datatables() {
start_local_datatables();
$('.main-records').each(function () {
var $this = $(this);
const $this = $(this);
// Controlla che la tabella non sia già inizializzata
if (!$.fn.DataTable.isDataTable('#' + $this.attr('id'))) {
var id_module = $this.data('idmodule');
var id_plugin = $this.data('idplugin');
var id_parent = $this.data('idparent');
const id_module = $this.data('idmodule');
const id_plugin = $this.data('idplugin');
const id_parent = $this.data('idparent');
// Parametri di ricerca da url o sessione
var search = getTableSearch();
const search = getTableSearch();
var column_search = [];
const column_search = [];
$this.find("th").each(function () {
var id = $(this).attr('id').replace("th_", "");
var single_value = search["search_" + id] ? search["search_" + id] : "";
const id = $(this).attr('id').replace("th_", "");
const single_value = search["search_" + id] ? search["search_" + id] : "";
column_search.push({
"sSearch": single_value,
});
});
var tempo_attesa_ricerche = (globals.tempo_attesa_ricerche * 1000);
$this.on('preInit.dt', function (ev, settings) {
$('#mini-loader').show();
});
var table = $this.DataTable({
const table = $this.DataTable({
language: globals.translations.datatables,
autoWidth: true,
dom: "ti",
@ -97,9 +95,100 @@ function start_datatables() {
style: 'multi',
selector: 'td:first-child'
},
buttons: [
buttons: getDatatablesButtons($this),
scroller: {
loadingIndicator: true,
displayBuffer: globals.dataload_page_buffer,
},
ajax: {
url: "ajax_dataload.php?id_module=" + id_module + "&id_plugin=" + id_plugin + "&id_parent=" + id_parent,
type: 'GET',
dataSrc: "data",
},
initComplete: initComplete,
drawCallback: drawCallback,
footerCallback: footerCallback,
});
table.on('processing.dt', function (e, settings, processing) {
if (processing) {
$('#mini-loader').show();
} else {
$('#mini-loader').hide();
}
})
}
});
}
/**
* Funzione per evitare il sorting al click della colonna.
* Utilizzata per evitare il sorting nelle ricerche.
* @param {*} e
*/
function stopTableSorting(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
/**
* Funzione per resettare il campo di ricerca in una specifica colonna.
* @param {string} type
*/
function resetTableSearch(type) {
if (type == null) $('[id^=th_] input').val('').trigger('keyup');
else $('[id^=th_' + type + '] input').val('').trigger('keyup');
}
/**
* Sostituisce i caratteri speciali per la ricerca attraverso le tabelle Datatables.
*
* @param {string} field
* @return string
*/
function searchFieldName(field) {
return field.replace(' ', '-').replace('.', '');
}
/**
* Salva nella sessione la ricerca per le tabelle Datatables.
*
* @param {int} module_id
* @param {string} field
* @param {mixed} value
*/
function searchTable(module_id, field, value) {
session_set('module_' + module_id + ',' + 'search_' + searchFieldName(field), value, 0);
}
/**
* Restituisce i valori di ricerca impostati nell'URL della pagina.
* @returns {{}}
*/
function getTableSearch() {
// Parametri di ricerca da url o sessione
const search = getUrlVars();
globals.search.forEach(function (value, index, array) {
if (search[array[index]] === undefined) {
search[array[index]] = array[value];
}
});
return search;
}
/**
* Restituisce i pulsanti da generare per la tabella Datatables.
* @returns
*/
function getDatatablesButtons(table) {
return [
// Pulsante di esportazione CSV
{
extend: 'csv',
footer: true,
fieldSeparator: ";",
exportOptions: {
modifier: {
@ -115,30 +204,36 @@ function start_datatables() {
}
}
},
// Pulsante di esportazione tramite copia
{
extend: 'copy',
footer: true,
exportOptions: {
modifier: {
selected: true
}
}
},
// Pulsante di esportazione via stampa della tabella
{
extend: 'print',
autoPrint: true,
footer: false, // Non funzionante in Firefox, e saltuarmente in Chrome
customize: function (win) {
$(win.document.body)
.css('font-size', '10pt')
.append(
'<table class="main-records table table-condensed table-bordered dataTable"><tfoot><tr><td></td><td class="pull-right">' + $('#summable').text() + '</td><td></td></tr></tfoot></table>'
);
$(win.document.body).find('table')
const datatable = getTable(table).datatable;
const footer = datatable.table().footer().children[0];
console.log(footer);
const body = $(win.document.body);
body.find('table')
.addClass('compact')
.css('font-size', 'inherit');
$(win.document.body).find('td:first-child')
.addClass('hide');
$(win.document.body).find('th:first-child')
.css('font-size', 'inherit')
.append(footer);
body.find('td:first-child, th:first-child')
.addClass('hide');
},
exportOptions: {
modifier: {
@ -146,8 +241,10 @@ function start_datatables() {
}
}
},
// Pulsante di esportazione in formato Excel
{
extend: 'excel',
footer: true,
exportOptions: {
modifier: {
selected: true
@ -163,37 +260,32 @@ function start_datatables() {
}
}
},
// Pulsante di esportazione in formato PDF
{
extend: 'pdf',
footer: true,
exportOptions: {
modifier: {
selected: true
}
}
},
],
scroller: {
loadingIndicator: true,
displayBuffer: globals.dataload_page_buffer,
},
ajax: {
url: "ajax_dataload.php?id_module=" + id_module + "&id_plugin=" + id_plugin + "&id_parent=" + id_parent,
type: 'GET',
dataSrc: "data",
},
initComplete: function (settings) {
var api = this.api();
var search = getTableSearch();
];
}
function initComplete(settings) {
const api = this.api();
const search = getTableSearch();
api.columns('.search').every(function () {
var column = this;
const column = this;
// Valore predefinito della ricerca
var tempo;
var header = $(column.header());
var name = header.attr('id').replace('th_', '');
let tempo;
const header = $(column.header());
const name = header.attr('id').replace('th_', '');
var value = search['search_' + name] ? search['search_' + name] : '';
const value = search['search_' + name] ? search['search_' + name] : '';
$('<br><input type="text" style="width:100%" class="form-control' + (value ? ' input-searching' : '') + '" placeholder="' + globals.translations.filter + '..." value="' + value.replace(/"/g, '&quot;') + '"><i class="deleteicon fa fa-times fa-2x' + (value ? '' : ' hide') + '"></i>')
.appendTo(column.header())
@ -221,12 +313,14 @@ function start_datatables() {
}
// Impostazione delle sessioni per le ricerche del modulo e del campo specificati
var module_id = $this.data('idmodule'); //+ "-" + $this.data('idplugin');
var field = $(this).parent().attr('id').replace('th_', '');
var value = $(this).val();
const module_id = $this.data('idmodule'); //+ "-" + $this.data('idplugin');
const field = $(this).parent().attr('id').replace('th_', '');
const value = $(this).val();
if (e.keyCode == 13 || $(this).val() == '') {
start_search(module_id, field, value);
} else {
const tempo_attesa_ricerche = (globals.tempo_attesa_ricerche * 1000);
tempo = window.setTimeout(start_search, tempo_attesa_ricerche, module_id, field, value);
}
});
@ -245,9 +339,11 @@ function start_datatables() {
$('.deleteicon').on("click", function (e) {
resetTableSearch($(this).parent().attr("id").replace("th_", ""));
});
},
drawCallback: function (settings) {
var api = new $.fn.dataTable.Api(settings);
}
function drawCallback(settings) {
const table = getTable(settings.nTable);
const datatable = table.datatable;
$(".dataTables_sizing .deleteicon").addClass('hide');
@ -260,7 +356,7 @@ function start_datatables() {
});
$("[data-link]").each(function () {
var $link = $(this);
const $link = $(this);
$(this).parent().not('.bound').addClass('bound').click(function (event) {
if ($link.data('type') === 'dialog') {
launch_modal(globals.translations.details, $link.data('link'));
@ -272,18 +368,19 @@ function start_datatables() {
});
// Reimposto il flag sulle righe ricaricate selezionate in precedenza
var selected = $this.data('selected') ? $this.data('selected').split(';') : [];
table.rows().every(function (rowIdx) {
if ($.inArray(this.id().toString(), selected) !== -1) {
table.row(':eq(' + rowIdx + ')', {
const selected = table.getSelectedRows();
datatable.rows().every(function (rowIdx) {
if (selected.includes(this.id())) {
datatable.row(':eq(' + rowIdx + ')', {
page: 'current'
}).select();
}
});
},
footerCallback: function (row, data, start, end, display) {
var i = -1;
var json = this.api().ajax.json();
}
function footerCallback(row, data, start, end, display) {
let i = -1;
const json = this.api().ajax.json();
this.api().columns().every(function () {
if (json.summable[i] !== undefined) {
@ -294,68 +391,6 @@ function start_datatables() {
i++;
});
}
});
table.on('processing.dt', function (e, settings, processing) {
if (processing) {
$('#mini-loader').show();
} else {
$('#mini-loader').hide();
}
})
}
});
}
function stopTableSorting(e) {
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
function resetTableSearch(type) {
if (type == null) $('[id^=th_] input').val('').trigger('keyup');
else $('[id^=th_' + type + '] input').val('').trigger('keyup');
}
function reset(type) {
return resetTableSearch(type);
}
/**
* Sostituisce i caratteri speciali per la ricerca attraverso le tabelle Datatables.
*
* @param string field
*
* @return string
*/
function searchFieldName(field) {
return field.replace(' ', '-').replace('.', '');
}
/**
* Salva nella sessione la ricerca per le tabelle Datatables.
*
* @param int module_id
* @param string field
* @param mixed value
*/
function searchTable(module_id, field, value) {
session_set('module_' + module_id + ',' + 'search_' + searchFieldName(field), value, 0);
}
function getTableSearch() {
// Parametri di ricerca da url o sessione
var search = getUrlVars();
globals.search.forEach(function (value, index, array) {
if (search[array[index]] === undefined) {
search[array[index]] = array[value];
}
});
return search;
}
/**
@ -424,13 +459,13 @@ function getTable(selector) {
// Aggiornamento del footer nel caso sia richiesto
if (globals.restrict_summables_to_selected) {
this.updateSelectedFooter();
this.updateFooterForSelectedRows();
}
},
addSelectedRows: function (row_ids) {
row_ids = Array.isArray(row_ids) ? row_ids : [row_ids];
row_ids.forEach(function (item, index) {
selected.set(item, true);
selected.set(item.toString(), true);
});
this.saveSelectedRows();
@ -438,7 +473,7 @@ function getTable(selector) {
removeSelectedRows: function (row_ids) {
row_ids = Array.isArray(row_ids) ? row_ids : [row_ids];
row_ids.forEach(function (item, index) {
selected.delete(item);
selected.delete(item.toString());
});
this.saveSelectedRows();
@ -448,12 +483,14 @@ function getTable(selector) {
this.saveSelectedRows();
},
// Aggiornamento dei campi summable
updateSelectedFooter: function () {
let datatable = this.datatable;
/**
* Nuovi valori dei campi summable
* @returns
*/
getSelectedRowsFooter: function () {
let ids = this.getSelectedRows();
$.ajax({
return $.ajax({
url: globals.rootdir + "/ajax.php",
type: "POST",
dataType: "json",
@ -462,8 +499,17 @@ function getTable(selector) {
id_plugin: this.id_plugin,
op: "summable-results",
ids: ids,
}
});
},
success: function (response) {
/**
* Aggiornamento dei campi summable
*/
updateFooterForSelectedRows: function () {
let datatable = this.datatable;
this.getSelectedRowsFooter().then(function (response) {
for (let [column, value] of Object.entries(response)) {
let index = parseInt(column) + 1;
let sel = datatable.column(index).footer();
@ -471,7 +517,6 @@ function getTable(selector) {
.attr("id", "summable")
.html(value);
}
}
});
},
};

View File

@ -39,6 +39,8 @@
"illuminate/database": "~5.4.0",
"intervention/image": "^2.3",
"league/csv": "^8.2",
"league/oauth2-client": "^2.6",
"league/oauth2-google": "^3.0",
"maximebf/debugbar": "^1.15",
"monolog/monolog": "^1.22",
"mpdf/mpdf": "^v8.0.7",
@ -55,6 +57,7 @@
"symfony/polyfill-php70": "^1.8",
"symfony/translation": "^3.3",
"symfony/var-dumper": "^3.3",
"thenetworg/oauth2-azure": "^2.0",
"willdurand/geocoder": "^3.3"
},
"require-dev": {

View File

@ -55,7 +55,7 @@ $rootdir = ROOTDIR;
$baseurl = BASEURL;
// Sicurezza della sessioni
ini_set('session.cookie_samesite', 'strict');
//ini_set('session.cookie_samesite', 'strict');
ini_set('session.use_trans_sid', '0');
ini_set('session.use_only_cookies', '1');

View File

@ -119,30 +119,11 @@ if (empty($record) || !$has_access) {
</a>
</li>';
$hide_left_sidebar = Auth::check() && (setting('Nascondere la barra dei plugin di default'));
echo '
<li class="control-sidebar-toggle">
<a data-toggle="control-sidebar" style="cursor: pointer">'.tr('Plugin').'</a>
<a style="cursor: pointer">'.tr('Plugin').'</a>
</li>
<script>
$( document ).ready(function() {';
if (!empty($hide_left_sidebar)) {
echo ' $(".control-sidebar").removeClass("control-sidebar-shown"); $("aside.content-wrapper, .main-footer").toggleClass("with-control-sidebar");';
}
echo '
$(".control-sidebar-toggle").bind("click", function() {
$("aside.content-wrapper, .main-footer").toggleClass("with-control-sidebar");
$(".control-sidebar").toggleClass("control-sidebar-shown");
});
});
</script>
</ul>
<div class="tab-content">

View File

@ -26,7 +26,7 @@ if (Auth::check()) {
</section><!-- /.content -->
</aside><!-- /.content-wrapper -->
<footer class="main-footer '.(string_contains($_SERVER['SCRIPT_FILENAME'], 'editor.php') ? 'with-control-sidebar' : '').'">
<footer class="main-footer">
<a class="hidden-xs" href="'.tr('https://www.openstamanager.com').'" title="'.tr("Il gestionale open source per l'assistenza tecnica e la fatturazione").'." target="_blank"><strong>'.tr('OpenSTAManager').'</strong></a>
<span class="pull-right hidden-xs">
<strong>'.tr('Versione').'</strong> '.$version.'
@ -59,7 +59,7 @@ if (Auth::check()) {
</script>';
}
$custom_css = setting('CSS Personalizzato');
$custom_css = html_entity_decode( setting('CSS Personalizzato') );
if (!empty($custom_css)) {
echo '
<style>'.$custom_css.'</style>';

View File

@ -115,7 +115,7 @@ if (Auth::check()) {
'hookSingle' => tr('Hai 1 notifica'),
'hookNone' => tr('Nessuna notifica'),
'singleCalendar' => tr("E' presente un solo periodo!"),
'noResults' => tr("Nessun elemento trovato"),
'noResults' => tr('Nessun elemento trovato'),
];
foreach ($translations as $key => $value) {
echo '
@ -123,7 +123,7 @@ if (Auth::check()) {
}
echo '
allegati: {
messaggio: "'.tr("Clicca o trascina qui per caricare uno o più file").'",
messaggio: "'.tr('Clicca o trascina qui per caricare uno o più file').'",
maxFilesize: "'.tr('Max upload: _SIZE_ MB').'",
errore: "'.tr('Errore').'",
modifica: "'.tr('Modifica allegato').'",
@ -206,6 +206,8 @@ if (Auth::check()) {
end_date: "'.$_SESSION['period_end'].'",
end_date_formatted: "'.Translator::dateToLocale($_SESSION['period_end']).'",
collapse_plugin_sidebar: '.intval(setting('Nascondere la barra dei plugin di default')).',
ckeditorToolbar: [
["Undo","Redo","-","Cut","Copy","Paste","PasteText","PasteFromWord","-","SpellChecker", "Scayt", "-","Link","Unlink","-","Bold","Italic","Underline","Superscript","SpecialChar","HorizontalRule","-","JustifyLeft","JustifyCenter","JustifyRight","JustifyBlock","-","NumberedList","BulletedList","Outdent","Indent","Blockquote","-","Styles","Format","Image","Table", "TextColor", "BGColor" ],
],
@ -405,23 +407,23 @@ if (Auth::check()) {
</ul>
</li>
<li><a href="#" onclick="window.print()" class="tip nav-button" title="'.tr('Stampa').'">
<li class="nav-button"><a href="#" onclick="window.print()" class="tip nav-button" title="'.tr('Stampa').'">
<i class="fa fa-print"></i>
</a></li>
<li><a href="'.base_path().'/bug.php" class="tip nav-button" title="'.tr('Segnalazione bug').'">
<li class="nav-button"><a href="'.base_path().'/bug.php" class="tip nav-button" title="'.tr('Segnalazione bug').'">
<i class="fa fa-bug"></i>
</a></li>
<li><a href="'.base_path().'/log.php" class="tip nav-button" title="'.tr('Log accessi').'">
<li class="nav-button"><a href="'.base_path().'/log.php" class="tip nav-button" title="'.tr('Log accessi').'">
<i class="fa fa-book"></i>
</a></li>
<li><a href="'.base_path().'/info.php" class="tip nav-button" title="'.tr('Informazioni').'">
<li class="nav-button"><a href="'.base_path().'/info.php" class="tip nav-button" title="'.tr('Informazioni').'">
<i class="fa fa-info"></i>
</a></li>
<li><a href="'.base_path().'/index.php?op=logout" onclick="sessionStorage.clear()" class="bg-red tip" title="'.tr('Esci').'">
<li class="nav-button"><a href="'.base_path().'/index.php?op=logout" onclick="sessionStorage.clear()" class="bg-red tip" title="'.tr('Esci').'">
<i class="fa fa-power-off"></i>
</a></li>
</ul>
@ -482,7 +484,7 @@ if (Auth::check()) {
if (string_contains($_SERVER['SCRIPT_FILENAME'], 'editor.php')) {
// Menu laterale per la visualizzazione dei plugin
echo '
<aside class="control-sidebar control-sidebar-light control-sidebar-shown">
<aside class="control-sidebar control-sidebar-light">
<h4 class="text-center">'.tr('Plugin disponibili').'</h4>
<ul class="nav nav-tabs nav-pills nav-stacked">
<li data-toggle="control-sidebar" class="active">
@ -542,7 +544,7 @@ if (Auth::check()) {
echo '
<!-- Right side column. Contains the navbar and content of the page -->
<aside class="content-wrapper '.(string_contains($_SERVER['SCRIPT_FILENAME'], 'editor.php') ? 'with-control-sidebar' : '').'">
<aside class="content-wrapper">
<!-- Main content -->
<section class="content">

View File

@ -159,7 +159,7 @@ echo '
<script>
var emails = [];
var id_anagrafica = "'.$id_anagrafica.'";
var pec = "'.$smtp['pec']. '";
var pec = "'.$smtp['pec'].'";
$(document).ready(function() {
// Auto-completamento destinatario

View File

@ -34,6 +34,9 @@ class Account extends Model
protected $table = 'em_accounts';
/** @var OAuth2 */
protected $gestoreOAuth2;
public function testConnection()
{
// Impostazione di connected_at a NULL
@ -54,6 +57,17 @@ class Account extends Model
return $result;
}
public function getGestoreOAuth2()
{
if (isset($this->gestoreOAuth2)) {
return $this->gestoreOAuth2;
}
$this->gestoreOAuth2 = new OAuth2($this);
return $this->gestoreOAuth2;
}
/* Relazioni Eloquent */
public function templates()

View File

@ -0,0 +1,198 @@
<?php
namespace Modules\Emails;
use InvalidArgumentException;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\Google;
use League\OAuth2\Client\Token\AccessToken;
use TheNetworg\OAuth2\Client\Provider\Azure;
class OAuth2
{
public static $providers = [
'microsoft' => [
'name' => 'Microsoft',
'class' => Azure::class,
'options' => [
'scope' => [
'offline_access',
'https://graph.microsoft.com/SMTP.Send',
//'https://outlook.office.com/IMAP.AccessAsUser.All'
],
],
'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#microsoft',
],
'google' => [
'name' => 'Google',
'class' => Google::class,
'options' => [
'scope' => ['https://mail.google.com/'],
'accessType' => 'offline',
],
'help' => 'https://docs.openstamanager.com/faq/configurazione-oauth2#google',
],
];
protected $provider;
protected $account;
public function __construct(Account $account)
{
$this->account = $account;
$this->init();
}
/**
* Inizializza il provider per l'autenticazione OAuth2.
*/
public function init()
{
$redirect_uri = base_url().'/oauth2.php';
$class = $this->getProviderConfiguration()['class'];
// Authorization
$this->provider = new $class([
'clientId' => $this->account->client_id,
'clientSecret' => $this->account->client_secret,
'redirectUri' => $redirect_uri,
'accessType' => 'offline',
]);
// Configurazioni specifiche per il provider di Microsoft Azure
if ($this->provider instanceof Azure) {
$this->provider->defaultEndPointVersion = Azure::ENDPOINT_VERSION_2_0;
$this->provider->tenant = 'consumers';
}
}
public function getProvider()
{
return $this->provider;
}
public function getProviderConfiguration()
{
return self::$providers[$this->account->provider];
}
public function needsConfiguration()
{
$access_token = $this->getAccessToken();
return empty($access_token);
}
/**
* Gestisce le operazioni di configurazione per l'autenticazione OAuth2.
* Restituisce l'URL di redirect per le operazioni di aggiornamento dei dati, lancia un eccezione in caso di errori e restituisce null in caso di completamento della configurazione.
*
* Nota: l'autenticazione OAuth2 richiede una serie di richieste su una singola pagina
* - Richiesta di autenticazione al server remoto (code, state vuoti)
* - Conferma di autenticazione alla pagina di redirect (code, state impostati)
* - Richiesta del token di accesso dalla pagina di redirect al server remoto
*
* @param string|null $code
* @param string|null $state
*
* @throws IdentityProviderException
* @throws InvalidArgumentException
*
* @return string|null
*/
public function configure($code, $state)
{
if (!$this->needsConfiguration()) {
return null;
}
$provider = $this->getProvider();
$options = $this->getProviderConfiguration()['options'];
if (empty($code)) {
// Fetch the authorization URL from the provider; this returns the
// urlAuthorize option and generates and applies any necessary parameters
// (e.g. state).
$authorization_url = $provider->getAuthorizationUrl($options);
// Get the state generated for you and store it to the session.
$this->account->oauth2_state = $provider->getState();
$this->account->save();
// Redirect the user to the authorization URL.
return $authorization_url;
} elseif (!empty($this->account->oauth2_state) && $this->account->oauth2_state !== $state) {
$this->account->oauth2_state = null;
$this->account->save();
throw new InvalidArgumentException();
} else {
$this->account->oauth2_state = null;
$this->account->save();
// Try to get an access token using the authorization code grant
$access_token = $provider->getAccessToken('authorization_code', [
'code' => $code,
]);
$refresh_token = $access_token->getRefreshToken();
$this->updateTokens($access_token, $refresh_token);
}
return null;
}
public function getRefreshToken()
{
$this->checkTokens();
return $this->account->refresh_token;
}
/**
* Restituisce l'access token per l'autenticazione OAuth2.
*
* @return AccessToken|null
*/
public function getAccessToken()
{
$this->checkTokens();
return unserialize($this->account->access_token);
}
/**
* Imposta l'access token per l'autenticazione OAuth2.
*
* @param AccessToken|null
*/
public function updateTokens($access_token, $refresh_token)
{
$this->account->access_token = serialize($access_token);
$this->account->refresh_token = $refresh_token;
$this->account->save();
}
protected function checkTokens()
{
$access_token = unserialize($this->account->access_token);
if (!empty($access_token) && $access_token->hasExpired()) {
// Tentativo di refresh del token di accesso
$refresh_token = $this->account->refresh_token;
if (!empty($refresh_token)) {
$access_token = $this->getProvider()->getAccessToken('refresh_token', [
'refresh_token' => $this->account->refresh_token,
]);
$refresh_token = $access_token->getRefreshToken();
} else {
$access_token = null;
$refresh_token = null;
}
$this->updateTokens($access_token, $refresh_token);
}
}
}

View File

@ -414,6 +414,17 @@ $operations['registrazione-contabile'] = [
],
];
$operations['exportFE-bulk'] = [
'text' => '<span class="'.((!extension_loaded('zip')) ? 'text-muted disabled' : '').'"><i class="fa fa-file-archive-o"></i> '.tr('Esporta stampe FE').'</span>',
'data' => [
'title' => '',
'msg' => tr('Vuoi davvero esportare i PDF delle fatture elettroniche selezionate in un archivio ZIP?'),
'button' => tr('Procedi'),
'class' => 'btn btn-lg btn-warning',
'blank' => true,
],
];
if ($module->name == 'Fatture di vendita') {
$operations['genera-xml'] = [
'text' => '<span><i class="fa fa-file-code-o"></i> '.tr('Genera fatture elettroniche').'</span>',
@ -437,17 +448,6 @@ if ($module->name == 'Fatture di vendita') {
],
];
$operations['exportFE-bulk'] = [
'text' => '<span class="'.((!extension_loaded('zip')) ? 'text-muted disabled' : '').'"><i class="fa fa-file-archive-o"></i> '.tr('Esporta stampe FE').'</span>',
'data' => [
'title' => '',
'msg' => tr('Vuoi davvero esportare i PDF delle fatture elettroniche selezionate in un archivio ZIP?'),
'button' => tr('Procedi'),
'class' => 'btn btn-lg btn-warning',
'blank' => true,
],
];
$operations['check-bulk'] = [
'text' => '<span><i class="fa fa-list-alt"></i> '.tr('Controlla fatture elettroniche').'</span>',
'data' => [

View File

@ -19,7 +19,7 @@
include_once __DIR__.'/../../core.php';
switch (post('op')) {
switch (filter('op')) {
case 'add':
$dbo->insert('em_accounts', [
'name' => post('name'),
@ -39,6 +39,8 @@ switch (post('op')) {
$dbo->query('UPDATE em_accounts SET predefined = 0');
}
$abilita_oauth2 = post('abilita_oauth2');
$dbo->update('em_accounts', [
'name' => post('name'),
'note' => post('note'),
@ -53,10 +55,24 @@ switch (post('op')) {
'timeout' => post('timeout'),
'ssl_no_verify' => post('ssl_no_verify'),
'predefined' => $predefined,
'provider' => post('provider'),
'client_id' => post('client_id'),
'client_secret' => post('client_secret'),
], ['id' => $id_record]);
flash()->info(tr('Informazioni salvate correttamente!'));
// Rimozione informazioni OAuth2 in caso di disabilitazione
if (!$abilita_oauth2) {
$dbo->update('em_accounts', [
'provider' => null,
'client_id' => null,
'client_secret' => null,
'access_token' => null,
'refresh_token' => null,
], ['id' => $id_record]);
}
// Validazione indirizzo email mittente
$check_email = Validate::isValidEmail(post('from_address'));
@ -96,5 +112,11 @@ switch (post('op')) {
flash()->info(tr('Account email eliminato!'));
break;
case 'oauth2':
$redirect = base_path().'/oauth2.php?id_account='.$account->id;
redirect($redirect);
break;
}

View File

@ -17,9 +17,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use Modules\Emails\OAuth2;
include_once __DIR__.'/../../core.php';
?>
echo '
<form action="" method="post" id="edit-form">
<input type="hidden" name="op" value="update">
<input type="hidden" name="backto" value="record-edit">
@ -27,35 +29,35 @@ include_once __DIR__.'/../../core.php';
<!-- DATI -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title"><?php echo tr('Dati'); ?></h3>
<h3 class="panel-title">'.tr('Dati').'</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Nome account'); ?>", "name": "name", "value": "$name$", "required": 1 ]}
{[ "type": "text", "label": "'.tr('Nome account').'", "name": "name", "value": "$name$", "required": 1 ]}
</div>
<div class="col-md-3">
{[ "type": "checkbox", "label": "<?php echo tr('Indirizzo PEC'); ?>", "name": "pec", "value": "$pec$" ]}
{[ "type": "checkbox", "label": "'.tr('Indirizzo PEC').'", "name": "pec", "value": "$pec$" ]}
</div>
<div class="col-md-3">
{[ "type": "checkbox", "label": "<?php echo tr('Indirizzo predefinito'); ?>", "name": "predefined", "value": "$predefined$", "help": "<?php echo tr('Account da utilizzare per l\'invio di tutte le email dal gestionale.'); ?>" ]}
{[ "type": "checkbox", "label": "'.tr('Indirizzo predefinito').'", "name": "predefined", "value": "$predefined$", "help": "'.tr('Account da utilizzare per l\'invio di tutte le email dal gestionale.').'" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Nome visualizzato'); ?>", "name": "from_name", "value": "$from_name$" ]}
{[ "type": "text", "label": "'.tr('Nome visualizzato').'", "name": "from_name", "value": "$from_name$" ]}
</div>
<div class="col-md-3">
{[ "type": "email", "label": "<?php echo tr('Email mittente'); ?>", "name": "from_address", "value": "$from_address$", "required": 1 ]}
{[ "type": "email", "label": "'.tr('Email mittente').'", "name": "from_address", "value": "$from_address$", "required": 1 ]}
</div>
<div class="col-md-3">
{[ "type": "checkbox", "label": "<?php echo tr('Non verificare il certificato SSL'); ?>", "name": "ssl_no_verify", "value": "$ssl_no_verify$" ]}
{[ "type": "checkbox", "label": "'.tr('Non verificare il certificato SSL').'", "name": "ssl_no_verify", "value": "$ssl_no_verify$" ]}
</div>
@ -63,44 +65,119 @@ include_once __DIR__.'/../../core.php';
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Server SMTP'); ?>", "name": "server", "required": 1, "value": "$server$" ]}
{[ "type": "text", "label": "'.tr('Server SMTP').'", "name": "server", "required": 1, "value": "$server$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Porta SMTP'); ?>", "name": "port", "required": 1, "class": "text-center", "decimals":"0", "max-value":"65535", "value": "$port$" ]}
{[ "type": "number", "label": "'.tr('Porta SMTP').'", "name": "port", "required": 1, "class": "text-center", "decimals":"0", "max-value":"65535", "value": "$port$" ]}
</div>
<div class="col-md-3">
{[ "type": "select", "label": "<?php echo tr('Sicurezza SMTP'); ?>", "name": "encryption", "values": "list=\"\": \"<?php echo tr('Nessuna'); ?>\", \"tls\": \"TLS\", \"ssl\": \"SSL\"", "value": "$encryption$" ]}
{[ "type": "select", "label": "'.tr('Sicurezza SMTP').'", "name": "encryption", "values": "list=\"\": \"'.tr('Nessuna').'\", \"tls\": \"TLS\", \"ssl\": \"SSL\"", "value": "$encryption$" ]}
</div>
</div>
<div class="row">
<div class="col-md-6">
{[ "type": "text", "label": "<?php echo tr('Username SMTP'); ?>", "name": "username", "value": "$username$" ]}
{[ "type": "text", "label": "'.tr('Username SMTP').'", "name": "username", "value": "$username$" ]}
</div>
<div class="col-md-3">
{[ "type": "password", "label": "<?php echo tr('Password SMTP'); ?>", "name": "password", "value": "$password$" ]}
{[ "type": "password", "label": "'.tr('Password SMTP').'", "name": "password", "value": "$password$" ]}
</div>
<div class="col-md-3">
{[ "type": "number", "label": "<?php echo tr('Timeout coda di invio (millisecondi)'); ?>", "name": "timeout", "value": "$timeout$", "decimals": 1, "min-value": 100 ]}
{[ "type": "number", "label": "'.tr('Timeout coda di invio (millisecondi)').'", "name": "timeout", "value": "$timeout$", "decimals": 1, "min-value": 100 ]}
</div>
</div>
<div class="row">
<div class="col-md-12">
{[ "type": "textarea", "label": "<?php echo tr('Note'); ?>", "name": "note", "value": "$note$" ]}
{[ "type": "textarea", "label": "'.tr('Note').'", "name": "note", "value": "$note$" ]}
</div>
</div>
</div>
</div>';
// Elenco provider disponibili
$providers = OAuth2::$providers;
$elenco_provider = [];
foreach ($providers as $key => $provider) {
$elenco_provider[] = [
'id' => $key,
'text' => $provider['name'],
'help' => $provider['help'],
];
}
echo '
<!-- OAuth2 -->
<div class="box box-info">
<div class="box-header">
<h3 class="box-title">'.tr('OAuth2').'</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-6">
<span class="label label-warning pull-right hidden" id="guida-configurazione"></span>
{[ "type": "select", "label": "'.tr('Provider account').'", "name": "provider", "value": "$provider$", "values": '.json_encode($elenco_provider).', "disabled": "'.intval(empty($account->provider)).'" ]}
</div>
<div class="col-md-3">
{[ "type": "checkbox", "label": "'.tr('Abilita OAuth2').'", "name": "abilita_oauth2", "value": "'.intval(!empty($account->provider)).'" ]}
</div>
<div class="col-md-3">
<a type="button" class="btn btn-success btn-block '.(empty($account->provider) || empty($account->client_id) || empty($account->client_secret) ? 'disabled' : '').'" style="margin-top: 25px" href="'.base_url().'/editor.php?id_module='.$id_module.'&id_record='.$id_record.'&op=oauth2">
<i class="fa fa-refresh"></i> '.(empty($account->access_token) ? tr('Completa configurazione') : tr('Ripeti configurazione')).'
</a>
</div>
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Client ID').'", "name": "client_id", "value": "$client_id$", "disabled": "'.intval(empty($account->provider)).'" ]}
</div>
<div class="col-md-6">
{[ "type": "text", "label": "'.tr('Client Secret').'", "name": "client_secret", "value": "$client_secret$", "disabled": "'.intval(empty($account->provider)).'" ]}
</div>
</div>
</div>
</div>
</form>
<?php
<script>
var abilita_oauth2 = input("abilita_oauth2");
var provider = input("provider");
var client_id = input("client_id");
var client_secret = input("client_secret");
var guida = $("#guida-configurazione");
abilita_oauth2.change(function() {
const disable = !abilita_oauth2.get();
provider.setDisabled(disable);
client_id.setDisabled(disable);
client_secret.setDisabled(disable);
});
provider.change(function() {
const data = provider.getData();
if (data.id) {
guida.removeClass("hidden");
guida.html(`<a href="${data.help}">'.tr('Istruzioni di configurazione').' <i class="fa fa-external-link"></i></a>`);
} else {
guida.addClass("hidden");
}
})
$(document).ready(function() {
provider.trigger("change");
})
</script>';
// Collegamenti diretti
// Template email collegati a questo account
$elementi = $dbo->fetchArray('SELECT `id`, `name` FROM `em_templates` WHERE `id_account` = '.prepare($id_record));
@ -134,4 +211,3 @@ if (!empty($elementi)) {
<i class="fa fa-trash"></i> '.tr('Elimina').'
</a>';
}
?>

53
oauth2.php Normal file
View File

@ -0,0 +1,53 @@
<?php
use Models\Module;
use Modules\Emails\Account;
use Modules\Emails\OAuth2;
$skip_permissions = true;
include_once __DIR__.'/core.php';
session_write_close();
// Authorization information
$state = $_GET['state'];
$code = $_GET['code'];
// Account individuato via oauth2_state
if (!empty($state)) {
$account = Account::where('oauth2_state', '=', $state)
->first();
} else {
$account = Account::find(get('id_account'));
// Impostazione access token a null per reimpostare la configurazione
$account->access_token = null;
$account->refresh_token = null;
$account->save();
}
if (empty($account)) {
echo tr('Errore durante il completamento della configurazione: account non trovato');
return;
}
// Inizializzazione
$oauth2 = new OAuth2($account);
// Redirect all'URL di autorizzazione del servizio esterno
$redirect = $oauth2->configure($code, $state);
// Redirect automatico al record
if (empty($redirect)) {
$modulo_account_email = Module::pool('Account email');
$redirect = base_path().'/editor.php?id_module='.$modulo_account_email->id.'&id_record='.$account->id;
}
if (empty($_GET['error'])) {
redirect($redirect);
exit();
} else {
echo $_GET['error'].'<br>'.$_GET['error_description'].'
<br><br>
<a href="'.$redirect.'">'.tr('Riprova').'</a>';
}

View File

@ -141,7 +141,7 @@ class FileManager implements ManagerInterface
<i class="fa fa-external-link"></i> '.$r['name'].'
</a>
<small> ('.$file->extension.')'.((!empty($file->size)) ? ' ('. FileSystem::formatBytes($file->size).')' : '').' '.(((setting('Logo stampe') == $r['filename']) || (setting('Filigrana stampe') == $r['filename'])) ? '<i class="fa fa-file-text-o"></i>' : '').'</small>'.'
<small> ('.$file->extension.')'.((!empty($file->size)) ? ' ('.FileSystem::formatBytes($file->size).')' : '').' '.(((setting('Logo stampe') == $r['filename']) || (setting('Filigrana stampe') == $r['filename'])) ? '<i class="fa fa-file-text-o"></i>' : '').'</small>'.'
</td>
<td>'.timestampFormat($r['created_at']).'</td>

View File

@ -23,6 +23,8 @@ use Modules\Emails\Account;
use Modules\Emails\Mail;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\OAuth;
use PHPMailer\PHPMailer\SMTP;
use Prints;
use Uploads;
@ -35,45 +37,63 @@ class EmailNotification extends PHPMailer implements NotificationInterface
public function __construct($account = null, $exceptions = null)
{
parent::__construct($exceptions);
parent::__construct(true);
$this->CharSet = 'UTF-8';
// Configurazione di base
$config = Account::find($account);
if (empty($config)) {
$config = Account::where('predefined', true)->first();
$account = $account instanceof Account ? $account : Account::find($account);
if (empty($account)) {
$account = Account::where('predefined', true)->first();
}
// Preparazione email
$this->IsHTML(true);
if (!empty($config['server'])) {
$this->IsSMTP(true);
if (!empty($account['server'])) {
$this->IsSMTP();
// Impostazioni di debug
$this->SMTPDebug = \App::debug() ? 2 : 0;
$this->SMTPDebug = 2;
$this->Debugoutput = function ($str, $level) {
$this->infos[] = $str;
};
// Impostazioni dell'host
$this->Host = $config['server'];
$this->Port = $config['port'];
$this->Host = $account['server'];
$this->Port = $account['port'];
// Impostazioni di autenticazione
if (!empty($config['username'])) {
if (!empty($account['username'])) {
$this->SMTPAuth = true;
$this->Username = $config['username'];
$this->Password = $config['password'];
$this->Username = $account['username'];
// Configurazione OAuth2
if (!empty($account['access_token'])) {
$oauth2 = $account->getGestoreOAuth2();
$this->AuthType = 'XOAUTH2';
$this->setOAuth(
new OAuth([
'provider' => $oauth2->getProvider(),
'refreshToken' => $oauth2->getRefreshToken(),
'clientId' => $account->client_id,
'clientSecret' => $account->client_secret,
'userName' => $account->username,
])
);
} else {
$this->Password = $account['password'];
}
}
// Impostazioni di sicurezza
if (in_array(strtolower($config['encryption']), ['ssl', 'tls'])) {
$this->SMTPSecure = strtolower($config['encryption']);
if (in_array(strtolower($account['encryption']), ['ssl', 'tls'])) {
$this->SMTPSecure = strtolower($account['encryption']);
}
if (!empty($config['ssl_no_verify'])) {
// Disabilitazione verifica host
if (!empty($account['ssl_no_verify'])) {
$this->SMTPOptions = [
'ssl' => [
'verify_peer' => false,
@ -84,8 +104,8 @@ class EmailNotification extends PHPMailer implements NotificationInterface
}
}
$this->From = $config['from_address'];
$this->FromName = $config['from_name'];
$this->From = $account['from_address'];
$this->FromName = $account['from_name'];
$this->WordWrap = 78;
}
@ -192,6 +212,7 @@ class EmailNotification extends PHPMailer implements NotificationInterface
// Segnalazione degli errori
if (!$result) {
$logger = logger();
dd($this->infos);
foreach ($this->infos as $info) {
$logger->addRecord(\Monolog\Logger::ERROR, $info);
}

View File

@ -167,7 +167,7 @@ class Query
$search_filters = [];
foreach ($search as $field => $original_value) {
$pos = array_search($field, $total['fields']);
$value = trim($original_value);
$value = is_array($original_value) ? $original_value : trim($original_value);
if (isset($value) && $pos !== false) {
$search_query = $total['search_inside'][$pos];

View File

@ -55,4 +55,3 @@ if (!empty($documento->idsede)) {
}
}
}

View File

@ -142,3 +142,11 @@ ALTER TABLE `dt_ddt` ADD `id_ddt_trasporto_interno` INT(11) NULL, ADD FOREIGN KE
-- Aggiunto ragruppamento referenti per sede
UPDATE `zz_plugins` SET `options` = ' { \"main_query\": [ { \"type\": \"table\", \"fields\": \"Nome, Indirizzo, Città, CAP, Provincia, Referente\", \"query\": \"SELECT an_sedi.id, an_sedi.nomesede AS Nome, an_sedi.indirizzo AS Indirizzo, an_sedi.citta AS Città, an_sedi.cap AS CAP, an_sedi.provincia AS Provincia, GROUP_CONCAT(an_referenti.nome SEPARATOR \\\", \\\") AS Referente FROM an_sedi LEFT OUTER JOIN an_referenti ON idsede = an_sedi.id WHERE 1=1 AND an_sedi.idanagrafica=|id_parent| GROUP BY an_sedi.id HAVING 2=2 ORDER BY an_sedi.id DESC\"} ]}' WHERE `zz_plugins`.`name` = 'Sedi';
UPDATE `zz_group_module` SET `clause` = 'in_interventi.id IN (SELECT idintervento FROM in_interventi_tecnici WHERE idintervento=in_interventi.id AND idtecnico=|id_anagrafica| UNION SELECT id_intervento FROM in_interventi_tecnici_assegnati WHERE id_intervento=in_interventi.id AND id_tecnico=|id_anagrafica|)' WHERE `zz_group_module`.`name` = 'Mostra interventi ai tecnici coinvolti';
-- Aggiunto supporto autenticazione OAuth 2
ALTER TABLE `em_accounts` ADD `provider` varchar(255),
ADD `client_id` TEXT,
ADD `client_secret` TEXT,
ADD `oauth2_state` TEXT,
ADD `access_token` TEXT,
ADD `refresh_token` TEXT;