Introduzione procedura di aggiornamento

This commit is contained in:
Dasc3er 2021-07-07 11:35:30 +02:00
parent 704a768d2b
commit a12b21ad51
21 changed files with 488 additions and 91 deletions

View File

@ -11,7 +11,7 @@ LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_DATABASE=
DB_USERNAME=root
DB_PASSWORD=

3
.gitignore vendored
View File

@ -89,7 +89,7 @@ config.inc.php
database.sql
VERSION
REVISION
.php_cs.cache
.php-cs-fixer.cache
manifest.json
checksum.json
database.json
@ -112,3 +112,4 @@ Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.vagrant

13
.php_cs → .php-cs-fixer.php Executable file → Normal file
View File

@ -10,23 +10,16 @@ $finder = PhpCsFixer\Finder::create()
->ignoreVCS(true)
->in(__DIR__);
$config = PhpCsFixer\Config::create()
->setRules([
$config = new PhpCsFixer\Config();
$config->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
'yoda_style' => false,
'no_short_echo_tag' => true,
'echo_tag_syntax' => ['format' => 'long'],
'ordered_imports' => true,
'no_alternative_syntax' => true,
'ordered_class_elements' => true,
'phpdoc_order' => true,
//'no_superfluous_phpdoc_tags' => [
// 'allow_mixed' => true,
// 'allow_unused_params' => true,
//],
//'phpdoc_add_missing_param_annotation' => [
// 'only_untyped' => false,
//],
])
->setFinder($finder);

View File

@ -3,10 +3,12 @@
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Models\Group;
use Models\Setting;
use Models\Upload;
use PDOException;
class InitializationController extends Controller
{
@ -24,13 +26,25 @@ class InitializationController extends Controller
if (!isset(self::$init)) {
$database = database();
$has_azienda = $database->fetchNum("SELECT `an_anagrafiche`.`idanagrafica`
// Controlli sull'inizializzazione dell'anagrafica Azienda
try {
$has_azienda = $database->fetchNum("SELECT `an_anagrafiche`.`idanagrafica`
FROM `an_anagrafiche`
LEFT JOIN `an_tipianagrafiche_anagrafiche` ON `an_anagrafiche`.`idanagrafica`=`an_tipianagrafiche_anagrafiche`.`idanagrafica`
LEFT JOIN `an_tipianagrafiche` ON `an_tipianagrafiche`.`idtipoanagrafica`=`an_tipianagrafiche_anagrafiche`.`idtipoanagrafica`
WHERE `an_tipianagrafiche`.`descrizione` = 'Azienda' AND `an_anagrafiche`.`deleted_at` IS NULL") != 0;
$has_user = $database->fetchNum('SELECT `id` FROM `zz_users`') != 0;
} catch (PDOException $e) {
$has_azienda = false;
}
// Controllo sull'utente amministratore
try {
$has_user = $database->fetchNum('SELECT `id` FROM `zz_users`') != 0;
} catch (PDOException $e) {
$has_user = false;
}
// Controlli sulle impostazioni
$settings = [
'Regime Fiscale' => true,
'Tipo Cassa Previdenziale' => false,
@ -41,16 +55,21 @@ class InitializationController extends Controller
'Valuta' => true,
];
if (!empty(setting("Percentuale ritenuta d'acconto"))) {
$settings["Causale ritenuta d'acconto"] = true;
}
$has_settings = true;
foreach ($settings as $setting => $required) {
if (empty(setting($setting)) && $required) {
$has_settings = false;
break;
try {
if (!empty(setting("Percentuale ritenuta d'acconto"))) {
$settings["Causale ritenuta d'acconto"] = true;
}
foreach ($settings as $setting => $required) {
if (empty(setting($setting)) && $required) {
$has_settings = false;
break;
}
}
} catch (QueryException $e) {
$has_settings = false;
}
self::$init = [

View File

@ -35,17 +35,6 @@ class LegacyController extends Controller
return $response;
}
protected static function isApiRequest($path)
{
// Fix per redirect all'API
$api_request = false;
if (in_array($path, ['api', 'api/', 'api/index.php'])) {
$api_request = true;
}
return $api_request;
}
public static function simulate($path)
{
$base_path = base_path('legacy');
@ -76,4 +65,15 @@ class LegacyController extends Controller
return $output;
}
protected static function isApiRequest($path)
{
// Fix per redirect all'API
$api_request = false;
if (in_array($path, ['api', 'api/', 'api/index.php'])) {
$api_request = true;
}
return $api_request;
}
}

View File

@ -12,7 +12,7 @@ class RequirementsController extends Controller
public function index(Request $request)
{
$args = [
'requirements' => self::getRequirementsList()
'requirements' => self::getRequirementsList(),
];
return view('config.requirements', $args);

View File

@ -0,0 +1,158 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Database\Migrations\Migrator;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Update;
class UpdateController extends Controller
{
protected static $legacyUpdateRate = 20;
protected static $legacyScriptValue = 60;
public function __construct()
{
$this->initMigrationsTable();
}
public function index(Request $request)
{
$migrations = self::computeMigrations();
$legacy = self::computeLegacyUpdates();
return view('config.update', [
'installing' => database()->isInstalled(),
'legacy_number' => $legacy['number'],
'updates_available' => $legacy['number'] + $migrations['number'],
'total_weight' => $legacy['weight'] + $migrations['weight'],
]);
}
public static function getMigrator(): Migrator
{
$app = app();
return $app['migrator'];
}
public static function isCompleted()
{
$migrations = self::computeMigrations();
$legacy = self::computeLegacyUpdates();
return $legacy['number'] + $migrations['number'] == 0;
}
public function execute(Request $request)
{
if ($request->input('legacy') == 'true') {
$response = $this->executeLegacyUpdate();
} else {
$response = $this->executeMigration();
}
return response()->json($response);
}
protected function initMigrationsTable()
{
$migrator = self::getMigrator();
if ($migrator->repositoryExists()) {
return;
}
$migrator->getRepository()->createRepository();
}
protected static function computeMigrations()
{
$migrator = self::getMigrator();
$paths = array_merge(
$migrator->paths(),
[app()->databasePath().DIRECTORY_SEPARATOR.'migrations']
);
// Elenco di tutte le migrazioni disponibili
$files = $migrator->getMigrationFiles($paths);
$ran = $migrator->getRepository()->getRan();
// Filtro per migrazioni da eseguire
$migrations = Collection::make($files)
->reject(function ($file) use ($migrator, $ran) {
return in_array($migrator->getMigrationName($file), $ran);
})->values()->all();
$count = count($migrations);
return [
'list' => $migrations,
'number' => $count,
'weight' => $count,
];
}
protected static function computeLegacyUpdates()
{
$updates = Update::getTodoUpdates();
$total = 0;
foreach ($updates as $update) {
if ($update['sql'] && (!empty($update['done']) || is_null($update['done']))) {
$queries = readSQLFile(base_dir().$update['directory'].$update['filename'].'.sql', ';');
$total += count($queries);
if (intval($update['done']) > 1) {
$total -= intval($update['done']) - 2;
}
}
if ($update['script']) {
$total += self::$legacyScriptValue;
}
}
return [
'list' => $updates,
'number' => count($updates),
'weight' => $total,
];
}
protected function executeMigration()
{
$migrations = $this->computeMigrations();
// Ricerca della migrazione indicata
$migration = $migrations['list'][0];
// Esecuzione migrazione
$migrator = self::getMigrator();
$migrator->runPending([$migration]);
return [
'progress' => 1,
'migration' => $migrator->getMigrationName($migration),
'completed' => count($migrations['list']) <= 1,
];
}
protected function executeLegacyUpdate()
{
$update = Update::getCurrentUpdate();
$result = Update::doUpdate(self::$legacyUpdateRate);
$rate = 0;
if (is_array($result)) {
$rate = $result[1] - $result[0];
} elseif (!empty($update['script'])) {
$rate = self::$legacyScriptValue;
}
return [
'progress' => $rate,
'legacyVersion' => $update['version'],
];
}
}

View File

@ -5,11 +5,25 @@ namespace App\Http\Middleware;
use App\Http\Controllers\ConfigurationController;
use App\Http\Controllers\InitializationController;
use App\Http\Controllers\RequirementsController;
use App\Http\Controllers\UpdateController;
use Closure;
use Illuminate\Http\Request;
class EnsureConfiguration
{
/**
* @return string|null
*/
protected $redirect_route = null;
/**
* @param null $redirect
*/
public function setRedirect($redirect): void
{
$this->redirect_route = $redirect;
}
/**
* Handle an incoming request.
*
@ -22,33 +36,38 @@ class EnsureConfiguration
return $next($request);
}
// Test sui requisiti del gestionale
$result = $this->checkRequirements($route);
if ($result !== null) {
return $result;
}
// Controlli in ordine per l'esecizione
$checks = [
'checkRequirements', // Test sui requisiti del gestionale
'checkConfiguration', // Test della connessione al database
'checkMigrations', // Verifiche sullo stato delle migrazioni
'checkInitialization', // Verifiche sullo stato delle impostazioni obbligatorie
];
// Test della connessione al database
$result = $this->checkConfiguration($route);
if ($result !== null) {
return $result;
}
foreach ($checks as $check) {
$continue = $this->{$check}($route);
// Verifiche sullo stato delle migrazioni
$result = $this->checkMigrations($route);
if ($result !== null) {
return $result;
}
// Redirect automatico causato dal controllo corrente
if (isset($this->redirect_route)) {
return redirect($this->redirect_route);
}
// Verifiche sullo stato delle impostazioni obbligatorie
$result = $this->checkInitialization($route);
if ($result !== null) {
return $result;
// Blocco sui controlli in caso di mancato completamento del controllo corrente
if (!$continue) {
break;
}
}
return $next($request);
}
/**
* Controlli sui requisiti minimi dell'ambiente di esecuzione.
*
* @param $route
*
* @return bool
*/
protected function checkRequirements($route)
{
$configuration_paths = ['requirements'];
@ -57,18 +76,25 @@ class EnsureConfiguration
if ($requirements_satisfied) {
// Redirect nel caso in cui i requisiti siano soddisfatti
if (in_array($route->getName(), $configuration_paths)) {
return redirect(route('configuration'));
$this->setRedirect(route('configuration'));
}
} else {
// Redirect per requisiti incompleti
if (!in_array($route->getName(), $configuration_paths)) {
return redirect(route('requirements'));
$this->setRedirect(route('requirements'));
}
}
return null;
return $requirements_satisfied;
}
/**
* Controlli sulla configurazione del gestionale.
*
* @param $route
*
* @return bool
*/
protected function checkConfiguration($route)
{
$configuration_paths = ['configuration', 'configuration-save', 'configuration-test'];
@ -77,18 +103,52 @@ class EnsureConfiguration
if ($configuration_completed) {
// Redirect nel caso in cui la configurazione sia correttamente funzionante
if (in_array($route->getName(), $configuration_paths)) {
return redirect(route('initialization'));
$this->setRedirect(route('update'));
}
} else {
// Redirect per configurazione mancante
if (!in_array($route->getName(), $configuration_paths)) {
return redirect(route('configuration'));
$this->setRedirect(route('configuration'));
}
}
return null;
return $configuration_completed;
}
/**
* Controlli sulle migrazioni (aggiornamenti) del gestionale.
*
* @param $route
*
* @return bool
*/
protected function checkMigrations($route)
{
$update_paths = ['update', 'update-execute'];
$update_completed = UpdateController::isCompleted();
if ($update_completed) {
// Redirect nel caso in cui la configurazione sia correttamente funzionante
if (in_array($route->getName(), $update_paths)) {
$this->setRedirect(route('initialization'));
}
} else {
// Redirect per configurazione mancante
if (!in_array($route->getName(), $update_paths)) {
$this->setRedirect(route('update'));
}
}
return $update_completed;
}
/**
* Controlli sull'inizializzazione delle informazioni di base del gestionale.
*
* @param $route
*
* @return bool
*/
protected function checkInitialization($route)
{
$initialization_paths = ['initialization', 'initialization-save'];
@ -97,20 +157,15 @@ class EnsureConfiguration
if ($initialization_completed) {
// Redirect nel caso in cui la configurazione sia correttamente funzionante
if (in_array($route->getName(), $initialization_paths)) {
return redirect(route('login'));
$this->setRedirect(route('login'));
}
} else {
// Redirect per configurazione mancante
if (!in_array($route->getName(), $initialization_paths)) {
return redirect(route('initialization'));
$this->setRedirect(route('initialization'));
}
}
return null;
}
protected function checkMigrations($route)
{
return null;
return $initialization_completed;
}
}

View File

@ -19,9 +19,8 @@
namespace App\OSM\Prints\Retro;
use App;
use AppLegacy;
use App\OSM\Prints\MPDFManager;
use AppLegacy;
class Manager extends MPDFManager
{

View File

@ -20,10 +20,10 @@
namespace App\OSM\Prints;
use App\OSM\ComponentManagerTrait;
use Common\SimpleModelTrait;
use Models\Group;
use Common\Model;
use Common\SimpleModelTrait;
use Illuminate\Database\Eloquent\Builder;
use Models\Group;
use Models\Module;
use Traits\LocalPoolTrait;

View File

@ -20,9 +20,9 @@
namespace App\OSM\Widgets\Retro;
use App\Http\Controllers\LegacyController;
use App\OSM\Widgets\ModalWidget as Original;
use Models\Module;
use Util\Query;
use App\OSM\Widgets\ModalWidget as Original;
class ModalWidget extends Original
{

View File

@ -13,13 +13,12 @@ class EmailHistory extends Component
* Create a new component instance.
*
* @param string|int $module
* @param int $record
* @param int $record
*/
public function __construct(
$module,
$record
) {
// Visualizzo il log delle operazioni di invio email
$this->emails = Mail::whereRaw('id IN (SELECT id_email FROM zz_operations WHERE id_record = '.prepare($record).' AND id_module = '.prepare($module).' AND id_email IS NOT NULL)')
->orderBy('created_at', 'DESC')

View File

@ -8,21 +8,20 @@ use Illuminate\View\Component;
class Select extends InputWrapper
{
/**
* Create a new component instance.
*
* @param string $name
* @param string $name
* @param string|null $id
* @param string|null $value
* @param bool|string $required
* @param string|null $label
* @param string|null $placeholder
* @param bool $multiple
* @param string $source
* @param string $query
* @param array $values
* @param array $options
* @param bool $multiple
* @param string $source
* @param string $query
* @param array $values
* @param array $options
*/
public function __construct(
$name,

View File

@ -20,8 +20,9 @@ class WidgetGroup extends Component
* Create a new component instance.
*
* @param string|int $module
* @param string $place
* @param string $position
* @param string $place
* @param string $position
*
* @throws \Exception
*/
public function __construct(

View File

@ -16,7 +16,6 @@
"type": "project",
"require": {
"php": "^7.3|^8.0",
"ext-apache": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
@ -58,7 +57,8 @@
"require-dev": {
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"friendsofphp/php-cs-fixer": "^2.10",
"friendsofphp/php-cs-fixer": "^3.0",
"laravel/homestead": "^12.2",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.3.3"

2
legacy

@ -1 +1 @@
Subproject commit 3900b789459d1cc805338bcc795a3ad187b972d0
Subproject commit 39a79aea88f220f4d49c349df176b1ef310005f8

View File

@ -0,0 +1,167 @@
@extends('layouts.guest')
@section('title', $installing ? tr('Installazione') : tr('Aggiornamento'))
@section('box_class', 'box-warning text-center')
@section('box_header')
<h3 class="box-title">{{ $installing ? tr('Installazione di OpenSTAManager') : tr('Aggiornamento di OpenSTAManager') }}</h3>
@endsection
@section('content')
@if($installing)
<p><strong>{{ tr("E' la prima volta che avvii OpenSTAManager e non hai ancora installato il database") }}.</strong></p>
@else
<p>{{ tr("E' necessario aggiornare il database a una nuova versione") }}.</p>
@endif
@php($button = $installing ? tr('Installa!') : tr('Aggiorna!'))
<p>{!! tr("Premi il tasto _BUTTON_ per procedere con l'_OP_!", [
'_BUTTON_' => '<b>'.$button.'</b>',
'_OP_' => $installing ? tr('installazione') : tr('aggiornamento'),
]) !!}</p>
<button type="button" class="btn btn-primary" id="contine_button">
{{ $button }}
</button>
<div id="update-info" class="hidden">
<div class="progress">
<div class="progress-bar bg-warning progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%">
<span>0%</span>
</div>
</div>
<hr>
<div class="box box-outline box-info text-center collapsed-box">
<div class="box-header with-border">
<h3 class="box-title"><a class="clickable" data-widget="collapse">{{ tr('Log') }}</a></h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button>
</div>
</div>
<div class="box-body info text-left"></div>
</div>
</div>
<div id="result"></div>
<div style="display: none;" id="completed">
<p><strong>{{ tr('Aggiornamento completato') }}</strong> <i class="fa fa-smile-o"></i></p>
@if($installing)
<!-- Istruzioni per la prima installazione -->
<p class="text-danger">{{ tr("E' fortemente consigliato rimuovere i permessi di scrittura dal file _FILE_", ['_FILE_' => '<b>config.inc.php</b>']) }}.</p>
@endif
<a class="btn btn-success btn-block" href="{{ route('login') }}">
<i class="fa fa-check"></i> {{ tr('Continua') }}
</a>
</div>
<script>
globals.update = {
totalNumber: {{ $updates_available }},
totalCount: {{ $total_weight }},
legacyNumber: {{ $legacy_number }},
url: "{{ route('update-execute') }}",
progress: {
number: 0,
count: 0,
legacyVersions: [],
},
translations: {
title: "{{ tr('Sei sicuro di voler procedere?') }}",
continue: "{{ tr('Procedi') }}",
legacyVersion: "{{ tr('Aggiornamento _DONE_ di _TODO_ (_VERSION_)') }}",
migrationList: "{{ tr('Elenco migrazioni del database') }}",
migration: "{{ tr('Migrazione _NAME_') }}",
}
};
function executeUpdate() {
$.ajax({
url: globals.update.url,
type: "POST",
dataType: "JSON",
cache: false,
data: {
legacy: globals.update.progress.number < globals.update.legacyNumber,
},
success: function(response) {
addProgress(response['progress']);
if (response['legacyVersion']){
addVersion(response['legacyVersion']);
} else if (response['migration']) {
addMigration(response['migration']);
}
if (!response['completed']) {
executeUpdate();
}
},
error: ajaxError
});
}
function addProgress(rate) {
globals.update.progress.count += rate;
let percent = globals.update.progress.count / globals.update.totalCount * 100;
percent = Math.round(percent);
percent = percent > 100 ? 100 : percent;
setPercent(percent);
}
function setPercent(percent) {
$("#update-info .progress-bar").width(percent + "%");
$("#update-info .progress-bar span").text(percent + "%");
if (percent >= 100) {
$("#completed").show();
}
}
function addVersion(version) {
if (globals.update.progress.legacyVersions.indexOf(version) === -1) {
globals.update.progress.legacyVersions.push(version);
globals.update.progress.number += 1;
let message = globals.update.translations.legacyVersion
.replace("_DONE_", globals.update.progress.number)
.replace("_TODO_", globals.update.totalNumber)
.replace("_VERSION_", version.trim());
const selector = $("#update-info .info");
selector.append("<p><strong>" + message + "</strong></p>");
}
}
function addMigration(name) {
const selector = $("#update-info .info");
if (selector.html().indexOf(globals.update.translations.migrationList) === -1) {
selector.append("<br><hr><br><p><strong>" + globals.update.translations.migrationList + "</strong></p>");
}
let message = globals.update.translations.migration
.replace("_NAME_", name);
selector.append("<p>" + message + "</p>");
}
$("#contine_button").click(function () {
swal({
title: globals.update.translations.title,
text: "",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn btn-lg btn-success",
confirmButtonText: globals.update.translations.continue,
}).then(function () {
$("#update-info").removeClass("hidden");
$("#contine_button").remove();
executeUpdate();
}, function () {
});
});
</script>
@endsection

View File

@ -39,6 +39,8 @@
search: search,
translations: {
"errorTitle": "{{ tr('Errore') }}",
"errorMessage": "{{ tr("Si è verificato un errore nell'esecuzione dell'operazione richiesta") }}",
"day": "{{ tr('Giorno') }}",
"week": "{{ tr('Settimana') }}",
"month": "{{ tr('Mese') }}",
@ -61,8 +63,6 @@
"delete": "{{ tr('Elimina') }}",
"deleteTitle": "{{ tr('Sei sicuro?') }}",
"deleteMessage": "{{ tr('Eliminare questo elemento?') }}",
"errorTitle": "{{ tr('Errore') }}",
"errorMessage": "{{ tr("Si è verificato un errore nell'esecuzione dell'operazione richiesta") }}",
"close": "{{ tr('Chiudi') }}",
"filter": "{{ tr('Filtra') }}",
"long": "{{ tr('La ricerca potrebbe richiedere del tempo') }}",

View File

@ -51,6 +51,8 @@
full_locale: '{{ locale() }}',
translations: {
"errorTitle": "{{ tr('Errore') }}",
"errorMessage": "{{ tr("Si è verificato un errore nell'esecuzione dell'operazione richiesta") }}",
password: {
"wordMinLength": "{{ tr('La password è troppo corta') }}",
"wordMaxLength": "{{ tr('La password è troppo lunga') }}",

View File

@ -1,7 +1,6 @@
<?php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\ConfirmablePasswordController;
use App\Http\Controllers\Auth\EmailVerificationNotificationController;
use App\Http\Controllers\Auth\EmailVerificationPromptController;
use App\Http\Controllers\Auth\PasswordResetController;

View File

@ -4,10 +4,10 @@ use App\Http\Controllers\ConfigurationController;
use App\Http\Controllers\HookController;
use App\Http\Controllers\InfoController;
use App\Http\Controllers\InitializationController;
use App\Http\Controllers\LegacyController;
use App\Http\Controllers\MessageController;
use App\Http\Controllers\RequirementsController;
use App\Http\Controllers\Test;
use App\Http\Controllers\UpdateController;
use App\Http\Controllers\UserController;
use App\Http\Controllers\WidgetModalController;
use Illuminate\Support\Facades\Route;
@ -46,6 +46,12 @@ Route::get('/config-test', [ConfigurationController::class, 'test'])
Route::post('/config', [ConfigurationController::class, 'save'])
->name('configuration-save');
// Installazione aggiornamenti del gestionale
Route::get('/update', [UpdateController::class, 'index'])
->name('update');
Route::post('/update', [UpdateController::class, 'execute'])
->name('update-execute');
// Inizializzazione del gestionale
Route::get('/init', [InitializationController::class, 'index'])
->name('initialization');
@ -120,7 +126,6 @@ Route::get('/logs', [UserController::class, 'logs'])
->middleware(['auth'])
->name('logs');
// Log di accesso
Route::get('/widget/modal/{id}', [WidgetModalController::class, 'modal'])
->whereNumber('id')