diff --git a/composer.json b/composer.json
index c2d50339..780aeca1 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,7 @@
"require": {
"php": "^8.2",
"illuminate/support": "^11.0",
- "uspdev/forms": "^0.6",
+ "uspdev/forms": "*",
"spatie/laravel-activitylog": "^4.9",
"spatie/laravel-permission": "^6.15",
"symfony/workflow": "^7.1",
@@ -30,11 +30,27 @@
"graphp/graph": "^1@dev",
"graphp/graphviz": "1.x-dev"
},
+ "repositories":
+ [
+ {
+ "type": "path",
+ "url": "../forms",
+ "options":{"symlink": true}
+ }
+ ],
"extra": {
"laravel": {
"providers": [
"Uspdev\\Workflow\\WorkflowServiceProvider"
]
}
- }
-}
+ },
+ "repositories":
+ [
+ {
+ "type": "path",
+ "url": "../forms",
+ "options": {"symlink": true}
+ }
+ ]
+}
\ No newline at end of file
diff --git a/config/workflow.php b/config/uspdev-workflow.php
similarity index 80%
rename from config/workflow.php
rename to config/uspdev-workflow.php
index a508df11..a8b282b5 100644
--- a/config/workflow.php
+++ b/config/uspdev-workflow.php
@@ -3,5 +3,7 @@
// Caminho de armazenamento das definições de workflow
// Pega do .env, na variável WORKFLOW_STORAGE_PATH, mas usa 'storage/app/workflow-definitions' como caminho default
return [
- 'storagePath' => env('WORKFLOW_STORAGE_PATH', storage_path('app/workflow-definitions')),
+ 'storagePath' => env('WORKFLOW_STORAGE_PATH', storage_path('app/workflowsJson')),
+
+ 'prefix' => 'uspdev-workflow',
];
diff --git a/resources/views/show/list-bckps.blade.php b/resources/views/show/list-bckps.blade.php
new file mode 100644
index 00000000..41788e08
--- /dev/null
+++ b/resources/views/show/list-bckps.blade.php
@@ -0,0 +1,56 @@
+@extends('uspdev-forms::layouts.app')
+
+@section('header')
+@endsection
+
+@section('content')
+
+
@include('uspdev-workflow::show.partials.tabs')
+
+
+
+
+
+
+ | Nome |
+ Descrição |
+ Ações |
+
+
+
+ @foreach ($workflowDefinitions as $workflowDefinition)
+ @php
+ $count = is_dir(config('uspdev-workflow.storagePath')) ? count(array_filter(scandir(config('uspdev-workflow.storagePath')), fn($filename) => str_contains($filename,$workflowDefinition->name))) : 0;
+ @endphp
+
+ |
+ {{ $workflowDefinition->name }}
+
+ {{--
+ Exibe o número de backups da definição que existem atualmente
+ --}}
+ {{ $count }}
+
+ |
+
+ {{ $workflowDefinition->description }}
+ |
+
+ @include('uspdev-workflow::show.partials.bckpgen-btn')
+ @includeWhen($count > 0,'uspdev-workflow::show.partials.bckplist-btn')
+ |
+
+ @endforeach
+
+
+
+
+
+ @include('uspdev-workflow::show.partials.globalbckp-btn')
+
+@endsection
diff --git a/resources/views/show/list-def-bckps.blade.php b/resources/views/show/list-def-bckps.blade.php
new file mode 100644
index 00000000..d9302c29
--- /dev/null
+++ b/resources/views/show/list-def-bckps.blade.php
@@ -0,0 +1,46 @@
+@extends('uspdev-forms::layouts.app')
+
+@section('content')
+
+
+
+
+
@include('uspdev-workflow::show.partials.bckpgen-btn')
+
+
+
+
+ | Data de criação |
+ Última modificação |
+ Ações |
+
+
+
+ @foreach ($time_data as $created_time => $updt_time)
+
+ |
+ {{ str_replace('_',' - ',str_replace('-','/',$created_time)) }}
+ |
+
+ {{ str_replace('_',' - ',str_replace('-','/',$updt_time)) }}
+ |
+
+ @include('uspdev-workflow::show.partials.restore-btn')
+ @include('uspdev-workflow::show.partials.bckpremove-btn')
+ |
+
+ @endforeach
+
+
+
+
+
+ @includeWhen(count($time_data) > 0,'uspdev-workflow::show.partials.defbckpremoveall-btn')
+
+@endsection
diff --git a/resources/views/show/list-defs.blade.php b/resources/views/show/list-defs.blade.php
new file mode 100644
index 00000000..8a00bbb1
--- /dev/null
+++ b/resources/views/show/list-defs.blade.php
@@ -0,0 +1,44 @@
+@extends('uspdev-forms::layouts.app')
+
+@section('header')
+@endsection
+
+@section('content')
+
+@include('uspdev-workflow::show.partials.tabs')
+
+
+
+
+
+
+ | Nome |
+ Descrição |
+ Ações |
+
+
+
+ @foreach ($workflowDefinitions as $workflowDefinition)
+
+ |
+ {{ $workflowDefinition->name }}
+ |
+
+ {{ $workflowDefinition->description }}
+ |
+
+ @include('uspdev-workflow::show.partials.edit-btn')
+ |
+
+ @endforeach
+
+
+
+
+@endsection
diff --git a/resources/views/show/partials/bckpgen-btn.blade.php b/resources/views/show/partials/bckpgen-btn.blade.php
new file mode 100644
index 00000000..5741f5fd
--- /dev/null
+++ b/resources/views/show/partials/bckpgen-btn.blade.php
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/resources/views/show/partials/bckplist-btn.blade.php b/resources/views/show/partials/bckplist-btn.blade.php
new file mode 100644
index 00000000..bf3e66dc
--- /dev/null
+++ b/resources/views/show/partials/bckplist-btn.blade.php
@@ -0,0 +1,3 @@
+
+ Listar backups
+
diff --git a/resources/views/show/partials/bckpremove-btn.blade.php b/resources/views/show/partials/bckpremove-btn.blade.php
new file mode 100644
index 00000000..52b1f100
--- /dev/null
+++ b/resources/views/show/partials/bckpremove-btn.blade.php
@@ -0,0 +1,3 @@
+
+ Remover backup
+
diff --git a/resources/views/show/partials/bckpremoveall-btn.blade.php b/resources/views/show/partials/bckpremoveall-btn.blade.php
new file mode 100644
index 00000000..d3511fe5
--- /dev/null
+++ b/resources/views/show/partials/bckpremoveall-btn.blade.php
@@ -0,0 +1,3 @@
+
+ Remover todos os backups
+
diff --git a/resources/views/show/partials/defbckpremoveall-btn.blade.php b/resources/views/show/partials/defbckpremoveall-btn.blade.php
new file mode 100644
index 00000000..2dcf593e
--- /dev/null
+++ b/resources/views/show/partials/defbckpremoveall-btn.blade.php
@@ -0,0 +1,3 @@
+
+ Remover todos os backups da definição
+
diff --git a/resources/views/show/partials/edit-btn.blade.php b/resources/views/show/partials/edit-btn.blade.php
new file mode 100644
index 00000000..cbee075f
--- /dev/null
+++ b/resources/views/show/partials/edit-btn.blade.php
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/resources/views/show/partials/globalbckp-btn.blade.php b/resources/views/show/partials/globalbckp-btn.blade.php
new file mode 100644
index 00000000..eb8603ee
--- /dev/null
+++ b/resources/views/show/partials/globalbckp-btn.blade.php
@@ -0,0 +1,3 @@
+
+ Gerar backups de todas as definições
+
diff --git a/resources/views/show/partials/restore-btn.blade.php b/resources/views/show/partials/restore-btn.blade.php
new file mode 100644
index 00000000..a6f22f1d
--- /dev/null
+++ b/resources/views/show/partials/restore-btn.blade.php
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/resources/views/show/partials/tabs.blade.php b/resources/views/show/partials/tabs.blade.php
new file mode 100644
index 00000000..06c59dcd
--- /dev/null
+++ b/resources/views/show/partials/tabs.blade.php
@@ -0,0 +1,8 @@
+
+ -
+ Definitions
+
+ -
+ Backup
+
+
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
new file mode 100644
index 00000000..ef9beb64
--- /dev/null
+++ b/routes/web.php
@@ -0,0 +1,16 @@
+ config('uspdev-workflow.prefix'), 'middleware' => ['web']], function () {
+
+ Route::get('/backups', [WorkflowBackupController::class, 'backups_index'])->name('workflows.backups-idx');
+ Route::get('/backups/gen-backups', [WorkflowBackupController::class, 'bckp_gen_all'])->name('workflows.gen-all-backups');
+ Route::get('/backups/{workflowDefinition}/gen-backup', [WorkflowBackupController::class, 'def_bckp_gen'])->name('workflows.gen-backup');
+ Route::get('/backups/{workflowDefinition}/list',[WorkflowBackupController::class, 'def_bckp_list'])->name('workflows.def-backup-list');
+ Route::get('/backups/{workflowDefinition}/remove/{created_time}', [WorkflowBackupController::class, 'remove_bckp'])->name('workflows.def-remove-bckp');
+ Route::get('/backups/{workflowDefinition}/remove-all', [WorkflowBackupController::class, 'remove_def_bckps'])->name('workflows.def-rmv-bckps');
+ Route::get('/backups/remove-all', [WorkflowBackupController::class, 'remove_all_bckps'])->name('workflows.rmv-bckps');
+ Route::get('/backups/{workflowDefinition}/restore/{created_time}', [WorkflowBackupController::class,'restore_backup'])->name('workflows.restore-bckp');
+});
\ No newline at end of file
diff --git a/src/Console/Commands/WorkflowSync.php b/src/Console/Commands/WorkflowSync.php
new file mode 100644
index 00000000..ad1c0cf6
--- /dev/null
+++ b/src/Console/Commands/WorkflowSync.php
@@ -0,0 +1,37 @@
+option('path') ?: config('uspdev-workflow.storagePath');
+ $this->info('Sicnronizando workflows do caminho: ' . $path);
+
+ // Chama o serviço de sincronizar no caminho especificado
+ app(WorkflowSyncService::class)->workflow_sync($path);
+ }
+}
diff --git a/src/Http/Controllers/WorkflowBackupController.php b/src/Http/Controllers/WorkflowBackupController.php
new file mode 100644
index 00000000..7523b0d4
--- /dev/null
+++ b/src/Http/Controllers/WorkflowBackupController.php
@@ -0,0 +1,229 @@
+ $workflowDefinitions, 'activeTab' => 'backup']);
+ }
+
+ /**
+ * Gera o backup de uma definição de workflow
+ * @param WorkflowDefinition $workflowDefinition
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function def_bckp_gen(WorkflowDefinition $workflowDefinition)
+ {
+ // Recupera o diretório para armazenar os backups do workflow
+ $file_dir = config('uspdev-workflow.storagePath');
+
+ // Cria o diretório caso ele não exista
+ if(!is_dir($file_dir))
+ {
+ mkdir($file_dir,0777);
+ }
+
+ // Forma o caominho do arquivo na forma defname@horariocriado.json
+ $file_path = $file_dir . '/' . $workflowDefinition['name'] . '@' . now()->format('d-m-Y_H:i:s') . '.json';
+
+ // Cria o arquivo para escrita
+ try
+ {
+ $json_file = fopen($file_path,'w');
+ }
+ catch(Exception $e)
+ {
+ print("Erro ao abrir arquivo: " . $e);
+ return;
+ }
+
+ // Gera o json a partir da definição do workflow
+ $json_encoded = json_encode($workflowDefinition->definition,JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
+
+ // Escreve o json da definição no arquivo
+ fwrite($json_file,$json_encoded);
+
+ fclose($json_file);
+
+ // Redireciona e retorna com uma mensagem de sucesso
+ return redirect()->back()->with('alert-success','Backup de ' . $workflowDefinition->name . ' gerado com sucesso.');
+ }
+
+ /**
+ * Gera o backup de todas as definições persistidas no banco de dados
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function bckp_gen_all()
+ {
+ // Recupera todas as definições do banco de dados
+ $wrkflw_defs = WorkflowDefinition::all();
+
+ // Gera um backup para cada definição
+ foreach($wrkflw_defs as $wrkflow_def)
+ {
+ $this->def_bckp_gen($wrkflow_def);
+ }
+
+ // Retorna a mensagem de sucesso
+ return redirect()->back()->with('alert-success','Backups de todas as definições gerados com sucesso.');
+ }
+
+ /**
+ * Lista todos os backups de uma definição
+ * @param WorkflowDefinition $workflowDefinition
+ * @return \Illuminate\Contracts\View\View
+ */
+ public function def_bckp_list(WorkflowDefinition $workflowDefinition)
+ {
+ // Recupera o diretório de armazenamento dos backups
+ $file_dir = config('uspdev-workflow.storagePath');
+
+ // Captura todos os arquivos em um array associativo
+ $files = scandir($file_dir);
+
+ // Filtra pelo nome da definição
+ $files = array_filter($files, function($filename) use ($workflowDefinition)
+ {
+ return str_contains($filename,$workflowDefinition->name);
+ });
+
+ $time_data = [];
+
+ // Percorre todos os arquivos e extrai informações - data de criação e data da ultima modificação
+ foreach($files as $filename)
+ {
+ $created_time = explode('@',$filename)[1];
+ $created_time = explode('.',$created_time)[0];
+
+ $last_mod_time = date('d-m-Y_H:i:s', filemtime($file_dir .'/'. $filename));
+
+ // Grava no formato: tempo_criado => tempo_ultima_mod
+ $time_data[$created_time] = $last_mod_time;
+ }
+
+ return view('uspdev-workflow::show.list-def-bckps', ['workflowDefinition' => $workflowDefinition, 'time_data' => $time_data]);
+ }
+
+ /**
+ * Remove um backup da definição, remontando o nome através do nome da definição e do tempo de criação do backup
+ * @param WorkflowDefinition $workflowDefinition
+ * @param string $created_time
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function remove_bckp(WorkflowDefinition $workflowDefinition, string $created_time)
+ {
+ // Remonta o tempo de criação para voltar ao formato Y-m-d_H:i:s
+ $created_time = str_replace(' - ','_',$created_time);
+ $created_time = str_replace('/','-',$created_time);
+
+ // Remonta o nome do arquivo
+ $filename = $workflowDefinition->name . '@' . $created_time . '.json';
+
+ // Remonta o caminho completo do arquivo
+ $filepath = config('uspdev-workflow.storagePath') . '/' . $filename;
+
+ // Caso o arquivo exista no caminho remontado anteriormente, o remove
+ if(File::exists($filepath))
+ {
+ File::delete($filepath);
+ return redirect()->back()->with('alert-warning','Backup ' . $filename . ' removido com sucesso.' );
+ }
+
+ // Caso contrário, exibe uma mensagem de erro.
+ else
+ {
+ return redirect()->back()->with('alert-danger', 'Impossível remover ' . $filename .' => arquivo não existe.');
+ }
+ }
+
+ /**
+ * Remove todos os backups existentes de uma definição
+ * @param WorkflowDefinition $workflowDefinition
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function remove_def_bckps(WorkflowDefinition $workflowDefinition)
+ {
+ // Recupera o diretório em que os backups são salvos
+ $file_dir = config('uspdev-workflow.storagePath');
+
+ // Filtra os arquivos pelos nomes que contém o nome da definição
+ $files = array_filter(scandir($file_dir),function($filename) use ($workflowDefinition){return str_contains($filename,$workflowDefinition->name);});
+
+ // Percorre todos os arquivos
+ foreach($files as $filename)
+ {
+ // Reconstrói o caminho dos arquivo
+ $filepath = $file_dir . '/' . $filename;
+
+ // Verifica a existência e deleta em caso afirmativo
+ if(File::exists($filepath));
+ {
+ File::delete($filepath);
+ }
+ }
+
+ return redirect()->back()->with('alert-warning', 'Backups de ' . $workflowDefinition->name . ' removidos com sucesso.');
+ }
+ /**
+ * Remove todos os backups existentes, de todas as definições
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function remove_all_bckps()
+ {
+ // Recupera o diretório em que os arquivos são salvos
+ $file_dir = config('uspdev-workflow.storagePath');
+
+ // Filtra para obter apenas os arquivos .json(evita '.' e '..', além de possível lixo)
+ $files = array_filter(scandir($file_dir), function($file) { return str_contains($file,'.json'); });
+
+ // Percorre todos os arquivos do diretório
+ foreach($files as $filename)
+ {
+ // Reconstrói o caminho do arquivo
+ $filepath = $file_dir . '/' . $filename;
+
+ // Verifica se o mesmo existe, e o deleta em caso afirmativo
+ if(File::exists($filepath))
+ {
+ File::delete($filepath);
+ }
+ }
+
+ return redirect()->back()->with('alert-warning', 'Backups removidos com sucesso.');
+ }
+
+ /**
+ * Restaura um backup (persiste no banco de dados)
+ * @param WorkflowDefinition $workflowDefinition
+ * @param string $created_time
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function restore_backup(WorkflowDefinition $workflowDefinition, string $created_time)
+ {
+ // Remonta o caminho completo do arquivo
+ $file_dir = config('uspdev-workflow.storagePath');
+ $filename = $workflowDefinition->name . '@' . $created_time;
+ $filepath = $file_dir . '/' . $filename . '.json';
+
+ // Chama o comando 'workflow:sync', passando o caminho desejado como option {--path}
+ Artisan::call('workflow:sync', ['--path' => $filepath]);
+
+ // Retorna uma mensagem de sucesso.
+ return redirect()->back()->with('alert-success','Backup ' . $filename . ' restaurado com sucesso');
+ }
+}
\ No newline at end of file
diff --git a/src/Services/WorkflowSyncService.php b/src/Services/WorkflowSyncService.php
new file mode 100644
index 00000000..94b0b8af
--- /dev/null
+++ b/src/Services/WorkflowSyncService.php
@@ -0,0 +1,106 @@
+ $decoded_json['name']],
+ [
+ 'name' => $decoded_json['name'],
+ 'description' => $decoded_json['description'],
+ 'definition' => $decoded_json,
+ ]);
+ }
+
+ /**
+ * Sincroniza todo um diretório de backup
+ * @param string $dir_path
+ * @return void
+ */
+ private function sync_dir(string $dir_path)
+ {
+ // Recupera todos os backups existentes no diretório especificado
+ $all_files = scandir($dir_path);
+
+ // Vetor auxiliar para recuperar o backup mais recentemente modificado
+ $most_recent_updt = [];
+
+ // Percorre todos os arquivos existentes no diretório
+ foreach($all_files as $filename)
+ {
+ if(str_contains($filename,'.json'))
+ {
+
+ // Recupera o nome da definição do backup
+ $curr_def = explode('@',$filename)[0];
+
+ // Remonta o caminho completo do arquivo
+ $filepath = $dir_path . '/' . $filename;
+
+ // Se ainda não existe a chave, cria e associa ao caminho atual
+ if(!array_key_exists($curr_def,$most_recent_updt))
+ {
+ $most_recent_updt[$curr_def] = $filepath;
+ }
+
+ // Se existe, e o tempo de modificação do elemento contido no vetor for menor que o do arquivo atual
+ //(significa que a última modificação do contido foi anterior à do arquivo atual), faz a substituição
+ elseif(filemtime($most_recent_updt[$curr_def]) < filemtime($filepath))
+ {
+ $most_recent_updt[$curr_def] = $filepath;
+ }
+ }
+ }
+
+ // Para cada elemento dentro do vetor de 'mais recentemente atualizado', sincroniza o arquivo de backup
+ foreach($most_recent_updt as $to_restore)
+ {
+ $this->sync_file($to_restore);
+ }
+ }
+
+ /**
+ * Sincroniza o arquivo/diretório no arquivo passado.
+ * @param string $path
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function workflow_sync(string $path)
+ {
+ // Caso seja um diretório
+ if(is_dir($path))
+ {
+ $this->sync_dir($path);
+ }
+
+ // Caso seja arquivo
+ elseif(is_file($path))
+ {
+ $this->sync_file($path);
+ }
+
+ // Mensagem de erro
+ else
+ {
+ return redirect()->back()->with('alert-danger','O caminho indicado: ' . $path . ' não representa um diretório nem um arquivo existente.');
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WorkflowServiceProvider.php b/src/WorkflowServiceProvider.php
index 9178a5c6..77499371 100644
--- a/src/WorkflowServiceProvider.php
+++ b/src/WorkflowServiceProvider.php
@@ -1,6 +1,7 @@
publishes([
- __DIR__.'/../config/workflow.php' => config_path('workflow.php'),
+ __DIR__.'/../config/workflow.php' => config_path('uspdev-workflow.php'),
], 'workflow-config');
// Publish migrations
@@ -22,8 +23,14 @@ public function boot()
__DIR__ . '/../database/migrations/' => database_path('migrations'),
], 'workflow-migrations');
- // Load migrations
- // $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
+ $this->loadViewsFrom(__DIR__ . '/../resources/views','uspdev-workflow');
+
+ $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
+
+ // Publica o comando de WorkflowSync
+ $this->commands([
+ WorkflowSync::class,
+ ]);
}
/**
@@ -34,7 +41,7 @@ public function boot()
public function register()
{
$this->mergeConfigFrom(
- __DIR__ . '/../config/workflow.php', 'uspdev-workflow'
+ __DIR__ . '/../config/uspdev-workflow.php', 'uspdev-workflow'
);
}
}