From 97eaa07057adf031be68639d52487f439067b5ed Mon Sep 17 00:00:00 2001 From: Dasc3er Date: Sat, 13 Mar 2021 14:13:19 +0100 Subject: [PATCH] Introduzione modulo Aggiornamenti con nuove strutture --- app/Http/Controllers/LegacyController.php | 3 +- .../Controllers/RequirementsController.php | 149 ++++++ app/Http/Middleware/EnsureConfiguration.php | 28 +- app/Http/Middleware/Language.php | 2 - app/View/Components/Input.php | 2 +- app/View/Components/Inputs/Number.php | 6 +- app/View/Components/Inputs/Select.php | 1 - app/helpers.php | 8 +- composer.json | 2 + config/requirements.php | 61 +++ legacy | 2 +- modules/Aggiornamenti/Config/config.php | 5 + .../Database/Migrations/.gitkeep | 0 .../Aggiornamenti/Database/Seeders/.gitkeep | 0 .../Seeders/AggiornamentiDatabaseSeeder.php | 21 + .../Aggiornamenti/Database/factories/.gitkeep | 0 modules/Aggiornamenti/Entities/.gitkeep | 0 modules/Aggiornamenti/Http/Aggiornamento.php | 466 ++++++++++++++++++ .../Controllers/AggiornamentiController.php | 249 ++++++++++ .../ControlliAggiuntiviController.php | 14 + .../Aggiornamenti/Http/DowngradeException.php | 9 + .../Aggiornamenti/Http/Middleware/.gitkeep | 0 modules/Aggiornamenti/Http/Requests/.gitkeep | 0 modules/Aggiornamenti/Http/UpdateHook.php | 116 +++++ .../AggiornamentiServiceProvider.php | 112 +++++ .../Providers/RouteServiceProvider.php | 69 +++ .../Aggiornamenti/Resources/assets/.gitkeep | 0 .../Aggiornamenti/Resources/assets/js/app.js | 0 .../Resources/assets/sass/app.scss | 0 modules/Aggiornamenti/Resources/lang/.gitkeep | 0 .../Resources/views/index.blade.php | 222 +++++++++ .../Resources/views/update.blade.php | 137 +++++ modules/Aggiornamenti/Routes/api.php | 18 + modules/Aggiornamenti/Routes/web.php | 35 ++ modules/Aggiornamenti/Tests/Feature/.gitkeep | 0 modules/Aggiornamenti/Tests/Unit/.gitkeep | 0 modules/Aggiornamenti/composer.json | 24 + modules/Aggiornamenti/module.json | 13 + modules/Aggiornamenti/package.json | 17 + modules/Aggiornamenti/webpack.mix.js | 14 + modules/listini/bulk.php | 26 - modules/ordini/add_preventivo.php | 85 ---- modules/ordini/bulk.php | 133 ----- modules/tipi_documento/actions.php | 115 ----- modules/tipi_documento/add.php | 46 -- modules/tipi_documento/ajax/select.php | 37 -- modules/tipi_documento/edit.php | 78 --- modules/tipi_documento/init.php | 24 - modules_statuses.json | 3 + .../components/inputs/checkbox.blade.php | 2 +- .../views/config/requirements-list.blade.php | 35 ++ resources/views/config/requirements.blade.php | 22 + resources/views/errors/503.blade.php | 10 + resources/views/layouts/app.blade.php | 17 + resources/views/layouts/guest.blade.php | 2 +- routes/legacy.php | 7 +- routes/web.php | 6 + 57 files changed, 1886 insertions(+), 567 deletions(-) create mode 100644 app/Http/Controllers/RequirementsController.php create mode 100644 config/requirements.php create mode 100644 modules/Aggiornamenti/Config/config.php create mode 100644 modules/Aggiornamenti/Database/Migrations/.gitkeep create mode 100644 modules/Aggiornamenti/Database/Seeders/.gitkeep create mode 100644 modules/Aggiornamenti/Database/Seeders/AggiornamentiDatabaseSeeder.php create mode 100644 modules/Aggiornamenti/Database/factories/.gitkeep create mode 100644 modules/Aggiornamenti/Entities/.gitkeep create mode 100644 modules/Aggiornamenti/Http/Aggiornamento.php create mode 100644 modules/Aggiornamenti/Http/Controllers/AggiornamentiController.php create mode 100644 modules/Aggiornamenti/Http/Controllers/ControlliAggiuntiviController.php create mode 100644 modules/Aggiornamenti/Http/DowngradeException.php create mode 100644 modules/Aggiornamenti/Http/Middleware/.gitkeep create mode 100644 modules/Aggiornamenti/Http/Requests/.gitkeep create mode 100644 modules/Aggiornamenti/Http/UpdateHook.php create mode 100644 modules/Aggiornamenti/Providers/AggiornamentiServiceProvider.php create mode 100644 modules/Aggiornamenti/Providers/RouteServiceProvider.php create mode 100644 modules/Aggiornamenti/Resources/assets/.gitkeep create mode 100644 modules/Aggiornamenti/Resources/assets/js/app.js create mode 100644 modules/Aggiornamenti/Resources/assets/sass/app.scss create mode 100644 modules/Aggiornamenti/Resources/lang/.gitkeep create mode 100644 modules/Aggiornamenti/Resources/views/index.blade.php create mode 100644 modules/Aggiornamenti/Resources/views/update.blade.php create mode 100644 modules/Aggiornamenti/Routes/api.php create mode 100644 modules/Aggiornamenti/Routes/web.php create mode 100644 modules/Aggiornamenti/Tests/Feature/.gitkeep create mode 100644 modules/Aggiornamenti/Tests/Unit/.gitkeep create mode 100644 modules/Aggiornamenti/composer.json create mode 100644 modules/Aggiornamenti/module.json create mode 100644 modules/Aggiornamenti/package.json create mode 100644 modules/Aggiornamenti/webpack.mix.js delete mode 100644 modules/listini/bulk.php delete mode 100644 modules/ordini/add_preventivo.php delete mode 100644 modules/ordini/bulk.php delete mode 100644 modules/tipi_documento/actions.php delete mode 100644 modules/tipi_documento/add.php delete mode 100644 modules/tipi_documento/ajax/select.php delete mode 100644 modules/tipi_documento/edit.php delete mode 100644 modules/tipi_documento/init.php create mode 100644 modules_statuses.json create mode 100644 resources/views/config/requirements-list.blade.php create mode 100644 resources/views/config/requirements.blade.php create mode 100644 resources/views/errors/503.blade.php diff --git a/app/Http/Controllers/LegacyController.php b/app/Http/Controllers/LegacyController.php index 9f6fc8cbb..29068f9ed 100644 --- a/app/Http/Controllers/LegacyController.php +++ b/app/Http/Controllers/LegacyController.php @@ -11,8 +11,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class LegacyController extends Controller { - public function index(Request $request, $path = 'index.php') + public function index(Request $request) { + $path = substr($request->getPathInfo(), 1); $base_path = base_path('legacy'); // Fix per redirect all'API diff --git a/app/Http/Controllers/RequirementsController.php b/app/Http/Controllers/RequirementsController.php new file mode 100644 index 000000000..3979cc398 --- /dev/null +++ b/app/Http/Controllers/RequirementsController.php @@ -0,0 +1,149 @@ + self::getRequirementsList() + ]; + + return view('config.requirements', $args); + } + + public static function getRequirementsList($file = null) + { + $requirements = self::getRequirements($file); + + $list = [ + tr('Apache') => $requirements['apache'], + tr('PHP (_VERSION_)', [ + '_VERSION_' => phpversion(), + ]) => $requirements['php'], + tr('Percorsi di servizio') => $requirements['paths'], + ]; + + return $list; + } + + public static function getRequirements($file = null) + { + if (empty($file) && isset(self::$requirements)) { + return self::$requirements; + } + + $list = config('requirements'); + if (!empty($file)) { + $file = realpath($file); + if (string_starts_with($file)) { + $list = include $file; + } + } + + // Apache + if (function_exists('apache_get_modules')) { + $available_modules = apache_get_modules(); + } + + $apache = $list['apache']; + foreach ($apache as $name => $values) { + $status = isset($available_modules) ? in_array($name, $available_modules) : false; + $status = isset($values['server']) && isset($_SERVER[$values['server']]) ? $_SERVER[$values['server']] == 'On' : $status; + + $apache[$name]['description'] = tr('Il modulo Apache _MODULE_ deve essere abilitato', [ + '_MODULE_' => ''.$name.'', + ]); + $apache[$name]['status'] = $status; + } + + // PHP + $php = $list['php']; + foreach ($php as $name => $values) { + if ($values['type'] == 'ext') { + $description = !empty($values['required']) ? tr("L'estensione PHP _EXT_ deve essere abilitata", [ + '_EXT_' => ''.$name.'', + ]) : tr("E' consigliata l'abilitazione dell'estensione PHP _EXT_", [ + '_EXT_' => ''.$name.'', + ]); + + $status = extension_loaded($name); + } else { + $suggested = str_replace(['>', '<'], '', $values['suggested']); + $value = ini_get($name); + + $description = tr("Valore consigliato per l'impostazione PHP: _VALUE_ (Valore attuale: _INI_)", [ + '_VALUE_' => $suggested, + '_INI_' => ini_get($name), + ]); + + $suggested = strpos($suggested, 'B') !== false ? $suggested : $suggested.'B'; + $value = strpos($value, 'B') !== false ? $value : $value.'B'; + + $ini = FileSystem::convertBytes($value); + $real = FileSystem::convertBytes($suggested); + + if (starts_with($values['suggested'], '>')) { + $status = $ini >= substr($real, 1); + } elseif (starts_with($values['suggested'], '<')) { + $status = $ini <= substr($real, 1); + } else { + $status = ($real == $ini); + } + + $php[$name]['value'] = $value; + + if (is_bool($suggested)) { + $suggested = !empty($suggested) ? 'On' : 'Off'; + } + } + + $php[$name]['description'] = $description; + $php[$name]['status'] = $status; + } + + // Percorsi di servizio + $paths = []; + foreach ($list['directories'] as $name) { + $status = is_writable(base_path().DIRECTORY_SEPARATOR.$name); + $description = tr('Il percorso _PATH_ deve risultare accessibile da parte del gestionale (permessi di lettura e scrittura)', [ + '_PATH_' => ''.$name.'', + ]); + + $paths[$name]['description'] = $description; + $paths[$name]['status'] = $status; + } + + $result = [ + 'apache' => $apache, + 'php' => $php, + 'paths' => $paths, + ]; + + if (empty($file)) { + self::$requirements = $result; + } + + return $result; + } + + public static function isSatisfied() + { + $general_status = true; + + $requirements = self::getRequirements(); + foreach ($requirements as $key => $values) { + foreach ($values as $value) { + $general_status &= !empty($value['required']) ? $value['status'] : true; + } + } + + return $general_status; + } +} diff --git a/app/Http/Middleware/EnsureConfiguration.php b/app/Http/Middleware/EnsureConfiguration.php index 38e3da0e4..e60eb95a1 100644 --- a/app/Http/Middleware/EnsureConfiguration.php +++ b/app/Http/Middleware/EnsureConfiguration.php @@ -4,6 +4,7 @@ namespace App\Http\Middleware; use App\Http\Controllers\ConfigurationController; use App\Http\Controllers\InitializationController; +use App\Http\Controllers\RequirementsController; use Closure; use Illuminate\Http\Request; @@ -21,6 +22,12 @@ class EnsureConfiguration return $next($request); } + // Test sui requisiti del gestionale + $result = $this->checkRequirements($route); + if ($result !== null) { + return $result; + } + // Test della connessione al database $result = $this->checkConfiguration($route); if ($result !== null) { @@ -42,9 +49,28 @@ class EnsureConfiguration return $next($request); } + protected function checkRequirements($route) + { + $configuration_paths = ['requirements']; + $requirements_satisfied = RequirementsController::isSatisfied(); + + if ($requirements_satisfied) { + // Redirect nel caso in cui i requisiti siano soddisfatti + if (in_array($route->getName(), $configuration_paths)) { + return redirect(route('configuration')); + } + } else { + // Redirect per requisiti incompleti + if (!in_array($route->getName(), $configuration_paths)) { + return redirect(route('requirements')); + } + } + + return null; + } + protected function checkConfiguration($route) { - // Test della connessione al database $configuration_paths = ['configuration', 'configuration-save', 'configuration-test']; $configuration_completed = ConfigurationController::isConfigured(); diff --git a/app/Http/Middleware/Language.php b/app/Http/Middleware/Language.php index f7b971ac1..926c69bcb 100644 --- a/app/Http/Middleware/Language.php +++ b/app/Http/Middleware/Language.php @@ -11,8 +11,6 @@ class Language /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next * @return mixed */ public function handle(Request $request, Closure $next) diff --git a/app/View/Components/Input.php b/app/View/Components/Input.php index 4f99fa0f4..4aa85a064 100644 --- a/app/View/Components/Input.php +++ b/app/View/Components/Input.php @@ -12,7 +12,7 @@ class Input extends Component /** * Create a new component instance. * - * @param string $name + * @param string $name * @param string|null $id * @param string|null $value * @param bool|string $required diff --git a/app/View/Components/Inputs/Number.php b/app/View/Components/Inputs/Number.php index 3a19447af..d74d486bd 100644 --- a/app/View/Components/Inputs/Number.php +++ b/app/View/Components/Inputs/Number.php @@ -10,13 +10,13 @@ class Number extends Input /** * 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 null $minValue + * @param null $minValue */ public function __construct( $name, @@ -44,7 +44,7 @@ class Number extends Input $decimals = setting('Cifre decimali per quantità'); // Se non è previsto un valore minimo, lo imposta a 1 - $minValue = isset($minValue) ? $minValue : '0.'.str_repeat('0', $decimals - 1).'1'; + $minValue = isset($minValue) ? $minValue : '0.'.str_repeat('0', $decimals - 1).'1'; $this->set([ 'decimals' => $decimals, diff --git a/app/View/Components/Inputs/Select.php b/app/View/Components/Inputs/Select.php index e1d6ce7db..f1cd69150 100644 --- a/app/View/Components/Inputs/Select.php +++ b/app/View/Components/Inputs/Select.php @@ -13,7 +13,6 @@ class Select extends Component */ public function __construct() { - // } /** diff --git a/app/helpers.php b/app/helpers.php index 6fef497a2..b824b56b2 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -2,8 +2,6 @@ /** * Gets the current locale. - * - * @return string */ function locale(): string { @@ -12,13 +10,11 @@ function locale(): string /** * Get the language portion of the locale. - * (ex. en_GB returns en) - * - * @return string + * (ex. en_GB returns en). */ function localeLanguage(): string { $locale = locale(); - return substr($locale, 0, strpos($locale, "_")); + return substr($locale, 0, strpos($locale, '_')); } diff --git a/composer.json b/composer.json index 75318a80b..681d514b5 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "type": "project", "require": { "php": "^7.3|^8.0", + "ext-apache": "*", "ext-curl": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -117,6 +118,7 @@ "Plugins\\DettagliArticolo\\": ["legacy/plugins/dettagli_articolo/custom/src/", "legacy/plugins/dettagli_articolo/src/"], "App\\": "app/", + "Modules\\": "modules/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" }, diff --git a/config/requirements.php b/config/requirements.php new file mode 100644 index 000000000..5a0135756 --- /dev/null +++ b/config/requirements.php @@ -0,0 +1,61 @@ + [ + 'zip' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'mbstring' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'pdo_mysql' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'dom' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'xsl' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'openssl' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'intl' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'curl' => [ + 'type' => 'ext', + 'required' => 1, + ], + 'soap' => [ + 'type' => 'ext', + 'required' => 1, + ], + + 'upload_max_filesize' => [ + 'type' => 'value', + 'suggested' => '>32M', + ], + 'post_max_size' => [ + 'type' => 'value', + 'suggested' => '>32M', + ], + ], + 'apache' => [ + 'mod_rewrite' => [ + 'server' => 'HTTP_MOD_REWRITE', + ], + ], + 'directories' => [ + 'backup', + 'files', + 'logs', + ], +]; diff --git a/legacy b/legacy index 01a293d36..5730434cc 160000 --- a/legacy +++ b/legacy @@ -1 +1 @@ -Subproject commit 01a293d3650fcf96d8617fab2b14a0c193bca6d9 +Subproject commit 5730434cc63c67880049f6adb116791dc8af8a5b diff --git a/modules/Aggiornamenti/Config/config.php b/modules/Aggiornamenti/Config/config.php new file mode 100644 index 000000000..b361208df --- /dev/null +++ b/modules/Aggiornamenti/Config/config.php @@ -0,0 +1,5 @@ + 'Aggiornamenti' +]; diff --git a/modules/Aggiornamenti/Database/Migrations/.gitkeep b/modules/Aggiornamenti/Database/Migrations/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Database/Seeders/.gitkeep b/modules/Aggiornamenti/Database/Seeders/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Database/Seeders/AggiornamentiDatabaseSeeder.php b/modules/Aggiornamenti/Database/Seeders/AggiornamentiDatabaseSeeder.php new file mode 100644 index 000000000..6b7f2a782 --- /dev/null +++ b/modules/Aggiornamenti/Database/Seeders/AggiornamentiDatabaseSeeder.php @@ -0,0 +1,21 @@ +call("OthersTableSeeder"); + } +} diff --git a/modules/Aggiornamenti/Database/factories/.gitkeep b/modules/Aggiornamenti/Database/factories/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Entities/.gitkeep b/modules/Aggiornamenti/Entities/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Http/Aggiornamento.php b/modules/Aggiornamenti/Http/Aggiornamento.php new file mode 100644 index 000000000..ea32e44b4 --- /dev/null +++ b/modules/Aggiornamenti/Http/Aggiornamento.php @@ -0,0 +1,466 @@ +directory = $directory ?: Zip::getExtractionDirectory(); + + if (!$this->isCoreUpdate() && empty($this->componentUpdates())) { + throw new InvalidArgumentException(); + } + } + + /** + * Pulisce la cartella di estrazione. + */ + public function delete() + { + delete($this->directory); + } + + /** + * Restituisce il percorso impostato per l'aggiornamento corrente. + * + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * Controlla se l'aggiornamento è di tipo globale. + * + * @return bool + */ + public function isCoreUpdate() + { + return file_exists($this->directory.'/VERSION'); + } + + public function getChangelog() + { + if ($this->isCoreUpdate()) { + $changelog = self::readChangelog($this->getDirectory(), Update::getVersion()); + } else { + $changelogs = []; + $elements = $this->componentUpdates(); + + $list = array_merge($elements['modules'], $elements['plugins']); + foreach ($list as $element) { + $changelog = self::readChangelog($element['path'], $element['version']); + + if (!empty($changelog)) { + $changelogs[] = ' +

'.$element['info']['name'].'

+ '.$changelog; + } + } + + $changelog = implode('
', $changelogs); + } + + return $changelog; + } + + public function getRequirements() + { + $file = $this->directory.'/config/requirements.php'; + $result = Requirements::getRequirementsList($file); + + return $result; + } + + public function getVersion() + { + return Update::getFile($this->getDirectory().'/VERSION'); + } + + /** + * Individua i componenti indipendenti che compongono l'aggiornamento. + * + * @return array + */ + public function componentUpdates() + { + if (!isset($this->components)) { + $finder = Finder::create() + ->files() + ->ignoreDotFiles(true) + ->ignoreVCS(true) + ->in($this->directory); + + $files = $finder->name('MODULE')->name('PLUGIN'); + + $results = []; + foreach ($files as $file) { + $is_module = basename($file->getRealPath()) == 'MODULE'; + $is_plugin = basename($file->getRealPath()) == 'PLUGIN'; + + $info = Ini::readFile($file->getRealPath()); + $installed = module($info['name']); + if ($is_module) { + $type = 'modules'; + } elseif ($is_plugin) { + $type = 'plugins'; + } + + if (!isset($results[$type])) { + $results[$type] = []; + } + $results[$type][] = [ + 'path' => dirname($file->getRealPath()), + 'config' => $file->getRealPath(), + 'is_installed' => !empty($installed), + 'current_version' => !empty($installed) ? $installed->version : null, + 'info' => $info, + ]; + } + + $this->components = $results; + } + + return $this->components; + } + + /** + * Effettua l'aggiornamento. + */ + public function execute() + { + if ($this->isCoreUpdate()) { + $this->executeCore(); + } else { + $components = $this->componentUpdates(); + + foreach ((array) $components['modules'] as $module) { + $this->executeModule($module); + } + + foreach ((array) $components['plugins'] as $plugin) { + $this->executeModule($plugin); + } + } + + $this->delete(); + } + + /** + * Completa l'aggiornamento globale secondo la procedura apposita. + */ + public function executeCore() + { + // Salva il file di configurazione + $config = file_get_contents(DOCROOT.'/config.inc.php'); + + // Copia i file dalla cartella temporanea alla root + copyr($this->directory, DOCROOT); + + // Ripristina il file di configurazione dell'installazione + file_put_contents(DOCROOT.'/config.inc.php', $config); + } + + /** + * Completa l'aggiornamento con le informazioni specifiche per i moduli. + * + * @param array $module + */ + public function executeModule($module) + { + // Informazioni dal file di configurazione + $info = $module['info']; + + // Informazioni aggiuntive per il database + $insert = [ + 'parent' => module($info['parent']), + 'icon' => $info['icon'], + ]; + + $id = $this->executeComponent($module['path'], 'modules', 'zz_modules', $insert, $info, $module['is_installed']); + + if (!empty($id)) { + // Fix per i permessi di amministratore + $element = Module::find($id); + + $element->groups()->syncWithoutDetaching($this->groups()); + } + } + + /** + * Instanzia un aggiornamento sulla base di uno zip indicato. + * Controlla inoltre che l'aggiornamento sia fattibile. + * + * @param string $file + * + * @throws DowngradeException + * @throws InvalidArgumentException + * + * @return static + */ + public static function make($file) + { + $extraction_dir = Zip::extract($file); + + $update = new static($extraction_dir); + + if ($update->isCoreUpdate()) { + $version = Update::getFile($update->getDirectory().'/VERSION'); + $current = Update::getVersion(); + + if (version_compare($current, $version) >= 0) { + $update->delete(); + + throw new DowngradeException(); + } + } else { + $components = $update->componentUpdates(); + + foreach ((array) $components['modules'] as $module) { + if (version_compare($module['current_version'], $module['info']['version']) >= 0) { + delete($module['path']); + } + } + + foreach ((array) $components['plugins'] as $plugin) { + if (version_compare($plugin['current_version'], $plugin['info']['version']) >= 0) { + delete($plugin['path']); + } + } + } + + // Instanzia nuovamente l'oggetto + return new static($extraction_dir); + } + + /** + * Controlla se è disponibile un aggiornamento nella repository GitHub. + * + * @return string|bool + */ + public static function isAvailable() + { + $release = self::getLastRelease(); + + $version = ltrim($release['tag_name'], 'v'); + $current = Update::getVersion(); + + $result = false; + if (version_compare($current, $version) < 0) { + $result = $version.($release['prerelease'] ? ' (beta)' : ''); + } + + // Aggiornamento cache dedicata + Cache::pool('Ultima versione di OpenSTAManager disponibile')->set($result); + + return $result; + } + + /** + * Scarica la release più recente (se presente). + * + * @return static + */ + public static function download() + { + if (self::isAvailable() === false) { + return null; + } + + $directory = Zip::getExtractionDirectory(); + $file = $directory.'/release.zip'; + directory($directory); + + $release = self::getLastRelease(); + self::getClient()->request('GET', $release['assets'][0]['browser_download_url'], ['sink' => $file]); + + $update = self::make($file); + delete($file); + + return $update; + } + + /** + * Restituisce i contenuti JSON dell'API del progetto. + * + * @return array + */ + public static function checkFiles() + { + $date = date('Y-m-d', filemtime(DOCROOT.'/core.php')); + + // Individuazione dei file tramite data di modifica + $files = Finder::create() + ->date('<= '.$date) + ->sortByName() + ->in(DOCROOT) + ->exclude([ + 'node_modules', + 'tests', + 'tmp', + 'vendor', + ]) + ->name('*.php') + ->notPath('*custom*') + ->files(); + + return iterator_to_array($files); + } + + /** + * Restituisce il changelog presente nel percorso indicato a partire dalla versione specificata. + * + * @param string $path + * @param string $version + * + * @return string + */ + protected static function readChangelog($path, $version = null) + { + $result = file_get_contents($path.'/CHANGELOG.md'); + + $start = strpos($result, '## '); + $result = substr($result, $start); + if (!empty($version)) { + $last = strpos($result, '## '.$version.' '); + + if ($last !== false) { + $result = substr($result, 0, $last); + } + } + + $result = Parsedown::instance()->text($result); + $result = str_replace(['h4>', 'h3>', 'h2>'], ['p>', 'b>', 'h4>'], $result); + + return $result; + } + + /** + * Completa l'aggiornamento del singolo componente come previsto dai parametri indicati. + * + * @param string $directory Percorso di copia dei contenuti + * @param string $table Tabella interessata dall'aggiornamento + * @param array $insert Informazioni per la registrazione + * @param array $info Contenuti della configurazione + * @param bool $is_installed + * + * @return int|null + */ + protected function executeComponent($path, $directory, $table, $insert, $info, $is_installed = false) + { + // Copia dei file nella cartella relativa + copyr($path, DOCROOT.'/'.$directory.'/'.$info['directory']); + + // Eventuale registrazione nel database + if (empty($is_installed)) { + $dbo = database(); + + $dbo->insert($table, array_merge($insert, [ + 'name' => $info['name'], + 'title' => !empty($info['title']) ? $info['title'] : $info['name'], + 'directory' => $info['directory'], + 'options' => $info['options'], + 'version' => $info['version'], + 'compatibility' => $info['compatibility'], + 'order' => 100, + 'default' => 0, + 'enabled' => 1, + ])); + + return $dbo->lastInsertedID(); + } + } + + /** + * Resituisce i permessi di default da impostare all'installazione del componente. + * + * @return array + */ + protected function groups() + { + if (!isset($this->groups)) { + $groups = Group::where('nome', 'Amministratori')->get(); + + $result = []; + foreach ($groups as $group) { + $result[$group->id] = [ + 'permission_level' => 'rw', + ]; + } + + $this->groups = $result; + } + + return $this->groups; + } + + /** + * Restituisce l'oggetto per la connessione all'API del progetto. + * + * @return Client + */ + protected static function getClient() + { + if (!isset(self::$client)) { + self::$client = new Client([ + 'base_uri' => 'https://api.github.com/repos/devcode-it/openstamanager/', + 'verify' => false, + ]); + } + + return self::$client; + } + + /** + * Restituisce i contenuti JSON dell'API del progetto. + * + * @return array + * @throws \GuzzleHttp\Exception\GuzzleException + */ + protected static function getLastRelease() + { + $response = self::getClient()->request('GET', 'releases'); + $body = $response->getBody(); + + // Impostazione per l'utilizzo o meno di versioni non stabili + $prerelease = intval(setting('Abilita canale pre-release per aggiornamenti')); + + // Ricerca dell'ultima release del tipi specificato + $releases = json_decode($body, true) ?: []; + foreach ($releases as $release) { + if ($release['prerelease'] == $prerelease) { + return $release; + } + } + + return null; + } +} diff --git a/modules/Aggiornamenti/Http/Controllers/AggiornamentiController.php b/modules/Aggiornamenti/Http/Controllers/AggiornamentiController.php new file mode 100644 index 000000000..39fb57842 --- /dev/null +++ b/modules/Aggiornamenti/Http/Controllers/AggiornamentiController.php @@ -0,0 +1,249 @@ + $update, + 'update_version' => $update->getVersion(), + 'update_requirements' => $update->getRequirements(), + ]; + + return view('aggiornamenti::update', $args); + } catch (InvalidArgumentException $e) { + } + + $custom = $this->customComponents(); + $tables = $this->customTables(); + + // Aggiornamenti + $alerts = []; + + if (!extension_loaded('zip')) { + $alerts[tr('Estensione ZIP')] = tr('da abilitare'); + } + + $upload_max_filesize = ini_get('upload_max_filesize'); + $upload_max_filesize = str_replace(['k', 'M'], ['000', '000000'], $upload_max_filesize); + // Dimensione minima: 32MB + if ($upload_max_filesize < 32000000) { + $alerts['upload_max_filesize'] = '32MB'; + } + + $post_max_size = ini_get('post_max_size'); + $post_max_size = str_replace(['k', 'M'], ['000', '000000'], $post_max_size); + // Dimensione minima: 32MB + if ($post_max_size < 32000000) { + $alerts['post_max_size'] = '32MB'; + } + + $args = [ + 'module' => module('Aggiornamenti'), + 'custom' => $custom, + 'tables' => $tables, + 'alerts' => $alerts, + 'enable_updates' => setting('Attiva aggiornamenti'), + 'requirements' => RequirementsController::getRequirementsList(), + ]; + + return view('aggiornamenti::index', $args); + } + + /** + * Controlla se il database presenta alcune sezioni personalizzate. + * + * @return array + */ + public function customStructure() + { + $results = []; + + $dirs = [ + 'modules', + 'templates', + 'plugins', + ]; + + // Controlli di personalizzazione fisica + foreach ($dirs as $dir) { + $files = glob(base_dir().'/'.$dir.'/*/custom/*.{php,html}', GLOB_BRACE); + $recursive_files = glob(base_dir().'/'.$dir.'/*/custom/**/*.{php,html}', GLOB_BRACE); + + $files = array_merge($files, $recursive_files); + + foreach ($files as $file) { + $file = str_replace(base_dir().'/', '', $file); + $result = explode('/custom/', $file)[0]; + + if (!in_array($result, $results)) { + $results[] = $result; + } + } + } + + // Gestione cartella include + $files = glob(base_dir().'/include/custom/*.{php,html}', GLOB_BRACE); + $recursive_files = glob(base_dir().'/include/custom/**/*.{php,html}', GLOB_BRACE); + + $files = array_merge($files, $recursive_files); + + foreach ($files as $file) { + $file = str_replace(base_dir().'/', '', $file); + $result = explode('/custom/', $file)[0]; + + if (!in_array($result, $results)) { + $results[] = $result; + } + } + + return $results; + } + + /** + * Controlla se il database presenta alcune sezioni personalizzate. + * + * @return array + */ + protected function customTables() + { + $tables = include base_dir().'/update/tables.php'; + + $names = []; + foreach ($tables as $table) { + $names[] = prepare($table); + } + + $database = database(); + + $results = $database->fetchArray('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '.prepare($database->getDatabaseName()).' AND TABLE_NAME NOT IN ('.implode(',', $names).") AND TABLE_NAME != 'updates'"); + + return array_column($results, 'TABLE_NAME'); + } + + /** + * Controlla se il database presenta alcune sezioni personalizzate. + * + * @return array + */ + protected function customDatabase() + { + $database = database(); + $modules = $database->fetchArray("SELECT name, CONCAT('modules/', directory) AS directory FROM zz_modules WHERE options2 != ''"); + $plugins = $database->fetchArray("SELECT name, CONCAT('plugins/', directory) AS directory FROM zz_plugins WHERE options2 != ''"); + + $results = array_merge($modules, $plugins); + + return $results; + } + + protected function customComponents() + { + $database_check = $this->customDatabase(); + $structure_check = $this->customStructure(); + + $list = []; + foreach ($database_check as $element) { + $pos = array_search($element['directory'], $structure_check); + + $list[] = [ + 'path' => $element['directory'], + 'database' => true, + 'directory' => $pos !== false, + ]; + + if ($pos !== false) { + unset($structure_check[$pos]); + } + } + + foreach ($structure_check as $element) { + $list[] = [ + 'path' => $element, + 'database' => false, + 'directory' => true, + ]; + } + + return $list; + } + + public function create(Request $request) + { + if (!setting('Attiva aggiornamenti')) { + die(tr('Accesso negato')); + } + + try { + $update = Aggiornamento::make($_FILES['blob']['tmp_name']); + } catch (DowngradeException $e) { + flash()->error(tr('Il pacchetto contiene una versione precedente del gestionale')); + } catch (InvalidArgumentException $e) { + flash()->error(tr('Il pacchetto contiene solo componenti già installate e aggiornate')); + } + } + + public function check(Request $request) + { + $result = Aggiornamento::isAvailable(); + $result = $result === false ? 'none' : $result; + + return $result; + } + + public function download(Request $request) + { + Aggiornamento::download(); + } + + public function execute(Request $request) + { + try { + $update = new Aggiornamento(); + + $update->execute(); + } catch (InvalidArgumentException $e) { + } + + $route = $this->router->urlFor('module', [ + 'module_id' => $args['module_id'], + ]); + $response = $response->withRedirect($route); + + return $response; + } + + public function cancel(Request $request) + { + try { + $update = new Aggiornamento(); + + $update->delete(); + } catch (InvalidArgumentException $e) { + } + + $route = $this->router->urlFor('module', [ + 'module_id' => $args['module_id'], + ]); + $response = $response->withRedirect($route); + + return $response; + } +} diff --git a/modules/Aggiornamenti/Http/Controllers/ControlliAggiuntiviController.php b/modules/Aggiornamenti/Http/Controllers/ControlliAggiuntiviController.php new file mode 100644 index 000000000..9aff8cef3 --- /dev/null +++ b/modules/Aggiornamenti/Http/Controllers/ControlliAggiuntiviController.php @@ -0,0 +1,14 @@ +. + */ + +namespace Modules\Aggiornamenti\Http; + +use GuzzleHttp\Client; +use Hooks\CachedManager; +use Update; + +/** + * Hook dedicato all'individuazione di nuove versioni del gestionale, pubblicate sulla repository ufficiale di GitHub. + */ +class UpdateHook extends CachedManager +{ + protected static $client = null; + + public function getCacheName() + { + return 'Ultima versione di OpenSTAManager disponibile'; + } + + public function cacheData() + { + return self::isAvailable(); + } + + public function response() + { + $update = $this->getCache()->content; + if ($update == Update::getVersion()) { + $update = null; + } + + $module = module('Aggiornamenti'); + $link = base_url().'/controller.php?id_module='.$module->id; + + $message = tr("E' disponibile la versione _VERSION_ del gestionale", [ + '_VERSION_' => $update, + ]); + + return [ + 'icon' => 'fa fa-download text-info', + 'link' => $link, + 'message' => $message, + 'show' => !empty($update), + ]; + } + + /** + * Controlla se è disponibile un aggiornamento nella repository GitHub. + * + * @return array|bool + */ + public static function isAvailable() + { + $api = self::getAPI(); + + if (!$api['prerelease'] or setting('Abilita canale pre-release per aggiornamenti')) { + $version[0] = ltrim($api['tag_name'], 'v'); + $version[1] = !empty($api['prerelease']) ? 'beta' : 'stabile'; + $current = Update::getVersion(); + + if (version_compare($current, $version[0]) < 0) { + return $version; + } + } + + return false; + } + + /** + * Restituisce l'oggetto per la connessione all'API del progetto. + * + * @return Client + */ + protected static function getClient() + { + if (!isset(self::$client)) { + self::$client = new Client([ + 'base_uri' => 'https://api.github.com/repos/devcode-it/openstamanager/', + 'verify' => false, + ]); + } + + return self::$client; + } + + /** + * Restituisce i contenuti JSON dell'API del progetto. + * + * @return array + */ + protected static function getAPI() + { + $response = self::getClient()->request('GET', 'releases'); + $body = $response->getBody(); + + return json_decode($body, true)[0]; + } +} diff --git a/modules/Aggiornamenti/Providers/AggiornamentiServiceProvider.php b/modules/Aggiornamenti/Providers/AggiornamentiServiceProvider.php new file mode 100644 index 000000000..23df46575 --- /dev/null +++ b/modules/Aggiornamenti/Providers/AggiornamentiServiceProvider.php @@ -0,0 +1,112 @@ +registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register config. + * + * @return void + */ + protected function registerConfig() + { + $this->publishes([ + module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'), + ], 'config'); + $this->mergeConfigFrom( + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower + ); + } + + /** + * Register views. + * + * @return void + */ + public function registerViews() + { + $viewPath = resource_path('views/modules/' . $this->moduleNameLower); + + $sourcePath = module_path($this->moduleName, 'Resources/views'); + + $this->publishes([ + $sourcePath => $viewPath + ], ['views', $this->moduleNameLower . '-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + } + + /** + * Register translations. + * + * @return void + */ + public function registerTranslations() + { + $langPath = resource_path('lang/modules/' . $this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + } + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return []; + } + + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (\Config::get('view.paths') as $path) { + if (is_dir($path . '/modules/' . $this->moduleNameLower)) { + $paths[] = $path . '/modules/' . $this->moduleNameLower; + } + } + return $paths; + } +} diff --git a/modules/Aggiornamenti/Providers/RouteServiceProvider.php b/modules/Aggiornamenti/Providers/RouteServiceProvider.php new file mode 100644 index 000000000..2f6c1d2ac --- /dev/null +++ b/modules/Aggiornamenti/Providers/RouteServiceProvider.php @@ -0,0 +1,69 @@ +mapApiRoutes(); + + $this->mapWebRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + * + * @return void + */ + protected function mapWebRoutes() + { + Route::middleware('web') + ->namespace($this->moduleNamespace) + ->group(module_path('Aggiornamenti', '/Routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->moduleNamespace) + ->group(module_path('Aggiornamenti', '/Routes/api.php')); + } +} diff --git a/modules/Aggiornamenti/Resources/assets/.gitkeep b/modules/Aggiornamenti/Resources/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Resources/assets/js/app.js b/modules/Aggiornamenti/Resources/assets/js/app.js new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Resources/assets/sass/app.scss b/modules/Aggiornamenti/Resources/assets/sass/app.scss new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Resources/lang/.gitkeep b/modules/Aggiornamenti/Resources/lang/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Resources/views/index.blade.php b/modules/Aggiornamenti/Resources/views/index.blade.php new file mode 100644 index 000000000..c765453f1 --- /dev/null +++ b/modules/Aggiornamenti/Resources/views/index.blade.php @@ -0,0 +1,222 @@ +@extends('modules.base') + +@section('module_content') + @if(!empty($custom) or !empty($tables)) + +
+
+

+ + {{ tr('Personalizzazioni') }} + +

+
+
+ + @if(!empty($custom)) + + + + + + + + @foreach($custom as $element) + + + + + + @endforeach +
{{ tr('Percorso') }}{{ tr('Cartella personalizzata') }}{{ tr('Database personalizzato') }}
{{ $element['path'] }}{{ $element['directory'] ? tr('Si') : tr('No') }}{{ $element['database'] ? tr('Si') : tr('No') }}
+ +

{{ tr("Si sconsiglia l'aggiornamento senza il supporto dell'assistenza ufficiale") }}.

+ @else +

{{ tr('Non ci sono strutture personalizzate') }}.

+ @endif + + @if(!empty($tables)) +
+ + {{ tr('Attenzione!') }} {{ tr('Ci sono delle tabelle non previste nella versione standard del gestionale: _LIST_', [ + '_LIST_' => implode(', ', $tables), + ]) }} +
+ @endif + +
+
+@endif + +@if(!empty($alerts)) +
+

{{ tr('Devi modificare il seguenti parametri del file di configurazione PHP (_FILE_) per poter caricare gli aggiornamenti', ['_FILE_' => 'php.ini']) }}:

+
    + @foreach($alerts as $key => $value) +
  • {{ $key }} = {{ $value }}
  • + @endforeach +
+
+@endif + + + +
+
+
+
+

+ {{ tr('Carica un aggiornamento') }} +

+
+ +
+
+ + + + {[ "type": "file", "name": "blob", "required": 1, "accept": ".zip" ]} + + +
+
+
+
+ +
+
+
+

+ {{ tr("Verifica l'integrità dell'intallazione") }} +

+
+
+ + + +
+
+
+ +
+
+
+

+ {{ tr('Ricerca aggiornamenti') }} +

+
+ +
+ @if(extension_loaded('curl')) + + @else + + @endif + + + + + +
+
+
+
+ +
+
+

{{ tr('Requisiti') }}

+ + @include('config.requirements-list') +
+@endsection diff --git a/modules/Aggiornamenti/Resources/views/update.blade.php b/modules/Aggiornamenti/Resources/views/update.blade.php new file mode 100644 index 000000000..e61b1a265 --- /dev/null +++ b/modules/Aggiornamenti/Resources/views/update.blade.php @@ -0,0 +1,137 @@ +{% extends "layouts/base.twig" %} + +{% block body_class %}hold-transition login-page{% endblock %} +{% block title %}{{ 'Aggiornamento'|trans }}{% endblock %} + +{% block body %} +
+ + +
+
+ + {% if update.isCoreUpdate() %} +

{{ "Il pacchetto selezionato contiene un aggiornamento dell'intero gestionale"|trans }}.

+

{{ "Si consiglia vivamente di effettuare un backup dell'installazione prima di procedere"|trans }}.

+ + + +
+
+ +

{{ 'OpenSTAManager versione _VERSION_'|trans({'_VERSION_': update.getVersion()}) }}

+ + {% include 'config/list.twig' with {requirements: update.getRequirements()} only %} + + {% else %} + {% set elements = update.componentUpdates() %} + +
+ + {{ 'Attenzione!'|trans }} {{ 'Verranno aggiornate le sole componenti del pacchetto che non sono già installate e aggiornate'|trans }}. +
+ + {% if elements.modules is not empty %} +

{{ 'Il pacchetto selezionato comprende i seguenti moduli'|trans }}:

+
    + + {% for element in elements.modules %} +
  • + {{ element['info']['version'] }} + + {% if element.is_installed %} + {{ 'Installato'|trans }}'; + {% endif %} + + {{ element['info']['name'] }} +
  • + {% endfor %} + +
+ {% endif %} + + {% if elements.plugins is not empty %} +

{{ 'Il pacchetto selezionato comprende i seguenti plugin'|trans }}:

+
    '; + + {% for element in elements.plugins %} +
  • + {{ element['info']['version'] }} + + {% if element.is_installed %} + {{ 'Installato'|trans }}'; + {% endif %} + + {{ element['info']['name'] }} +
  • + {% endfor %} + +
+ {% endif %} + {% endif %} + +
+ +
+ {% set changelog = update.getChangelog() %} + + {% if changelog is not empty %} + {{ changelog|raw }} + {% else %} +

{{ 'Nessuna changelog individuabile'|trans }}.

+ {% endif %} + +
+ +
+ +
+ +
+ +
+ +
+
+
+ + +{% endblock %} diff --git a/modules/Aggiornamenti/Routes/api.php b/modules/Aggiornamenti/Routes/api.php new file mode 100644 index 000000000..4a6bccb17 --- /dev/null +++ b/modules/Aggiornamenti/Routes/api.php @@ -0,0 +1,18 @@ +get('/aggiornamenti', function (Request $request) { + return $request->user(); +}); \ No newline at end of file diff --git a/modules/Aggiornamenti/Routes/web.php b/modules/Aggiornamenti/Routes/web.php new file mode 100644 index 000000000..c4c46c63e --- /dev/null +++ b/modules/Aggiornamenti/Routes/web.php @@ -0,0 +1,35 @@ +group(function () { + Route::get('', [AggiornamentiController::class, 'index']); + + Route::post('controllo-versione', [AggiornamentiController::class, 'check']) + ->name('controllo-versione'); + + Route::post('scarica-aggiornamento', [AggiornamentiController::class, 'download']) + ->name('scarica-aggiornamento'); + + Route::get('checksum', [ControlliAggiuntiviController::class, 'checksum']) + ->name('checksum'); + Route::get('database', [ControlliAggiuntiviController::class, 'database']) + ->name('database'); + Route::get('controlli', [ControlliAggiuntiviController::class, 'controlli']) + ->name('controlli'); + }); diff --git a/modules/Aggiornamenti/Tests/Feature/.gitkeep b/modules/Aggiornamenti/Tests/Feature/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/Tests/Unit/.gitkeep b/modules/Aggiornamenti/Tests/Unit/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/modules/Aggiornamenti/composer.json b/modules/Aggiornamenti/composer.json new file mode 100644 index 000000000..8a10ecb54 --- /dev/null +++ b/modules/Aggiornamenti/composer.json @@ -0,0 +1,24 @@ +{ + "name": "devcode-it/osm-aggiornamenti-module", + "description": "", + "authors": [{ + "name": "DevCode s.n.c.", + "email": "info@openstamanager.com" + }], + "require": { + "erusev/parsedown": "^1.7" + }, + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "Modules\\Aggiornamenti\\": "" + } + } +} diff --git a/modules/Aggiornamenti/module.json b/modules/Aggiornamenti/module.json new file mode 100644 index 000000000..f24dea0b8 --- /dev/null +++ b/modules/Aggiornamenti/module.json @@ -0,0 +1,13 @@ +{ + "name": "Aggiornamenti", + "alias": "aggiornamenti", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Aggiornamenti\\Providers\\AggiornamentiServiceProvider" + ], + "aliases": {}, + "files": [], + "requires": [] +} diff --git a/modules/Aggiornamenti/package.json b/modules/Aggiornamenti/package.json new file mode 100644 index 000000000..4599509fe --- /dev/null +++ b/modules/Aggiornamenti/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "scripts": { + "dev": "npm run development", + "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch-poll": "npm run watch -- --watch-poll", + "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", + "prod": "npm run production", + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" + }, + "devDependencies": { + "cross-env": "^7.0", + "laravel-mix": "^5.0.1", + "laravel-mix-merge-manifest": "^0.1.2" + } +} diff --git a/modules/Aggiornamenti/webpack.mix.js b/modules/Aggiornamenti/webpack.mix.js new file mode 100644 index 000000000..6d37eedd1 --- /dev/null +++ b/modules/Aggiornamenti/webpack.mix.js @@ -0,0 +1,14 @@ +const dotenvExpand = require('dotenv-expand'); +dotenvExpand(require('dotenv').config({ path: '../../.env'/*, debug: true*/})); + +const mix = require('laravel-mix'); +require('laravel-mix-merge-manifest'); + +mix.setPublicPath('../../public').mergeManifest(); + +mix.js(__dirname + '/Resources/assets/js/app.js', 'js/aggiornamenti.js') + .sass( __dirname + '/Resources/assets/sass/app.scss', 'css/aggiornamenti.css'); + +if (mix.inProduction()) { + mix.version(); +} diff --git a/modules/listini/bulk.php b/modules/listini/bulk.php deleted file mode 100644 index d2825c5da..000000000 --- a/modules/listini/bulk.php +++ /dev/null @@ -1,26 +0,0 @@ -query('UPDATE mg_prezzi_articoli SET sconto_percentuale='.prepare($sconto).' WHERE id='.prepare($id_record)); - } - flash()->info(tr('Sconti modificati correttamente!')); - break; -} - -return [ - 'aggiorna-listino' => [ - 'text' => ''.tr('Modifica sconto').'', - 'data' => [ - 'title' => tr('Inserisci lo sconto per questi articoli'), - 'msg' => '{[ "type": "text", "label": "'.tr('Nuovo sconto').'","icon-after":"%", "name": "sconto" ]}', - 'button' => tr('Modifica'), - 'class' => 'btn btn-lg btn-warning', - 'blank' => false, - ], - ], -]; diff --git a/modules/ordini/add_preventivo.php b/modules/ordini/add_preventivo.php deleted file mode 100644 index 9b7f28137..000000000 --- a/modules/ordini/add_preventivo.php +++ /dev/null @@ -1,85 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -use Modules\Ordini\Ordine; -use Modules\Preventivi\Preventivo; - -$documento_finale = Ordine::find($id_record); -$dir = $documento_finale->direzione; - -$id_documento = get('id_documento'); -if (!empty($id_documento)) { - $documento = Preventivo::find($id_documento); - - $options = [ - 'op' => 'add_documento', - 'type' => 'preventivo', - 'button' => tr('Aggiungi'), - 'documento' => $documento, - 'documento_finale' => $documento_finale, - 'tipo_documento_finale' => Ordine::class, - ]; - - echo App::load('importa.php', [], $options, true); - - return; -} - -$id_anagrafica = $documento_finale->idanagrafica; - -echo ' -
-
- {[ "type": "select", "label": "'.tr('Preventivo').'", "name": "id_documento", "ajax-source": "preventivi", "select-options": {"idanagrafica": '.$id_anagrafica.', "stato": "is_fatturabile=1 OR is_completato"} ]} -
-
- -
- -
- -
- '.tr('Caricamento in corso').'... -
'; - -$file = basename(__FILE__); -echo ' - - -'; diff --git a/modules/ordini/bulk.php b/modules/ordini/bulk.php deleted file mode 100644 index 6643676c2..000000000 --- a/modules/ordini/bulk.php +++ /dev/null @@ -1,133 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -use Modules\Ordini\Ordine; -use Modules\Fatture\Fattura; -use Modules\Fatture\Stato; -use Modules\Fatture\Tipo; - -$module_fatture = 'Fatture di vendita'; - - -// Segmenti -$id_fatture = Modules::get($module_fatture)['id']; -if (!isset($_SESSION['module_'.$id_fatture]['id_segment'])) { - $segments = Modules::getSegments($id_fatture); - $_SESSION['module_'.$id_fatture]['id_segment'] = isset($segments[0]['id']) ? $segments[0]['id'] : null; -} -$id_segment = $_SESSION['module_'.$id_fatture]['id_segment']; - -switch (post('op')) { - case 'crea_fattura': - $documenti = collect(); - $numero_totale = 0; - $descrizione_tipo = 'Fattura immediata di vendita'; - - $tipo_documento = Tipo::where('descrizione', $descrizione_tipo)->first(); - - $stato_documenti_accodabili = Stato::where('descrizione', 'Bozza')->first(); - $accodare = post('accodare'); - - $data = date('Y-m-d'); - $id_segment = post('id_segment'); - - // Lettura righe selezionate - foreach ($id_records as $id) { - $documento_import = Ordine::find($id); - $anagrafica = $documento_import->anagrafica; - $id_anagrafica = $anagrafica->id; - - // Proseguo solo se i documenti scelti sono fatturabili - $ordine = $dbo->fetchOne('SELECT or_statiordine.descrizione AS stato FROM or_ordini LEFT JOIN or_statiordine ON or_ordini.idstatoordine=or_statiordine.id WHERE or_ordini.id='.prepare($id))['stato']; - if (!in_array($ordine, ['Fatturato', 'Evaso', 'Bozza', 'In attesa di conferma', 'Annullato'])) { - $righe = $documento_import->getRighe(); - if (!empty($righe)) { - ++$numero_totale; - - // Ricerca fattura per anagrafica tra le registrate - $fattura = $documenti->first(function ($item, $key) use ($id_anagrafica) { - return $item->anagrafica->id == $id_anagrafica; - }); - - // Ricerca fattura per anagrafica se l'impostazione di accodamento è selezionata - if (!empty($accodare) && empty($fattura)) { - $fattura = Fattura::where('idanagrafica', $id_anagrafica) - ->where('idstatodocumento', $stato_documenti_accodabili->id) - ->where('idtipodocumento', $tipo_documento->id) - ->first(); - - if (!empty($fattura)) { - $documenti->push($fattura); - } - } - - // Creazione fattura per anagrafica - if (empty($fattura)) { - $fattura = Fattura::build($anagrafica, $tipo_documento, $data, $id_segment); - $documenti->push($fattura); - } - - // Inserimento righe - foreach ($righe as $riga) { - $qta = $riga->qta_rimanente; - - if ($qta > 0) { - //Fix per idconto righe fattura - $riga->idconto = $fattura->idconto; - $copia = $riga->copiaIn($fattura, $qta); - - // Aggiornamento seriali dalla riga dell'ordine - if ($copia->isArticolo()) { - $copia->serials = $riga->serials; - } - } - } - } - } - } - - if ($numero_totale > 0) { - flash()->info(tr('_NUM_ ordini fatturati!', [ - '_NUM_' => $numero_totale, - ])); - } else { - flash()->warning(tr('Nessun ordine fatturato!')); - } - break; -} -if ($module['name'] == 'Ordini cliente') { -$operations['crea_fattura'] = [ - 'text' => ' '.tr('Fattura _TYPE_', ['_TYPE_' => strtolower($module['name'])]), - 'data' => [ - 'title' => tr('Fatturare i _TYPE_ selezionati?', ['_TYPE_' => strtolower($module['name'])]), - 'msg' => '{[ "type": "checkbox", "label": "'.tr('Aggiungere alle _TYPE_ non ancora emesse?', ['_TYPE_' => strtolower($module_fatture)]).'", "placeholder": "'.tr('Aggiungere alle _TYPE_ nello stato bozza?', ['_TYPE_' => strtolower($module_fatture)]).'", "name": "accodare" ]} -
{[ "type": "select", "label": "'.tr('Sezionale').'", "name": "id_segment", "required": 1, "values": "query=SELECT id, name AS descrizione FROM zz_segments WHERE id_module=\''.$id_fatture.'\' AND is_fiscale = 1 ORDER BY name", "value": "'.$id_segment.'" ]}', - 'button' => tr('Procedi'), - 'class' => 'btn btn-lg btn-warning', - 'blank' => false, - ], - ]; -} -if($operations){ - return $operations; -} else { - return; -} \ No newline at end of file diff --git a/modules/tipi_documento/actions.php b/modules/tipi_documento/actions.php deleted file mode 100644 index 59a708bea..000000000 --- a/modules/tipi_documento/actions.php +++ /dev/null @@ -1,115 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -switch (filter('op')) { - case 'update': - $descrizione = filter('descrizione'); - $dir = filter('dir'); - $codice_tipo_documento_fe = filter('codice_tipo_documento_fe'); - - if (isset($descrizione) && isset($dir) && isset($codice_tipo_documento_fe)) { - if ($dbo->fetchNum('SELECT * FROM `co_tipidocumento` WHERE `dir`='.prepare($dir).' AND `codice_tipo_documento_fe`='.prepare($codice_tipo_documento_fe).' AND `id`!='.prepare($id_record)) == 0) { - - $predefined = post('predefined'); - if (!empty($predefined)) { - $dbo->query('UPDATE co_tipidocumento SET predefined = 0 WHERE dir = '.prepare($dir)); - } - - $dbo->update('co_tipidocumento', [ - 'descrizione' => $descrizione, - 'dir' => $dir, - 'codice_tipo_documento_fe' => $codice_tipo_documento_fe, - 'help' => filter('help'), - 'predefined' => $predefined, - 'enabled' => post('enabled'), - ], ['id' => $id_record]); - - flash()->info(tr('Salvataggio completato!')); - } else { - flash()->error(tr("E' già presente una tipologia di _TYPE_ con la stessa combinazione di direzione e tipo documento FE", [ - '_TYPE_' => 'tipo documento', - ])); - } - } else { - flash()->error(tr('Ci sono stati alcuni errori durante il salvataggio')); - } - - break; - - case 'add': - $descrizione = filter('descrizione'); - $dir = filter('dir'); - $codice_tipo_documento_fe = filter('codice_tipo_documento_fe'); - - if (isset($descrizione) && isset($dir) && isset($codice_tipo_documento_fe)) { - if ($dbo->fetchNum('SELECT * FROM `co_tipidocumento` WHERE `dir`='.prepare($dir).' AND `codice_tipo_documento_fe`='.prepare($codice_tipo_documento_fe)) == 0) { - $dbo->insert('co_tipidocumento', [ - 'descrizione' => $descrizione, - 'dir' => $dir, - 'codice_tipo_documento_fe' => $codice_tipo_documento_fe, - ]); - $id_record = $dbo->lastInsertedID(); - - if (isAjaxRequest()) { - echo json_encode(['id' => $id_record, 'text' => $descrizione]); - } - - flash()->info(tr('Aggiunta nuova tipologia di _TYPE_', [ - '_TYPE_' => 'tipo documento', - ])); - } else { - flash()->error(tr("E' già presente una tipologia di _TYPE_ con la stessa combinazione di direzione e tipo documento FE", [ - '_TYPE_' => 'tipo documento', - ])); - } - } else { - flash()->error(tr('Ci sono stati alcuni errori durante il salvataggio')); - } - - break; - - case 'delete': - - $documenti = $dbo->fetchNum('SELECT id FROM co_documenti WHERE idtipodocumento ='.prepare($id_record)); - - if (isset($id_record) && empty($documenti)) { - $dbo->query('DELETE FROM `co_tipidocumento` WHERE `id`='.prepare($id_record)); - flash()->info(tr('Tipologia di _TYPE_ eliminata con successo.', [ - '_TYPE_' => 'tipo documento', - ])); - } else { - - $dbo->update('co_tipidocumento', [ - 'deleted_at' => date(), - 'predefined' => 0, - 'enabled' => 0, - ], ['id' => $id_record]); - - flash()->info(tr('Tipologia di _TYPE_ eliminata con successo.', [ - '_TYPE_' => 'tipo documento', - ])); - - - //flash()->error(tr('Sono presenti dei documenti collegati a questo tipo documento')); - } - - break; -} \ No newline at end of file diff --git a/modules/tipi_documento/add.php b/modules/tipi_documento/add.php deleted file mode 100644 index 5dd4e2052..000000000 --- a/modules/tipi_documento/add.php +++ /dev/null @@ -1,46 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -?>
- - - -
-
- {[ "type": "text", "label": "", "name": "descrizione", "required": 1 ]} -
- -
- {[ "type": "select", "label": "", "name": "dir", "values": "list=\"\": \"Non specificato\", \"entrata\": \"\", \"uscita\": \"\"", "required": 1 ]} -
- -
- {[ "type": "select", "label": "", "name": "codice_tipo_documento_fe", "values": "query=SELECT codice AS id, CONCAT_WS(' - ', codice, descrizione) AS descrizione FROM fe_tipi_documento", "required": 1 ]} -
-
- - -
-
- -
-
-
\ No newline at end of file diff --git a/modules/tipi_documento/ajax/select.php b/modules/tipi_documento/ajax/select.php deleted file mode 100644 index 9946a3e69..000000000 --- a/modules/tipi_documento/ajax/select.php +++ /dev/null @@ -1,37 +0,0 @@ -. - */ - -include_once __DIR__.'/../../../core.php'; - -switch ($resource) { - case 'tipi_documento': - $query = 'SELECT id, descrizione FROM co_tipidocumento |where| ORDER BY descrizione ASC'; - - $where[] = 'co_tipidocumento.enabled = 1'; - $where[] = 'dir='.$superselect['dir']; - - foreach ($elements as $element) { - $filter[] = 'id='.prepare($element); - } - if (!empty($search)) { - $search_fields[] = 'descrizione LIKE '.prepare('%'.$search.'%'); - } - - break; -} diff --git a/modules/tipi_documento/edit.php b/modules/tipi_documento/edit.php deleted file mode 100644 index ab5d4a437..000000000 --- a/modules/tipi_documento/edit.php +++ /dev/null @@ -1,78 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -?>
- - - - - -
-
- {[ "type": "text", "label": "", "name": "descrizione", "required": 1, "value": "$descrizione$" ]} -
- -
- {[ "type": "select", "label": "", "name": "dir", "value": "$dir$", "values": "list=\"\": \"Non specificato\", \"entrata\": \"\", \"uscita\": \"\"", "required": 1 ]} -
- -
- {[ "type": "select", "label": "", "name": "codice_tipo_documento_fe", "value": "$codice_tipo_documento_fe$", "values": "query=SELECT codice AS id, CONCAT_WS(' - ', codice, descrizione) AS descrizione FROM fe_tipi_documento", "required": 1 ]} -
- -
- {[ "type": "checkbox", "label": "", "name": "predefined", "value": "", "help":"." ]} -
- -
- {[ "type": "checkbox", "label": "", "name": "enabled", "value": "" ]} -
- -
- {[ "type": "checkbox", "label": "", "name": "reversed", "value": "", "readonly": 1 ]} -
- -
- {[ "type": "text", "label": "", "name": "help", "value": "$help$" ]} -
- -
- - -
- -fetchNum('SELECT id FROM co_documenti WHERE idtipodocumento='.prepare($id_record)); - -if (!empty($numero_documenti)) { - echo ' -
- '.tr('Ci sono _NUM_ documenti collegati', [ - '_NUM_' => $numero_documenti, - ]).'. -
'; -} -?> - - - - diff --git a/modules/tipi_documento/init.php b/modules/tipi_documento/init.php deleted file mode 100644 index e68f56050..000000000 --- a/modules/tipi_documento/init.php +++ /dev/null @@ -1,24 +0,0 @@ -. - */ - -include_once __DIR__.'/../../core.php'; - -if (isset($id_record)) { - $record = $dbo->fetchOne('SELECT * FROM `co_tipidocumento` WHERE id='.prepare($id_record)); -} diff --git a/modules_statuses.json b/modules_statuses.json new file mode 100644 index 000000000..d3f8f809b --- /dev/null +++ b/modules_statuses.json @@ -0,0 +1,3 @@ +{ + "Aggiornamenti": true +} diff --git a/resources/views/components/inputs/checkbox.blade.php b/resources/views/components/inputs/checkbox.blade.php index bea415e32..fe6e79748 100644 --- a/resources/views/components/inputs/checkbox.blade.php +++ b/resources/views/components/inputs/checkbox.blade.php @@ -8,7 +8,7 @@ 'value' => $value, 'required' => $required, 'data-parsley-errors-container' => '#'.$unique_id.'-errors' - ]) }} onchange="$(this).parent().find(\'[type = hidden]\').val(+this.checked).trigger(\'change\')"/> + ]) }} onchange="$(this).parent().find('[type = hidden]').val(+this.checked).trigger('change')"/>