# Änderungen an zentrum
+# V0.1.3 Process ExportController drawio2db.py
+
+- ExportController: $_SERVER['documentroot] replaced by FileHelper::documentRoot()
+- new module Process
+- new drawio2db.py
+
# V0.1.2 Layout: Startmenü oder Startmenü-Public
- User: neu: isGuest()
- zentrum.blade: ohne Anmeldung: keine "Verwaltung", "Anmelden"-Button
'patterns' => '',
];
}
- $path = $_SERVER['DOCUMENT_ROOT'] . "/export";
+ $path = FileHelper::documentRoot() . "/export";
$patterns = str_replace(['*', '?'], ['.*', '.'], $fields['patterns']);
$patterns = '/^' . auth()->user()->name . "\\.$patterns/";
$files = FileHelper::fileInfoList($path, $patterns);
'filename' => '',
];
}
- $path = $_SERVER['DOCUMENT_ROOT'] . "/temp";
+ $path = FileHelper::documentRoot() . "/temp";
if ($request->btnSubmit === 'btnUpload') {
if (($file = $request->file('file')) != null) {
$relativePath = basename($path);
public function removeFile(string $nodeEncoded, Request $request)
{
$node = FileHelper::decodeUrl($nodeEncoded);
- $full = $_SERVER['DOCUMENT_ROOT'] . "/export/$node";
+ $full = FileHelper::documentRoot() . "/export/$node";
if (file_exists($full)) {
unlink($full);
}
--- /dev/null
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Models\File;
+use App\Models\Change;
+use App\Helpers\Helper;
+use App\Models\Process;
+use App\Helpers\DbHelper;
+use App\Models\SProperty;
+use App\Helpers\FileHelper;
+use App\Helpers\Pagination;
+use App\Helpers\ViewHelper;
+use Illuminate\Http\Request;
+use App\Helpers\ContextLaraKnife;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Route;
+use Illuminate\Support\Facades\Validator;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+
+class ProcessController extends Controller
+{
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create(Request $request)
+ {
+ if ($request->btnSubmit === 'btnCancel') {
+ $rc = redirect('/process-index');
+ } else {
+ $fields = $request->all();
+ if (count($fields) === 0) {
+ $fields = [
+ 'division_scope' => '',
+ 'activity_scope' => '',
+ 'file' => '',
+ 'owner_id' => auth()->id()
+ ];
+ }
+ $context = new ContextLaraKnife($request, $fields);
+ $optionsDivision = SProperty::optionsByScope('division', $fields['division_scope'], '');
+ $optionsActivity = SProperty::optionsByScope('activity', $fields['activity_scope'], '');
+ $rc = view('process.create', [
+ 'context' => $context,
+ 'optionsDivision' => $optionsDivision,
+ 'optionsActivity' => $optionsActivity
+ ]);
+ }
+ return $rc;
+ }
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit(Process $process, Request $request)
+ {
+ if ($request->btnSubmit === 'btnCancel') {
+ $rc = redirect('/process-index');
+ } else {
+ $fields = $request->all();
+ if (count($fields) === 0) {
+ $fields = [
+ 'serialno' => Process::nextSerialNo(),
+ 'division_scope' => $process->division_scope,
+ 'activity_scope' => $process->activity_scope,
+ 'path' => $process->path,
+ 'title' => $process->title,
+ 'roles' => $process->roles,
+ 'subprocesses' => $process->subprocesses,
+ 'datasources' => $process->datasources,
+ 'texts' => $process->texts,
+ 'info' => $process->info,
+ 'owner_id' => $process->owner_id
+ ];
+ }
+ $context = new ContextLaraKnife($request, $fields, $process);
+ $optionsDivision = SProperty::optionsByScope('division', $process->division_scope, '');
+ $optionsActivity = SProperty::optionsByScope('activity', $process->activity_scope, '');
+ $optionsOwner = DbHelper::comboboxDataOfTable('users', 'name', 'id', $fields['owner_id'], __('<Please select>'));
+ $rc = view('process.edit', [
+ 'context' => $context,
+ 'optionsDivision' => $optionsDivision,
+ 'optionsActivity' => $optionsActivity,
+ 'optionsOwner' => $optionsOwner,
+ ]);
+ }
+ return $rc;
+ }
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy(Process $process, Request $request)
+ {
+ if ($request->btnSubmit === 'btnDelete') {
+ $process->delete();
+ }
+ return redirect('/process-index');
+ }
+ /**
+ * Display the database records of the resource.
+ */
+ public function index(Request $request)
+ {
+ if ($request->btnSubmit === 'btnNew') {
+ return redirect('/process-create');
+ } else {
+ $sql = "
+SELECT t0.*,
+ t1.name as division,
+ t2.name as activity,
+ t3.name as owner
+FROM processes t0
+LEFT JOIN sproperties t1 ON t1.id=t0.division_scope
+LEFT JOIN sproperties t2 ON t2.id=t0.activity_scope
+LEFT JOIN users t3 ON t3.id=t0.owner_id
+";
+ $parameters = [];
+ $fields = $request->all();
+ if (count($fields) == 0) {
+ $fields = [
+ 'division' => '',
+ 'activity' => '',
+ 'owner' => '',
+ 'serialno' => '',
+ 'path' => '',
+ 'title' => '',
+ 'roles' => '',
+ 'subprocesses' => '',
+ 'datasources' => '',
+ 'texts' => '',
+ 'info' => '',
+ '_sortParams' => 'id:asc'
+ ];
+ }
+ $conditions = [];
+ ViewHelper::addConditionComparison($fields, $conditions, $parameters, 'division_scope', 'division');
+ ViewHelper::addConditionComparison($fields, $conditions, $parameters, 'activity_scope', 'activity');
+ ViewHelper::addConditionComparison($fields, $conditions, $parameters, 'owner_id', 'owner');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'path');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'title');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'roles');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'subprocesses');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'datasources');
+ ViewHelper::addConditionPattern($fields, $conditions, $parameters, 'texts');
+ $sql = DbHelper::addConditions($sql, $conditions);
+ $sql = DbHelper::addOrderBy($sql, $fields['_sortParams']);
+ $pagination = new Pagination($sql, $parameters, $fields);
+ $records = $pagination->records;
+ $optionsDivision = SProperty::optionsByScope('division', $fields['division'], 'all');
+ $optionsActivity = SProperty::optionsByScope('activity', $fields['activity'], 'all');
+ $optionsOwner = DbHelper::comboboxDataOfTable('users', 'name', 'id', $fields['owner'], __('all'));
+ $context = new ContextLaraKnife($request, $fields);
+ return view('process.index', [
+ 'context' => $context,
+ 'records' => $records,
+ 'optionsDivision' => $optionsDivision,
+ 'optionsActivity' => $optionsActivity,
+ 'optionsOwner' => $optionsOwner,
+ 'pagination' => $pagination
+ ]);
+ }
+ }
+ public function parseDrawIoXml(string $filename, array &$fields): void
+ {
+ $script = FileHelper::baseDirectory() . '/scripts/drawio2db.py';
+ $output = [];
+ $fullPath = storage_path('app/public/' . $filename);
+ exec("$script $fullPath", $output);
+ $multiline = '';
+ $matches = null;
+ $target = null;
+ $fields['roles'] = $fields['subprocesses'] = $fields['datasources'] = $fields['texts'] = null;
+ foreach ($output as $line) {
+ if (str_starts_with($line, 'Title: ')) {
+ $fields['title'] = substr($line, 7);
+ } elseif (str_starts_with($line, ' ')) {
+ $multiline .= substr($line, 2) . "\n";
+ } elseif (preg_match('/^(Roles|SubProcesses|Data Sources|Texts)/', $line, $matches)) {
+ switch ($target) {
+ case 'Roles':
+ $fields['roles'] = $multiline;
+ break;
+ case 'SubProcesses':
+ $fields['subprocesses'] = $multiline;
+ break;
+ case 'Data Sources':
+ $fields['datasources'] = $multiline;
+ break;
+ case 'Texts':
+ $fields['texts'] = $multiline;
+ break;
+ default:
+ break;
+ }
+ $multiline = '';
+ $target = $matches[1];
+ }
+ }
+ if ($target === 'Texts') {
+ $fields['texts'] = $multiline;
+ }
+ }
+
+ /**
+ * Returns the validation rules.
+ * @return array<string, string> The validation rules.
+ */
+ private function rules(bool $isCreate = false): array
+ {
+ $rc = [
+ 'serialno' => $isCreate ? '' : 'required',
+ 'division_scope' => $isCreate ? '' : 'required',
+ 'activity_scope' => $isCreate ? '' : 'required',
+ 'path' => $isCreate ? '' : 'required',
+ 'title' => $isCreate ? '' : 'required',
+ 'roles' => '',
+ 'subprocesses' => '',
+ 'datasources' => '',
+ 'texts' => '',
+ 'info' => '',
+ 'owner_id' => $isCreate ? '' : 'required'
+ ];
+ return $rc;
+ }
+ public static function routes()
+ {
+ Route::get('/process-index', [ProcessController::class, 'index'])->middleware('auth');
+ Route::post('/process-index', [ProcessController::class, 'index'])->middleware('auth');
+ Route::get('/process-create', [ProcessController::class, 'create'])->middleware('auth');
+ Route::put('/process-store', [ProcessController::class, 'store'])->middleware('auth');
+ Route::post('/process-edit/{process}', [ProcessController::class, 'edit'])->middleware('auth');
+ Route::get('/process-edit/{process}', [ProcessController::class, 'edit'])->middleware('auth');
+ Route::post('/process-update/{process}', [ProcessController::class, 'update'])->middleware('auth');
+ Route::get('/process-show/{process}/delete', [ProcessController::class, 'show'])->middleware('auth');
+ Route::delete('/process-show/{process}/delete', [ProcessController::class, 'destroy'])->middleware('auth');
+ }
+ /**
+ * Display the specified resource.
+ */
+ public function show(Process $process, Request $request)
+ {
+ if ($request->btnSubmit === 'btnCancel') {
+ $rc = redirect('/process-index')->middleware('auth');
+ } else {
+ $optionsDivision = SProperty::optionsByScope('division', $process->division_scope, '');
+ $optionsActivity = SProperty::optionsByScope('activity', $process->activity_scope, '');
+ $optionsOwner = DbHelper::comboboxDataOfTable('users', 'name', 'id', $process->owner_id, __(''));
+ $context = new ContextLaraKnife($request, null, $process);
+ $rc = view('process.show', [
+ 'context' => $context,
+ 'optionsDivision' => $optionsDivision,
+ 'optionsActivity' => $optionsActivity,
+ 'optionsOwner' => $optionsOwner,
+ 'mode' => 'delete'
+ ]);
+ }
+ return $rc;
+ }
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store(Request $request)
+ {
+ $rc = null;
+ if ($request->btnSubmit === 'btnStore') {
+ $fields = $request->all();
+ $validator = Validator::make($fields, $this->rules(true));
+ if ($validator->fails()) {
+ $rc = back()->withErrors($validator)->withInput();
+ } else {
+ $validated = $validator->validated();
+ $fullName = $this->storeFile($request);
+ if ($fullName == null) {
+ $rc = back()->withErrors($validator)->withInput();
+ } else {
+ $this->parseDrawIoXml($fullName, $validated);
+ $process = Process::create($validated);
+ Change::createFromFields($validated, Change::$CREATE, 'Process', $process->id);
+ $rc = redirect('/process-edit/' . $process->id);
+ }
+ }
+ }
+ if ($rc == null) {
+ $rc = redirect('/process-index');
+ }
+ return $rc;
+ }
+ /**
+ * Stores the file and returns the full file name.
+ * @param \Illuminate\Http\Request $request the request information
+ * @return string|null the full file name or null if no file was uploaded
+ */
+ public function storeFile(Request $request): ?string
+ {
+ $rc = null;
+ $file = $request->file();
+ $file = $request->file('file');
+ if ($file != null) {
+ $name = $file->getClientOriginalName();
+ $filename = session('userName') . '_' . strval(time()) . '!' . $name;
+ $rc = FileHelper::storeFile($request, 'file', $filename);
+ }
+ return $rc;
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update(Process $process, Request $request)
+ {
+ $rc = null;
+ if ($request->btnSubmit === 'btnStore') {
+ $fields = $request->all();
+ $validator = Validator::make($fields, $this->rules(false));
+ if ($validator->fails()) {
+ $rc = back()->withErrors($validator)->withInput();
+ } else {
+ $validated = $validator->validated();
+ $validated['path'] = strip_tags($validated['path']);
+ $validated['roles'] = strip_tags($validated['roles']);
+ $validated['subprocesses'] = strip_tags($validated['subprocesses']);
+ $validated['datasources'] = strip_tags($validated['datasources']);
+ $validated['texts'] = strip_tags($validated['texts']);
+ $validated['info'] = strip_tags($validated['info']);
+ $process->update($validated);
+ }
+ }
+ if ($rc == null) {
+ $rc = redirect('/process-index');
+ }
+ return $rc;
+ }
+}
--- /dev/null
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Support\Facades\DB;
+use Hamatoma\Laraknife\ViewHelpers;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+
+class Process extends Model
+{
+ use HasFactory;
+ protected $table = 'processes';
+ protected $fillable = [
+ 'serialno',
+ 'division_scope',
+ 'activity_scope',
+ 'path',
+ 'title',
+ 'roles',
+ 'subprocesses',
+ 'datasources',
+ 'texts',
+ 'info',
+ 'owner_id'
+ ];
+ public static function nextSerialNo(): int
+ {
+ $max = Process::max('serialno') ?? 0;
+ return $max + 1;
+ }
+}
"dist": {
"type": "path",
"url": "../laraknife",
- "reference": "aa3c5c562078b91aba72a3407b98d08461a36a09"
+ "reference": "919c89cab04807f66b7f4a4881139e8df5b687b9"
},
"require-dev": {
"phpunit/phpunit": "11.0.x-dev"
},
{
"name": "monolog/monolog",
- "version": "3.8.1",
+ "version": "3.9.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4"
+ "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
- "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6",
+ "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6",
"shasum": ""
},
"require": {
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
- "source": "https://github.com/Seldaek/monolog/tree/3.8.1"
+ "source": "https://github.com/Seldaek/monolog/tree/3.9.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2024-12-05T17:15:07+00:00"
+ "time": "2025-03-24T10:02:05+00:00"
},
{
"name": "nesbot/carbon",
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::create('processes', function (Blueprint $table) {
+ $table->id();
+ $table->timestamps();
+ $table->integer('serialno')->nullable()->unique();
+ $table->integer('division_scope');
+ $table->integer('activity_scope');
+ $table->text('path')->nullable();
+ $table->string('title')->unique();
+ $table->text('roles')->nullable();
+ $table->text('subprocesses')->nullable();
+ $table->text('datasources')->nullable();
+ $table->text('texts')->nullable();
+ $table->text('info')->nullable();
+ $table->foreignId('owner_id')->nullable()->references('id')->on('users');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('processes');
+ }
+};
--- /dev/null
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\Module;
+use App\Models\Menuitem;
+use App\Models\SProperty;
+use Illuminate\Database\Seeder;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+
+class ProcessSeeder extends Seeder
+{
+ /**
+ * Run the database seeds.
+ */
+ public function run(): void
+ {
+ Menuitem::insertIfNotExists('processes', 'bi bi-diagram-3');
+ Module::insertIfNotExists('Process');
+
+ SProperty::insertIfNotExists(2051, 'division', 'Piepmatz', 10, 'PM');
+ SProperty::insertIfNotExists(2052, 'division', 'Verein', 20, 'V');
+ SProperty::insertIfNotExists(2053, 'division', 'Unverpackt', 30, 'UV');
+ SProperty::insertIfNotExists(2054, 'division', 'FairFashion', 40, 'FF');
+ SProperty::insertIfNotExists(2055, 'division', 'Allgäu Fairnetzt', 50, 'AF');
+ SProperty::insertIfNotExists(2056, 'division', 'CommYounitySpace', 60, 'CS');
+
+ SProperty::insertIfNotExists(2071, 'activity', 'Fibu', 10, 'F');
+ SProperty::insertIfNotExists(2072, 'activity', 'Einkauf', 20, 'EK');
+ SProperty::insertIfNotExists(2073, 'activity', 'Lager', 30, 'LG');
+ SProperty::insertIfNotExists(2074, 'activity', 'Reklamation', 40, 'R');
+ SProperty::insertIfNotExists(2075, 'activity', 'Ladenbetrieb', 50, 'LD');
+ SProperty::insertIfNotExists(2076, 'activity', 'Mitgliederverwaltung', 60, 'M');
+ SProperty::insertIfNotExists(2077, 'activity', 'Events', 70, 'EV');
+ SProperty::insertIfNotExists(2078, 'activity', 'Öffentlichkeitsarbeit', 80, 'Ö');
+ SProperty::insertIfNotExists(2079, 'activity', 'Spendenverwaltung', 90, 'SV');
+ SProperty::insertIfNotExists(2070, 'activity', 'Fördermittelaquise', 100, 'FA');
+ SProperty::insertIfNotExists(2081, 'activity', 'Bildung', 110, 'BI');
+ SProperty::insertIfNotExists(2082, 'activity', 'Webseitenverwaltung', 120, 'WS');
+ SProperty::insertIfNotExists(2083, 'activity', 'interne Kommunikation', 130, 'IK');
+ SProperty::insertIfNotExists(2084, 'activity', 'externe Kommunikation', 140, 'EK');
+ SProperty::insertIfNotExists(2085, 'activity', 'Zugangsverwaltung', 150, 'ZV');
+ }
+}
--- /dev/null
+@extends('layouts.backend')
+
+@section('content')
+ <form enctype="multipart/form-data" id="process-create" action="/process-store" method="POST">
+ @csrf
+ @method('PUT')
+ <x-laraknife.panels.create title="Import einer Prozessbeschreibung">
+ <x-laraknife.forms.file position="first" name="file" label="Drawio-Datei" width2="4" />
+ <x-laraknife.forms.const-text position="alone" name="message" text="{{ $context->valueOf('message') }}"
+ width1="0" width2="12" />
+ <input type="hidden" name="owner_id" value="{{ $context->valueOf('owner_id') }}">
+ <x-laraknife.forms.combobox position="first" name="division_scope" label="Division" :options="$optionsDivision"
+ width2="4" />
+ <x-laraknife.forms.combobox position="last" name="activity_scope" label="Activity" :options="$optionsActivity"
+ width2="4" />
+ </x-laraknife.panels.create>
+ </form>
+@endsection
--- /dev/null
+@extends('layouts.backend')
+
+@section('content')
+ <form id="process-edit" action="/process-update/{{ $context->model->id }}" method="POST">
+ @csrf
+ <x-laraknife.panels.edit title="Änderung einer Prozessbeschreibung">
+ <x-laraknife.forms.string position="alone" name="title" label="Title" value="{{ $context->valueOf('title') }}"
+ width2="10" attribute="readonly" />
+ <x-laraknife.forms.string type="number" position="first" name="serialno" label="Nummer"
+ value="{{ $context->valueOf('serialno') }}" width2="4" />
+ <x-laraknife.forms.combobox position="last" name="owner_id" label="Besitzer" :options="$optionsOwner" width2="4"
+ attribute="readonly" />
+ <x-laraknife.forms.combobox position="first" name="division_scope" label="Abteilung" :options="$optionsDivision"
+ width2="4" />
+ <x-laraknife.forms.combobox position="last" name="activity_scope" label="Tätigkeitsfeld" :options="$optionsActivity"
+ width2="4" />
+ <x-laraknife.forms.string position="alone" name="path" label="Pfad" value="{{ $context->valueOf('path') }}"
+ width2="10" />
+ <x-laraknife.forms.text position="first" name="roles" label="Rollen" value="{{ $context->valueOf('roles') }}"
+ width2="4" rows="4" attribute="readonly" />
+ <x-laraknife.forms.text position="last" name="subprocesses" label="Sub-Prozesse"
+ value="{{ $context->valueOf('subprocesses') }}" width2="4" rows="4" attribute="readonly" />
+ <x-laraknife.forms.text position="first" name="texts" label="Texte" value="{{ $context->valueOf('texts') }}"
+ width2="4" rows="4" attribute="readonly" />
+ <x-laraknife.forms.text position="last" name="datasources" label="Datenquellen"
+ value="{{ $context->valueOf('datasources') }}" width2="4" rows="4" attribute="readonly" />
+ <x-laraknife.forms.text position="alone" name="info" label="Info" value="{{ $context->valueOf('info') }}"
+ width2="10" rows="2" />
+ </x-laraknife.panels.edit>
+ </form>
+@endsection
--- /dev/null
+@extends('layouts.backend')
+
+@section('content')
+<form id="process-index" action="/process-index" method="POST">
+ @csrf
+ <x-laraknife.panels.index title="Prozesse">
+ <x-laraknife.panels.filter legend="{{ $pagination->legendText() }}">
+ <x-laraknife.forms.combobox position="first" name="division" label="Abteilung" :options="$optionsDivision" class="lkn-autoupdate" width2="4" />
+ <x-laraknife.forms.combobox position="last" name="owner" label="Besitzer" :options="$optionsOwner" class="lkn-autoupdate" width2="4" />
+ <x-laraknife.forms.combobox position="alone" name="activity" label="Tätigkeitsfeld" :options="$optionsActivity" class="lkn-autoupdate" width2="4" />
+ <x-laraknife.forms.string position="first" name="title" label="Titel" value="{{ $context->valueOf('title') }}" width2="4" />
+ <x-laraknife.forms.string position="last" name="path" label="Pfad" value="{{ $context->valueOf('path') }}" width2="4" />
+ <x-laraknife.forms.string position="first" name="roles" label="Rolle" value="{{ $context->valueOf('roles') }}" width2="4" />
+ <x-laraknife.forms.string position="last" name="subprocesses" label="Subprozess" value="{{ $context->valueOf('subprocesses') }}" width2="4" />
+ <x-laraknife.forms.string position="first" name="texts" label="Text" value="{{ $context->valueOf('texts') }}" width2="4" />
+ <x-laraknife.forms.string position="last" name="datasources" label="Datenquelle" value="{{ $context->valueOf('datasources') }}" width2="4" />
+ </x-laraknife.panels.filter>
+ <x-laraknife.panels.index-button buttonType="new"/>
+ <x-laraknife.panels.sortable-table :context="$context" :pagination="$pagination">
+ <thead>
+ <tr>
+ <th></th>
+ <th sortId="serialno">Nummer</th>
+ <th sortId="title">Titel</th>
+ <th sortId="division">Abteilung</th>
+ <th sortId="activity">Tätigkeitsfeld</th>
+ <th sortId="path">Pfad</th>
+ <th sortId="owner">Besitzer</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+@foreach ($records as $process)
+ <tr>
+ <td><x-laraknife.icons.change-record module="process" no="{{ $process->id }}" /></td>
+ <td>{{$process->serialno}}</td>
+ <td>{{$process->title}}</td>
+ <td>{{ __($process->division) }}</td>
+ <td>{{ __($process->activity) }}</td>
+ <td>{{$process->path}}</td>
+ <td>{{$process->owner}}</td>
+ <td><x-laraknife.icons.delete-record module="process" no="{{ $process->id }}" /></td>
+ </tr>
+@endforeach
+ </tbody>
+ </x-laraknife.panels.sortable-table>
+ </x-laraknife.panels.index>
+</form>
+@endsection
--- /dev/null
+@extends('layouts.backend')
+
+@section('content')
+ <form id="process-show" action="/process-show/{{ $context->model->id }}/{{ $mode }}" method="POST">
+ @csrf
+ @if ($mode === 'delete')
+ @method('DELETE')
+ @endif
+ <x-laraknife.panels.show
+ title="{{ __($mode !== 'delete' ? 'Eine Prozessbeschreibung' : 'Löschen einer Prozessbeschreibung') }}"
+ mode="{{ $mode }}">
+ <x-laraknife.forms.string position="alone" name="title" label="Titel" value="{{ $context->valueOf('title') }}"
+ width2="10" attribute="readonly" />
+ <x-laraknife.forms.string position="first" name="serialno" label="Nummer"
+ value="{{ $context->valueOf('serialno') }}" width2="4" attribute="readonly" />
+ <x-laraknife.forms.combobox position="last" name="owner_id" label="Besitzer" :options="$optionsOwner" width2="4"
+ attribute="readonly" />
+ <x-laraknife.forms.combobox position="first" name="division_scope" label="Abteilung" :options="$optionsDivision"
+ width2="4" attribute="readonly" />
+ <x-laraknife.forms.combobox position="last" name="activity_scope" label="Tätigkeitsfeld" :options="$optionsActivity"
+ width2="4" attribute="readonly" />
+ <x-laraknife.forms.string position="alone" name="path" label="Pfad" value="{{ $context->valueOf('path') }}"
+ width2="10" attribute="readonly" />
+ <x-laraknife.forms.text position="first" name="roles" label="Rollen" value="{{ $context->valueOf('roles') }}"
+ width2="4" attribute="readonly" rows="4" />
+ <x-laraknife.forms.text position="last" name="subprocesses" label="Subprozesse"
+ value="{{ $context->valueOf('subprocesses') }}" width2="4" attribute="readonly" rows="4" />
+ <x-laraknife.forms.text position="first" name="texts" label="Texte" value="{{ $context->valueOf('texts') }}"
+ width2="4" attribute="readonly" rows="4" />
+ <x-laraknife.forms.text position="last" name="datasources" label="Datenquellen"
+ value="{{ $context->valueOf('datasources') }}" width2="4" attribute="readonly" rows="4" />
+ <x-laraknife.forms.text position="alone" name="info" label="Info" value="{{ $context->valueOf('info') }}"
+ width2="10" attribute="readonly" rows="2" />
+ </x-laraknife.panels.show>
+ </form>
+@endsection
use App\Http\Controllers\MandatorController;
use App\Http\Controllers\MenuitemController;
use App\Http\Controllers\SPropertyController;
+use App\Http\Controllers\ProcessController;
use App\Http\Controllers\TransactionController;
if (User::isGuest()) {
MandatorController::routes();
AccountController::routes();
TransactionController::routes();
+ProcessController::routes();
--- /dev/null
+#! /usr/bin/python3
+import xml.etree.ElementTree as ET
+import sys
+import os
+import re
+
+
+class AnalyseDrawio:
+ """
+ A tool for analyzing a drawio file (XML)
+ Prints the database relevant information.
+ """
+
+ def __init__(self, xml_file: str):
+ """
+ Constructor
+ :param xml_file: the name of the drawio file to analyze
+ """
+ self.xml_file = xml_file
+ self.actions = 0
+ self.comments = 0
+ self.decisions = 0
+ self.delays = 0
+ self.documents = 0
+ self.manual_inputs = 0
+ self.start_end = 0
+ self.data_sources = []
+ self.subProcesses = {}
+ self.texts = []
+ self.title = ""
+ self.roles = {}
+ self.regColor = re.compile(r"fillColor=.*?(#[0-9A-Fa-f]{6})")
+ self.role_to_color = {
+ "Ladenteam": "#E6FFCC",
+ "Eventteam": "#CCFF99",
+ "Beirät:innen": "#CCE5FF",
+ "Vorstandschaft": "#99CCFF",
+ "Buchhaltung": "#FFCCFF",
+ "Einkauf": "#FF99CC",
+ "Postfachverwaltung": "#FFE6CC",
+ "Mitgliederverwaltung": "#FFCC99",
+ "Admins": "#E9E0CB",
+ "Ehrenamtlicher": "#FF9999",
+ "Mitglieder": "#FF6666",
+ "Community": "#FF3333",
+ "Interessierte": "#B266FF",
+ "Kund:innen": "#7F00FF",
+ }
+ self.color_to_role = {v: k for k, v in self.role_to_color.items()}
+ if len(self.color_to_role) != len(self.role_to_color):
+ print("+++ Error: duplicate color in role_to_color")
+
+ def one_item(self, elem):
+ """
+ Analyze one item in the XML file
+ :param elem: the item to analyze
+ """
+ has_value = "value" in elem.attrib
+ text = ""
+ if has_value:
+ text = elem.attrib["value"]
+ if text != "" and text not in self.texts:
+ self.texts.append(text)
+ if "style" in elem.attrib:
+ style = elem.attrib["style"]
+ if style.startswith("ellipse"):
+ self.start_end += 1
+ elif style.startswith("rhombus"):
+ self.decisions += 1
+ elif style.find("shape=callout") >= 0:
+ self.comments += 1
+ elif style.find("shape=delay") >= 0:
+ self.delays += 1
+ elif style.find("shape=manualInput") >= 0:
+ self.manual_inputs += 1
+ elif style.find("shape=note") >= 0:
+ self.documents += 1
+ elif style.startswith("shape=process"):
+ self.subProcesses[text] = 1
+ elif style.startswith("shape=parallelogram"):
+ self.subProcesses[text] = 1
+ elif style.startswith("text;"):
+ if style.find("fontSize=24") >= 0:
+ self.title = text
+ elif style.find("shape=mxgraph.flowchart.stored_data") >= 0:
+ if text not in self.data_sources:
+ self.data_sources.append(text)
+ elif style.startswith("rounded=1"):
+ self.actions += 1
+ # if style.find('fillColor=') >= 0:
+ matcher = self.regColor.search(style)
+ if matcher:
+ color = matcher.group(1).upper()
+ if color in self.color_to_role:
+ role = self.color_to_role[color]
+ if role not in self.roles:
+ self.roles[role] = 1
+ else:
+ self.roles[role] += 1
+
+ def analyze_mxCell(self):
+ """
+ Analyze the mxCell elements in the XML file
+ """
+ tree = ET.parse(self.xml_file)
+ root = tree.getroot()
+
+ for elem in root.iter("mxCell"):
+ self.one_item(elem)
+
+ def print_results(self):
+ """
+ Print the results of the analysis
+ """
+ print(f"Title: {self.title}")
+ print(f"Actions: {self.actions}")
+ print(f"Comments: {self.comments}")
+ print(f"Decisions: {self.decisions}")
+ print(f"Delays: {self.delays}")
+ print(f"Documents: {self.documents}")
+ print(f"Manual inputs: {self.manual_inputs}")
+ print(f"Start/End: {self.start_end}")
+ print(f"Roles ({len(self.roles.keys())}):")
+ keys = self.roles.keys()
+ keys = sorted(keys, key=lambda x: x.lower())
+ for key in keys:
+ print(f" {key}")
+ print(f"SubProcesses ({len(self.subProcesses)}):")
+ keys = self.subProcesses.keys()
+ keys = sorted(keys, key=lambda x: x.lower())
+ for key in self.subProcesses:
+ key = self.stripText(key)
+ print(f" {key}")
+ print(f"Data sources ({len(self.data_sources)}):")
+ keys = self.data_sources
+ keys = sorted(keys, key=lambda x: x.lower())
+ for key in self.data_sources:
+ key = self.stripText(key)
+ print(f" {key}")
+ print(f"Texts ({len(self.texts)}):")
+ texts = self.texts
+ texts = sorted(texts, key=lambda x: x.lower())
+ for text in texts:
+ text = self.stripText(text)
+ print(f" {text}")
+
+ def stripText(self, text: str) -> str:
+ """
+ Strip the text from unwanted HTML elements
+ :param text: the text to strip
+ :return: the stripped text
+ """
+ text = text.replace("-<div>", "")
+ text = text.replace("<div>", "")
+ text = text.replace("</div>", "")
+ text = text.replace("<br>", "")
+ text = text.replace("<br/>", "")
+ text = text.replace(" ", " ")
+ return text
+
+
+def main(argv: list):
+ """
+ Main program
+ :param argv: the list of command line arguments (without the program name)
+ """
+ xml_file = argv[0] if len(argv) > 0 else "data/drawio.xml"
+ if not os.path.exists(xml_file):
+ print(f"File {xml_file} not found")
+ else:
+ analyse = AnalyseDrawio(xml_file)
+ analyse.analyze_mxCell()
+ analyse.print_results()
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])