From 5ee0e859035e903414c354cf97b8f9ed7080d921 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 16:31:06 +0100
Subject: [PATCH 01/17] symlinked

---
 templates/themes/default/_footer.html.twig | 15 ---------------
 1 file changed, 15 deletions(-)
 delete mode 100644 templates/themes/default/_footer.html.twig

diff --git a/templates/themes/default/_footer.html.twig b/templates/themes/default/_footer.html.twig
deleted file mode 100644
index a7208df..0000000
--- a/templates/themes/default/_footer.html.twig
+++ /dev/null
@@ -1,15 +0,0 @@
-<footer class="footer">
-    <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-bottom navbar-bottom">
-        <div class="d-flex justify-content-xl-around">
-            <div class="ms-5">powered by
-                <a href="#">
-                    <img class="mx-1" src="{{ asset('build/images/Spookie/spookie_64x64.png') }}" alt="Spookie">
-                </a>
-            </div>
-            <div id="legal">
-                <a href="{{ path('pages_display', { 'slug': 'imprint' }) }}">Imprint</a>
-                <a href="{{ path('pages_display', { 'slug': 'privacy' }) }}">Privacy Policy</a>
-            </div>
-        </div>
-    </nav>
-</footer>
\ No newline at end of file

From 1d783f0cc6d273c0715fa45883b88ab74bdf6ae3 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 16:34:59 +0100
Subject: [PATCH 02/17] symlinked default

---
 assets/controllers/crop_avatar_controller.js  |  34 ++++
 assets/controllers/hello_controller.js        |  16 --
 .../controllers/upload-avatar_controller.js   | 158 ++++++++++++++++++
 templates/themes/24unix.net/_footer.html.twig |  15 ++
 templates/themes/24unix.net/_header.html.twig |  79 +++++++++
 templates/themes/24unix.net/base.html.twig    | 117 +++++++++++++
 .../themes/24unix.net/pages/index.html.twig   |  13 ++
 .../24unix.net/projects/index.html.twig       |  74 ++++++++
 .../themes/24unix.net/projects/show.html.twig |  35 ++++
 .../security/forgot_password.html.twig        |  22 +++
 .../24unix.net/security/login.html.twig       |  60 +++++++
 .../security/mail/recovery.html.twig          |   9 +
 .../security/mail/registration.html.twig      |  39 +++++
 .../security/recovery_mail_sent.html.twig     |  11 ++
 .../24unix.net/security/register.html.twig    |  60 +++++++
 .../security/registration_finished.html.twig  |  19 +++
 .../security/resend_activation.html.twig      |  20 +++
 .../security/reset_password.html.twig         |  12 ++
 .../24unix.net/user/edit_profile.html.twig    |  62 +++++++
 .../24unix.net/user/list_users.html.twig      |  49 ++++++
 .../24unix.net/user/show_profile.html.twig    |  46 +++++
 21 files changed, 934 insertions(+), 16 deletions(-)
 create mode 100644 assets/controllers/crop_avatar_controller.js
 delete mode 100644 assets/controllers/hello_controller.js
 create mode 100644 assets/controllers/upload-avatar_controller.js
 create mode 100644 templates/themes/24unix.net/_footer.html.twig
 create mode 100644 templates/themes/24unix.net/_header.html.twig
 create mode 100644 templates/themes/24unix.net/base.html.twig
 create mode 100644 templates/themes/24unix.net/pages/index.html.twig
 create mode 100644 templates/themes/24unix.net/projects/index.html.twig
 create mode 100644 templates/themes/24unix.net/projects/show.html.twig
 create mode 100644 templates/themes/24unix.net/security/forgot_password.html.twig
 create mode 100644 templates/themes/24unix.net/security/login.html.twig
 create mode 100644 templates/themes/24unix.net/security/mail/recovery.html.twig
 create mode 100644 templates/themes/24unix.net/security/mail/registration.html.twig
 create mode 100644 templates/themes/24unix.net/security/recovery_mail_sent.html.twig
 create mode 100644 templates/themes/24unix.net/security/register.html.twig
 create mode 100644 templates/themes/24unix.net/security/registration_finished.html.twig
 create mode 100644 templates/themes/24unix.net/security/resend_activation.html.twig
 create mode 100644 templates/themes/24unix.net/security/reset_password.html.twig
 create mode 100644 templates/themes/24unix.net/user/edit_profile.html.twig
 create mode 100644 templates/themes/24unix.net/user/list_users.html.twig
 create mode 100644 templates/themes/24unix.net/user/show_profile.html.twig

diff --git a/assets/controllers/crop_avatar_controller.js b/assets/controllers/crop_avatar_controller.js
new file mode 100644
index 0000000..2393657
--- /dev/null
+++ b/assets/controllers/crop_avatar_controller.js
@@ -0,0 +1,34 @@
+import { Controller } from '@hotwired/stimulus'
+import Swal from 'sweetalert2'
+
+export default class extends Controller {
+	static values = {
+		cropImage: String
+	}
+
+	connect() {
+		console.log('crop', this.cropImageValue)
+		window.CropAvatar = this
+	}
+
+	open() {
+		console.log('open crop')
+		Swal.fire({
+			title: 'Are you sure?',
+			text: "You won't be able to revert this!",
+			icon: 'warning',
+			showCancelButton: true,
+			confirmButtonColor: '#3085d6',
+			cancelButtonColor: '#d33',
+			confirmButtonText: 'Yes, delete it!',
+		}).then((result) => {
+			if (result.isConfirmed) {
+				Swal.fire(
+					'Deleted!',
+					'Your file has been deleted.',
+					'success',
+				)
+			}
+		})
+	}
+}
diff --git a/assets/controllers/hello_controller.js b/assets/controllers/hello_controller.js
deleted file mode 100644
index e847027..0000000
--- a/assets/controllers/hello_controller.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Controller } from '@hotwired/stimulus';
-
-/*
- * This is an example Stimulus controller!
- *
- * Any element with a data-controller="hello" attribute will cause
- * this controller to be executed. The name "hello" comes from the filename:
- * hello_controller.js -> "hello"
- *
- * Delete this file or adapt it for your use!
- */
-export default class extends Controller {
-    connect() {
-        this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
-    }
-}
diff --git a/assets/controllers/upload-avatar_controller.js b/assets/controllers/upload-avatar_controller.js
new file mode 100644
index 0000000..96d9dd3
--- /dev/null
+++ b/assets/controllers/upload-avatar_controller.js
@@ -0,0 +1,158 @@
+import { Controller } from '@hotwired/stimulus'
+import { Dropzone } from 'dropzone'
+import Cropper from 'cropperjs'
+
+export default class extends Controller {
+	static values = {
+		avatarImage: String,
+		userId: Number
+	}
+
+	connect() {
+		console.log('image', this.avatarImageValue)
+		console.log('id', this.userIdValue)
+
+		/*
+		const formElement = document.getElementById('avatarDropzone')
+		const dzMessage = document.getElementsByClassName('dz-message')
+		formElement.style.backgroundImage = `url('${this.avatarImageValue}')`
+		formElement.style.backgroundRepeat = 'no-repeat'
+		formElement.style.backgroundPosition = 'center center'
+		formElement.style.backgroundSize = 'auto'
+		formElement.style.borderRadius = '25px'
+		console.log('formelement', formElement)
+*/
+
+		console.log('init Dropzone')
+		let avatarDropzone = new Dropzone('#avatarDropzone', {
+			url: `/user/upload/avatar/${this.userIdValue}`,
+			avatarUrl: this.avatarImageValue,
+			acceptedFiles: '.jpg, .jpeg, .png, gif',
+			maxFiles: 1,
+			dictDefaultMessage: '',
+			init() {
+				avatarDropzone = this
+				// If the thumbnail is already in the right size on your server:
+				const mockFile = { name: 'Filename', size: 12345 }
+				const callback = null // Optional callback when it's done
+				const crossOrigin = null // Added to the `img` tag for crossOrigin handling
+				const resizeThumbnail = false // Tells Dropzone whether it should resize the image first
+				avatarDropzone.displayExistingFile(mockFile, avatarDropzone.options.avatarUrl, callback, crossOrigin, resizeThumbnail);
+				avatarDropzone.files.push(mockFile); // line missing in official docs
+
+				/*
+				console.log('url', avatarDropzone.options.foo)
+				this.hiddenFileInput.removeAttribute('multiple')
+				this.on('maxfilesexceeded', (file) => {
+					this.removeAllFiles()
+					this.addFile(file)
+				})
+				this.on('error', (file, data) => {
+					console.log('error')
+					if (data.detail) {
+						this.emit('error', file, data.detail)
+					}
+				})
+				console.log('beforemock')
+				const mockFile = { name: 'Name Image', size: 12345, type: 'image/jpeg' }
+				this.emit('addedfile', mockFile)
+				this.emit('success', mockFile)
+				console.log('avi', this.avatarImageValue)
+				this.emit('thumbnail', mockFile, avatarDropzone.options.avatarUrl)
+				console.log('aftermock')
+				this.emit('complete', mockFile);
+				this.files.push(mockFile);
+				*/
+			},
+			transformFile(file, done) {
+				// Create the image editor overlay
+				const editor = document.createElement('div')
+				editor.style.position = 'fixed'
+				editor.style.left = '25px'
+				editor.style.right = '25px'
+				editor.style.top = '25px'
+				editor.style.bottom = '25px'
+				editor.style.zIndex = '9999'
+				editor.style.borderRadius = '15px;'
+				editor.style.borderColor = '#FF8844'
+				editor.style.backgroundColor = '#000'
+				document.body.appendChild(editor)
+
+				// Create an image node for Cropper.js
+				const image = new Image()
+				image.src = URL.createObjectURL(file)
+				editor.appendChild(image)
+
+				// Create Cropper.js
+				const cropper = new Cropper(image, {aspectRatio: 1})
+
+				// Create confirm button at the top left of the viewport
+				const buttonCrop = document.createElement('button')
+				buttonCrop.style.position = 'absolute'
+				buttonCrop.style.right = '10px'
+				buttonCrop.style.bottom = '50px'
+				buttonCrop.style.zIndex = '9999'
+				buttonCrop.textContent = 'Crop'
+				buttonCrop.className = 'btn btn-secondary'
+				editor.appendChild(buttonCrop)
+				buttonCrop.addEventListener('click', () => {
+					// clear preview
+					//formElement.style.backgroundImage = ''
+
+					const canvas = cropper.getCroppedCanvas({
+						width: 256,
+						height: 256
+					})
+					canvas.toBlob((blob) => {
+						avatarDropzone.createThumbnail(
+							blob,
+							avatarDropzone.options.thumbnailWidth,
+							avatarDropzone.options.thumbnailHeight,
+							avatarDropzone.options.thumbnailMethod,
+							false,
+							(dataURL) => {
+								avatarDropzone.emit('thumbnail', file, dataURL)
+								done(blob)
+							}
+						)
+					})
+
+					// Remove the editor from the view
+					document.body.removeChild(editor)
+				})
+
+				// Create keep original button
+				const buttonKeep = document.createElement('button')
+				buttonKeep.style.position = 'absolute'
+				buttonKeep.style.right = '100px'
+				buttonKeep.style.bottom = '50px'
+				buttonKeep.style.zIndex = '9999'
+				buttonKeep.textContent = 'Keep Original'
+				buttonKeep.className = 'btn btn-primary'
+				buttonKeep.focus()
+				editor.appendChild(buttonKeep)
+				buttonKeep.addEventListener('click', () => {
+					//formElement.style.backgroundImage = ''
+
+					// Remove the editor from the view
+					this.removeAllFiles()
+
+					done(file)
+					document.body.removeChild(editor)
+				})
+
+				// info text
+				const infoText = document.createElement('span')
+				infoText.style.position = 'absolute'
+				infoText.style.opacity = '0.5'
+				infoText.style.left = '100px'
+				infoText.style.bottom = '50px'
+				infoText.style.display = 'inline:block'
+				infoText.style.zIndex = '9999'
+				infoText.textContent = 'Use mouse wheel/touchpad to adapt size'
+				infoText.className = 'btn btn-secondary'
+				editor.appendChild(infoText)
+			}
+		})
+	}
+}
diff --git a/templates/themes/24unix.net/_footer.html.twig b/templates/themes/24unix.net/_footer.html.twig
new file mode 100644
index 0000000..a7208df
--- /dev/null
+++ b/templates/themes/24unix.net/_footer.html.twig
@@ -0,0 +1,15 @@
+<footer class="footer">
+    <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-bottom navbar-bottom">
+        <div class="d-flex justify-content-xl-around">
+            <div class="ms-5">powered by
+                <a href="#">
+                    <img class="mx-1" src="{{ asset('build/images/Spookie/spookie_64x64.png') }}" alt="Spookie">
+                </a>
+            </div>
+            <div id="legal">
+                <a href="{{ path('pages_display', { 'slug': 'imprint' }) }}">Imprint</a>
+                <a href="{{ path('pages_display', { 'slug': 'privacy' }) }}">Privacy Policy</a>
+            </div>
+        </div>
+    </nav>
+</footer>
\ No newline at end of file
diff --git a/templates/themes/24unix.net/_header.html.twig b/templates/themes/24unix.net/_header.html.twig
new file mode 100644
index 0000000..b99ea83
--- /dev/null
+++ b/templates/themes/24unix.net/_header.html.twig
@@ -0,0 +1,79 @@
+<nav
+        class="navbar navbar-expand-md navbar-dark sticky-top px-sm-5 border-0 {{ is_granted('ROLE_PREVIOUS_ADMIN') ? 'bg-light' : 'bg-dark' }}"
+>
+
+    <a class="navbar-brand border-0" href="{{ path('app_main') }}">
+        <img src="{{ asset('build/images/24unix/24_logo_bg_64x64.png') }}" alt="24unix.net" id="site-logo">
+    </a>
+
+    <button class="navbar-toggler border-0" type="button" data-toggle="collapse" data-target="#CollapsingNavbar">
+        &#9776;
+    </button>
+
+    <div class="collapse navbar-collapse" id="CollapsingNavbar">
+        <ul class="navbar-nav ms-auto">
+            <!--
+            <li>
+                <form class="d-flex">
+                    <input class="form-control me-2 my-2" type="search" placeholder="Search" aria-label="Search">
+                    <button class="btn" type="submit">Search</button>
+                </form>
+            </li>
+            -->
+            {% if is_granted('ROLE_USER') %}
+                <li class="nav-item dropdown me-auto">
+                    <button type="button" id="navbar-dropdown" data-bs-target="#dropdown-menu" data-bs-toggle="dropdown"
+                            class="btn btn-outline-dark dropdown-toggle button-login">
+                        {% if app.user.avatar %}
+                            <img class="rounded-circle border border-primary"
+                                 width="50px"
+                                 src="{{ avatar_asset(app.user.avatar)|imagine_filter('squared_thumbnail_small') }}"
+                                 alt="profile image"/>
+                        {% else %}
+                            {{ app.user.username }}
+                        {% endif %}
+                    </button>
+
+                    <div class="dropdown-menu dropdown-menu-dark dropdown-menu-end" id="dropdown-menu"
+                         aria-labelledby="navbar-dropdown">
+                        <a class="dropdown-item" href="{{ path('app_profile_edit', { 'username': app.user.username }) }}">
+                            <span class="fa fa-lg fa-fw fa-user" aria-hidden="true"></span>
+                            Profile</a>
+                        <a class="dropdown-item" href="#">
+                            <span class="fa fa-lg fa-fw fa-wrench" aria-hidden="true"></span>
+                            Settings</a>
+                        <div class="dropdown-divider"></div>
+                        {% if is_granted('ROLE_ADMIN') %}
+                            <a class="dropdown-item" href="{{ path('admin') }}">
+                                <span class="fa fa-lg fa-fw fa-cog" aria-hidden="true"></span>
+                                Administration
+                            </a>
+                            <div class="dropdown-divider"></div>
+                        {% endif %}
+
+                        {%  if is_granted('ROLE_PREVIOUS_ADMIN') %}
+                            <a class="dropdown-item" href="{{  path('app_main', {
+                                '_switch_user': '_exit'
+                            }) }}">
+                                <span class="fa fa-lg fa-fw fa-backward" aria-hidden="true"></span>
+                                Back to Admin</a>
+                            <div class="dropdown-divider"></div>
+                        {% endif %}
+
+                        <a class="dropdown-item" href="{{ path('security_logout') }}">
+                            <span class="fa fa-lg fa-fw fa-sign-out" aria-hidden="true"></span>&nbsp;
+                            Logout
+                        </a>
+                    </div>
+                </li>
+            {% else %}
+                <li class="nav-item">
+                    <a class="btn btn-primary button-login" href="{{ path('security_login') }}" role="button"
+                       id="buttonLogin">
+                        <span class="fa fa-sign-in fa-lg fa-fw" aria-hidden="true"></span>Log In
+                    </a>
+                </li>
+            {% endif %}
+        </ul>
+    </div>
+</nav>
diff --git a/templates/themes/24unix.net/base.html.twig b/templates/themes/24unix.net/base.html.twig
new file mode 100644
index 0000000..9110af2
--- /dev/null
+++ b/templates/themes/24unix.net/base.html.twig
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>{% block title %}Spookie{% endblock %}</title>
+    <link rel="icon"
+          href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
+
+    {% block stylesheets %}
+        {{ encore_entry_link_tags('app') }}
+    {% endblock %}
+
+    {% block javascripts %}
+        {{ encore_entry_script_tags('app') }}
+
+        <!-- Matomo -->
+        <script>
+			let _paq = window._paq = window._paq || []
+			/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
+			_paq.push(["trackPageView"])
+			_paq.push(["enableLinkTracking"]);
+			(function () {
+				const u = "https://analytics.24unix.net/"
+				_paq.push(["setTrackerUrl", u + "matomo.php"])
+				_paq.push(["setSiteId", "1"])
+				const d = document, g = d.createElement("script"), s = d.getElementsByTagName("script")[0]
+				g.async = true
+				g.src = u + "matomo.js"
+				s.parentNode.insertBefore(g, s)
+			})()
+        </script>
+        <!-- End Matomo Code -->
+    {% endblock %}
+
+</head>
+<body data-turbo="false">
+
+{% include '@default/_header.html.twig' %}
+
+<!-- sidebar -->
+<nav id="sidebar" class="m-3">
+    <ul class="list-group ml1" id="main-menu">
+        <li class="list-group-item list-group-item-action">
+            <a href="{{ path('app_projects') }}" class="text-decoration-none">
+                <span class="fa fa-lg fa-fw fa-file-code-o" aria-hidden="true" title="Projects"></span>
+                <span class="menuText">&nbsp;Projects</span>
+            </a>
+        </li>
+        <li class="list-group-item list-group-item-action">
+            <a href="//git.24unix.net" class="text-decoration-none" target="_blank">
+                <span class="fa fa-lg fa-fw fa-gitea" aria-hidden="true" title="Gitea"></span>
+                <span class="menuText">&nbsp;Gitea</span>
+            </a>
+            <span class="menuText"><span class="fa fa-external-link" aria-hidden="true"></span></span>
+        </li>
+        <li class="list-group-item list-group-item-action">
+            <a href="//cloud.24unix.net" class="text-decoration-none" target="_blank">
+                <span class="fa fa-lg fa-fw fa-nextcloud" aria-hidden="true" title="NextCloud"></span>
+                <span class="menuText">&nbsp;NextCloud</span>
+            </a>
+            <span class="menuText"><span class="fa fa-external-link" aria-hidden="true"></span></span>
+        </li>
+        <li class="list-group-item list-group-item-action">
+            <a href="#" id="toggleSidebar">
+                <span id="toggleIcon" class="fa fa-lg fa-caret-square-o-left"></span>
+            </a>
+        </li>
+        <!--                <a href="//pastebin.24unix.net">pastebin.24unix.net</a>-->
+    </ul>
+</nav>
+
+<div class="container-fluid" id="content">
+
+
+    <div class="col m-3" id="main_content">
+
+        {#
+        <div data-turbo="false">
+            {% for message in app.flashes('success') %}
+                <div id="react"></div>
+                <div
+                        {{ stimulus_controller('toastify', {
+                            flash: message
+                        }) }}
+                >
+                </div>
+                dump(app)
+            {% endfor %}
+        </div>
+        #}
+
+        {% for message in app.flashes('error') %}
+            <div class="alert alert-danger">
+                {{ message }}
+            </div>
+        {% endfor %}
+
+        {% block body %}
+            <div class="m-5">
+                <h1 class="title-show">Quote of the Moment</h1>
+                <div class="quote-box p-3">
+                    <span class="fa fa-quote-left"></span><br>
+                    {{ quote | raw | nl2br }}
+                </div>
+            </div>
+        {% endblock %}
+    </div>
+
+</div>
+
+{% include '@default/_footer.html.twig' %}
+
+
+
+</body>
+</html>
\ No newline at end of file
diff --git a/templates/themes/24unix.net/pages/index.html.twig b/templates/themes/24unix.net/pages/index.html.twig
new file mode 100644
index 0000000..7eb1db0
--- /dev/null
+++ b/templates/themes/24unix.net/pages/index.html.twig
@@ -0,0 +1,13 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}page_name{% endblock %}
+
+{% block body %}
+    {{ page.content | raw }}
+    <br>
+    Page created by: <a href="{{ path('app_profile', { 'username': page.owner.username }) }}">{{ page.owner.username }}</a>
+    {{ page.createdAt | ago }}
+    {% if page.modifiedAt %}
+        (last update: {{ page.modifiedAt | ago }})
+    {% endif %}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/themes/24unix.net/projects/index.html.twig b/templates/themes/24unix.net/projects/index.html.twig
new file mode 100644
index 0000000..003002d
--- /dev/null
+++ b/templates/themes/24unix.net/projects/index.html.twig
@@ -0,0 +1,74 @@
+{# templates/projects/index.html.twig #}
+{% extends '@default/base.html.twig' %}
+
+{% block title %} Projects {% endblock %}
+
+{% block body %}
+    <div class="container-fluid box">
+        <div class="row">
+            <h2>This is an overview of my current public (and open source) projects.</h2>
+
+            <!-- projects List -->
+            <div class="col-sm-12">
+                {% for project in projects %}
+                    <div class="project-container bg-dark my-4">
+                        <div class="row">
+                            <div class="col-sm-3">
+                                <a href="{{ path('app_projects', { name: project.name }) }}">
+                                    {% if project.teaserImage %}
+                                        <img
+                                                class="project-image"
+                                                src="/uploads/projects/{{  project.teaserImage  }}"
+                                                alt="Teaser">
+                                    {% else %}
+                                        <img
+                                                class="project-image"
+                                                src="{{ asset('build/images/24unix/24_logo_bg_96x96.png') }}"
+                                                alt="Teaser">
+                                    {% endif %}
+                                </a>
+                                <br>
+                                <div>
+                                    {% for developer in project.developer %}
+                                        <a class="align-left blog-details"
+                                           href="{{ path('app_profile', { 'username':developer.username }) }}">
+                                            {% if developer.avatar is not null %}
+                                                <img class="rounded-circle mt-5 mb-4 ms-5"
+                                                     src="{{ avatar_asset(developer.avatar)|imagine_filter('squared_thumbnail_small') }}" alt="profile image"/>
+                                                <br>
+                                            {% endif %}
+                                            <div class="ms-5 mb-4">
+                                                <a href="{{ path('app_profile', { 'username':developer.username }) }}">{{ developer.username }}</a>
+                                            </div>
+                                    {% endfor %}
+                                </div>
+
+                            </div>
+                            <div class="col-sm-8 mt-2">
+                                <a href="{{ path('app_projects', { name: project.name }) }}">
+                                    <div class="article-title d-inline-block pl-3 align-middle">
+                                        <h2>{{ project.name }}</h2>
+                                    </div>
+                                </a>
+                                <br>
+                                <div class="blog-teaser mb-2 pb-2 text-xl-start">
+                                    {{ project.description }}
+                                    <br>
+                                    <br>
+                                    started: {{ project.createdAt | ago }}
+                                </div>
+                            </div>
+                        </div>
+
+                    </div>
+                {% endfor %}
+            </div>
+
+            {% if is_granted('ROLE_ADMIN') %}
+                <div class="text-xl-start">
+                    <a href="{{ path('app_main') }}"><span class="fa fa-plus-circle"></span></a>
+                </div>
+            {%  endif %}
+        </div>
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/templates/themes/24unix.net/projects/show.html.twig b/templates/themes/24unix.net/projects/show.html.twig
new file mode 100644
index 0000000..4c586f1
--- /dev/null
+++ b/templates/themes/24unix.net/projects/show.html.twig
@@ -0,0 +1,35 @@
+{# templates/blog/blog_show.html.twig #}
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Projects{% endblock %}
+
+{% block body %}
+
+    <div class="row">
+        <div class="col-sm-12">
+            {% if is_granted('ROLE_ADMIN') %}
+                <div class="d-flex justify-content-end">
+                    <a href="{{ path('app_main', { id : project.id }) }}"><span class="fa fa-3x fa-fw fa-edit"></span></a>
+                    <a href="{{ path('app_main', { id : project.id }) }}"><span class="fa fa-3x fa-fw fa-trash"></span></a>
+                </div>
+            {% endif %}
+
+            <div class="show-article-container p-3 mt-4">
+                <div class="show-article-title-container d-inline-block pl-3 align-middle">
+                    <h2>{{ project.name }}</h2>
+                </div>
+                <div>
+                    Source: <a href="{{ project.url }}" target="_blank">{{ project.url }}</a>&nbsp;
+                    <span class="fa fa-external-link" aria-hidden="true"></span>
+                </div>
+                <div class="row">
+                    <div class="col-sm-12">
+                        <div class="article-text">
+                            {{ readme | markdown_to_html }}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+{%  endblock %}
\ No newline at end of file
diff --git a/templates/themes/24unix.net/security/forgot_password.html.twig b/templates/themes/24unix.net/security/forgot_password.html.twig
new file mode 100644
index 0000000..c397c08
--- /dev/null
+++ b/templates/themes/24unix.net/security/forgot_password.html.twig
@@ -0,0 +1,22 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Reset your password{% endblock %}
+
+{% block body %}
+    {% for flash_error in app.flashes('reset_password_error') %}
+        <div class="alert alert-danger" role="alert">{{ flash_error }}</div>
+    {% endfor %}
+    <h1>Reset your password</h1>
+
+    {{ form_start(requestForm) }}
+        {{ form_row(requestForm.account) }}
+        <div>
+            <small>
+                Enter your email address or your username and we will send you a
+                link to reset your password.
+            </small>
+        </div>
+
+        <button class="btn btn-primary float-end">Send password reset email</button>
+    {{ form_end(requestForm) }}
+{% endblock %}
diff --git a/templates/themes/24unix.net/security/login.html.twig b/templates/themes/24unix.net/security/login.html.twig
new file mode 100644
index 0000000..5522024
--- /dev/null
+++ b/templates/themes/24unix.net/security/login.html.twig
@@ -0,0 +1,60 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Log In!{% endblock %}
+
+{% block body %}
+    <div class="container rounded">
+        <div class="row">
+            <div class="login-form bg-dark mt-4 p-4">
+                <form method="post" class="row g-3">
+                    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
+
+                    {% if error %}
+                        <div class="alert-dark alert-danger">
+                            {{ error.messageKey|trans(error.messageData, 'security') }}
+                        </div>
+                    {% endif %}
+
+
+                    <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
+
+                    <div class="col-12">
+                        <label for="inputUsername">Username or eMail</label>
+                        <input type="text"
+                               name="username"
+                               autocomplete="nickname"
+                               id="inputUsername"
+                               class="form-control"
+                               value="{{ last_username }}"
+                               required
+                               autofocus
+                        >
+                    </div>
+                    <div class="col-12">
+                        <label for="inputPassword">Password</label>
+                        <input
+                                type="password"
+                                name="password"
+                                autocomplete="current-password"
+                                id="inputPassword"
+                                class="form-control"
+                                required
+                        >
+                    </div>
+                    <div class="form-check mx-3">
+                        <label>
+                            <input type="checkbox" name="_remember_me" class="form-check-input">Remember me
+                        </label>
+                    </div>
+                    <div>
+                        <a href="{{ path('security_forgot_password') }}">Forgot password?</a>
+                        <button class="btn btn-primary float-end" type="submit">
+                            Sign in
+                        </button>
+                    </div>
+                    <a href="{{ path('security_register') }}">Need an account? Register now.</a>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock %}
diff --git a/templates/themes/24unix.net/security/mail/recovery.html.twig b/templates/themes/24unix.net/security/mail/recovery.html.twig
new file mode 100644
index 0000000..a651258
--- /dev/null
+++ b/templates/themes/24unix.net/security/mail/recovery.html.twig
@@ -0,0 +1,9 @@
+<h1>Hi!</h1>
+
+<p>To reset your password, please visit the following link</p>
+
+<a href="{{ url('security_recovery_reset', {token: resetToken.token}) }}">Reset password</a>
+
+<p>This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.</p>
+
+<p>Cheers!</p>
diff --git a/templates/themes/24unix.net/security/mail/registration.html.twig b/templates/themes/24unix.net/security/mail/registration.html.twig
new file mode 100644
index 0000000..665506f
--- /dev/null
+++ b/templates/themes/24unix.net/security/mail/registration.html.twig
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en" style="margin:0;padding:0;">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1"/>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+    <meta name="format-detection" content="telephone=no"/>
+    <title></title>
+    <style type="text/css">
+        @media screen and (max-width: 599px) {
+            div {
+                max-width: 100% !important;
+            }
+        }
+        body {
+            background-color: #1e1f1e;
+            margin:25px;
+            padding:0;
+        }
+    </style>
+</head>
+<body>
+<div style="border-collapse:collapse;border-spacing:0;">
+    <h1>Hi {{ username }}, please confirm your email!</h1>
+
+    <p>
+        Please confirm your email address by clicking the following link: <br><br>
+        <a href="{{ signedUrl|raw }}">Confirm my Email</a>.
+        <br>
+        This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}.
+    </p>
+
+    <p>
+        Kind regards,
+        24unix.net
+    </p>
+</div>
+</body>
+</html>
diff --git a/templates/themes/24unix.net/security/recovery_mail_sent.html.twig b/templates/themes/24unix.net/security/recovery_mail_sent.html.twig
new file mode 100644
index 0000000..8786623
--- /dev/null
+++ b/templates/themes/24unix.net/security/recovery_mail_sent.html.twig
@@ -0,0 +1,11 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Password Reset Email Sent{% endblock %}
+
+{% block body %}
+    <p>
+        If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
+        This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
+    </p>
+    <p>If you don't receive an email please check your spam folder or <a href="{{ path('security_forgot_password') }}">try again</a>.</p>
+{% endblock %}
diff --git a/templates/themes/24unix.net/security/register.html.twig b/templates/themes/24unix.net/security/register.html.twig
new file mode 100644
index 0000000..6b3a3bc
--- /dev/null
+++ b/templates/themes/24unix.net/security/register.html.twig
@@ -0,0 +1,60 @@
+{% extends '@default/base.html.twig' %}
+{% form_theme registrationForm _self %}
+
+{% block title %}Register{% endblock %}
+
+
+{% block form_row %}
+    {%- set widget_attr = {} -%}
+    {%- if help is not empty -%}
+        {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
+    {%- endif -%}
+
+    {{- form_label(form, null, {
+        label_attr: { class: 'sr-only' }
+    }) -}}
+    {{- form_errors(form) -}}
+    {{- form_widget(form, widget_attr) -}}
+    {{- form_help(form) -}}
+{% endblock %}
+
+
+{% block body %}
+    {% for flash_error in app.flashes('verify_email_error') %}
+        <div class="alert alert-danger" role="alert">{{ flash_error }}</div>
+    {% endfor %}
+
+    <div class="container rounded">
+        <div class="row">
+            <div class="login-form bg-dark mt-4 p-4">
+                <h1 class="h3 mb-3 font-weight-normal">Register</h1>
+
+                {{ form_start(registrationForm, {
+                    attr: { class: 'form-signin' }
+                }) }}
+                <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
+                {{ form_row(registrationForm.username, {
+                    attr: { placeholder: 'User name' }
+                }) }}
+                {{ form_row(registrationForm.email, {
+                    attr: { placeholder: 'eMail' }
+                }) }}
+                {{ form_row(registrationForm.password.first, {
+                    attr: { placeholder: 'Password'}
+                }) }}
+                {{ form_row(registrationForm.password.second, {
+                    attr: { placeholder: 'Confirm password'}
+                }) }}
+
+                {{ form_row(registrationForm.agreeTerms) }}
+
+                <button type="submit" class="btn btn-primary float-end">Register</button>
+                {{ form_end(registrationForm) }}
+
+            </div>
+        </div>
+    </div>
+
+{% endblock %}
+
+
diff --git a/templates/themes/24unix.net/security/registration_finished.html.twig b/templates/themes/24unix.net/security/registration_finished.html.twig
new file mode 100644
index 0000000..5f12a74
--- /dev/null
+++ b/templates/themes/24unix.net/security/registration_finished.html.twig
@@ -0,0 +1,19 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Registration finished{% endblock %}
+
+{% block body %}
+
+    <div class="container rounded">
+        <div class="row">
+            <div class="login-form bg-dark mt-4 p-4">
+                <h1 class="h3 mb-3 font-weight-normal">Registration finished</h1>
+                <p>You'll receive an email soon to confirm your identity.</p>
+
+            </div>
+        </div>
+    </div>
+
+{% endblock %}
+
+
diff --git a/templates/themes/24unix.net/security/resend_activation.html.twig b/templates/themes/24unix.net/security/resend_activation.html.twig
new file mode 100644
index 0000000..c0e89c2
--- /dev/null
+++ b/templates/themes/24unix.net/security/resend_activation.html.twig
@@ -0,0 +1,20 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Verify eMail{% endblock %}
+
+{% block body %}
+    <div class="container">
+        <div class="row">
+            <div class="login-form bg-dark mt-4 p-4">
+                <h1 class="h3 mb-3 font-weight-normal">Verify your Email</h1>
+                <p>
+                    A verification email was sent - please check it to enable your
+                    account before logging in.
+                </p>
+                <form method="POST">
+                    <button type="submit" class="btn btn-primary">Re-send Email</button>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock %}
diff --git a/templates/themes/24unix.net/security/reset_password.html.twig b/templates/themes/24unix.net/security/reset_password.html.twig
new file mode 100644
index 0000000..ed95f9b
--- /dev/null
+++ b/templates/themes/24unix.net/security/reset_password.html.twig
@@ -0,0 +1,12 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}Reset your password{% endblock %}
+
+{% block body %}
+    <h1>Reset your password</h1>
+
+    {{ form_start(resetForm) }}
+        {{ form_row(resetForm.plainPassword) }}
+        <button class="btn btn-primary">Update password</button>
+    {{ form_end(resetForm) }}
+{% endblock %}
diff --git a/templates/themes/24unix.net/user/edit_profile.html.twig b/templates/themes/24unix.net/user/edit_profile.html.twig
new file mode 100644
index 0000000..84d5f97
--- /dev/null
+++ b/templates/themes/24unix.net/user/edit_profile.html.twig
@@ -0,0 +1,62 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}
+    Profile of {{ user.username }}
+{% endblock %}
+
+{% block body %}
+
+    <div class="container box rounded bg-dark mt-5 mb-5">
+        {{ form_start(userForm) }}
+        <div class="row">
+
+            <div class="col-md-3 border-right">
+                <div class="d-flex flex-column align-items-center text-center p-3 py-5">
+                    {#
+                    {% if user.avatar is not null %}
+                        <img class="rounded-circle mt-5 mb-4"
+                             src="{{ avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') }}"
+                             alt="profile image"/>
+                    {% endif %}
+                    #}
+
+                    <div
+                            class="dropzone"
+                            id="avatarDropzone"
+                            style="width:172px;"
+                            {{ stimulus_controller('upload_avatar', {
+                                avatarImage: avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') ,
+                                userId: user.id
+                            }) }}
+                    >
+                    </div>
+                    <span class="font-weight-bold">{{ user.username }}</span>
+                    <span class="text-white-50"><span class="fa fa-lg fa-envelope me-1"></span><a href="mailto:{{ user.email }}">{{  user.email }}</a></span>
+                </div>
+            </div>
+            <div class="col-md-8 border-right">
+                <div class="p-3 py-5">
+                    <div class="d-flex justify-content-between align-items-center mb-3">
+                        <h4 class="text-right">User Profile</h4>
+                    </div>
+                    <div class="row mt-2">
+                        {{ form_row(userForm.username) }}
+                        {{ form_row(userForm.firstName) }}
+                        {{ form_row(userForm.lastName) }}
+                        {{ form_row(userForm.email) }}
+                        {{ form_row(userForm.newPassword.first) }}
+                        {{ form_row(userForm.newPassword.second) }}
+                        {{ form_rest(userForm) }}
+                    </div>
+                    <div class="mb-5 text-center float-end">
+                        <button class="btn btn-primary profile-button" type="submit">Save Profile</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+        {{ form_end(userForm) }}
+
+
+    </div>
+
+{% endblock %}
diff --git a/templates/themes/24unix.net/user/list_users.html.twig b/templates/themes/24unix.net/user/list_users.html.twig
new file mode 100644
index 0000000..7893a17
--- /dev/null
+++ b/templates/themes/24unix.net/user/list_users.html.twig
@@ -0,0 +1,49 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}
+    Userlist
+{% endblock %}
+
+{% block body %}
+
+
+    <div class="container box rounded bg-dark mt-5 mb-5">
+        <div class="row">
+            <table class="table table-striped">
+                <thead>
+                <tr>
+                    <th scope="col">Username</th>
+                    <th scope="col">First</th>
+                    <th scope="col">Last</th>
+                    <th scope="col">eMail</th>
+                    <th scope="col">Registered</th>
+                    <th scope="col">Manage</th>
+                </tr>
+                </thead>
+                {% for user in users %}
+                    <tr>
+                        <td>
+                            <a href="{{ path('app_main', { '_switch_user': user.username }) }}">{{ user.username }}</a>
+                        </td>
+                        <td>
+                            {{ user.firstName }}
+                        </td>
+                        <td>
+                            {{ user.lastName }}
+                        </td>
+                        <td>
+                            {{ user.email }}
+                        </td>
+                        <td>
+                            {{ user.createdAt|ago }}
+                        </td>
+                        <td>
+                            <button type="button" class="btn btn-outline-primary btn-lg btn-circle ml-2"><i class="fa fa-edit"></i> </button>
+                        </td>
+
+                    </tr>
+                {% endfor %}
+            </table>
+        </div>
+    </div>
+{% endblock %}
diff --git a/templates/themes/24unix.net/user/show_profile.html.twig b/templates/themes/24unix.net/user/show_profile.html.twig
new file mode 100644
index 0000000..55b038c
--- /dev/null
+++ b/templates/themes/24unix.net/user/show_profile.html.twig
@@ -0,0 +1,46 @@
+{% extends '@default/base.html.twig' %}
+
+{% block title %}
+    Profile of {{ user.username }}
+{% endblock %}
+
+{% block body %}
+
+    <div class="container box rounded bg-dark mt-5 mb-5">
+        <div class="row">
+            <div class="col-md-3 border-right">
+                <div class="d-flex flex-column align-items-center text-center p-3 py-5">
+                    {% if user.avatar is not null %}
+                        <img class="rounded-circle mt-5 mb-4"
+                             src="{{ avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') }}"
+                             alt="profile image"/>
+                    {% endif %}
+                    <span class="font-weight-bold">{{ user.username }}</span>
+                    {% if is_granted('ROLE_ADMIN') %}
+                        <span class="font-weight-bold">
+                            <a href="{{ path('app_main', { '_switch_user': user.username }) }}">switch user</a>
+                        </span>
+                    {% endif %}
+
+                    <span class="text-white-50"><i class="fa fa-lg fa-envelope me-1"></i><a href="mailto:{{  user.email }}">{{ user.email }}</a></span>
+                </div>
+            </div>
+            <div class="col-md-5 border-right">
+                <div class="p-3 py-5">
+                    <div class="d-flex justify-content-between align-items-center mb-3">
+                        <h4 class="text-right">User Profile</h4>
+                    </div>
+                    <div class="row mt-2">
+                        <label class="labels" for="first-name">First Name</label>
+                        <input type="text" disabled id="first-name" class="form-control" value="{{ user.firstName }}">
+
+                        <label class="labels" for="last-name">Last Name</label>
+                        <input type="text" disabled id="last-name" class="form-control" value="{{ user.lastName }}">
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+{% endblock %}

From d9b86de4bb576fabbd0b11a0d5c22e8032a84bfe Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 16:39:18 +0100
Subject: [PATCH 03/17] symlinked default

---
 templates/themes/default                      |   1 +
 templates/themes/default/_header.html.twig    |  68 ------------
 templates/themes/default/base.html.twig       | 105 ------------------
 .../themes/default/pages/index.html.twig      |  13 ---
 .../themes/default/projects/index.html.twig   |  74 ------------
 .../themes/default/projects/show.html.twig    |  35 ------
 .../security/forgot_password.html.twig        |  22 ----
 .../themes/default/security/login.html.twig   |  46 --------
 .../default/security/mail/recovery.html.twig  |   9 --
 .../security/mail/registration.html.twig      |  39 -------
 .../security/recovery_mail_sent.html.twig     |  11 --
 .../default/security/register.html.twig       |  60 ----------
 .../security/registration_finished.html.twig  |  19 ----
 .../security/resend_activation.html.twig      |  20 ----
 .../default/security/reset_password.html.twig |  12 --
 .../default/user/edit_profile.html.twig       |  55 ---------
 .../themes/default/user/list_users.html.twig  |  49 --------
 .../default/user/show_profile.html.twig       |  63 -----------
 18 files changed, 1 insertion(+), 700 deletions(-)
 create mode 120000 templates/themes/default
 delete mode 100644 templates/themes/default/_header.html.twig
 delete mode 100644 templates/themes/default/base.html.twig
 delete mode 100644 templates/themes/default/pages/index.html.twig
 delete mode 100644 templates/themes/default/projects/index.html.twig
 delete mode 100644 templates/themes/default/projects/show.html.twig
 delete mode 100644 templates/themes/default/security/forgot_password.html.twig
 delete mode 100644 templates/themes/default/security/login.html.twig
 delete mode 100644 templates/themes/default/security/mail/recovery.html.twig
 delete mode 100644 templates/themes/default/security/mail/registration.html.twig
 delete mode 100644 templates/themes/default/security/recovery_mail_sent.html.twig
 delete mode 100644 templates/themes/default/security/register.html.twig
 delete mode 100644 templates/themes/default/security/registration_finished.html.twig
 delete mode 100644 templates/themes/default/security/resend_activation.html.twig
 delete mode 100644 templates/themes/default/security/reset_password.html.twig
 delete mode 100644 templates/themes/default/user/edit_profile.html.twig
 delete mode 100644 templates/themes/default/user/list_users.html.twig
 delete mode 100644 templates/themes/default/user/show_profile.html.twig

diff --git a/templates/themes/default b/templates/themes/default
new file mode 120000
index 0000000..be2ab77
--- /dev/null
+++ b/templates/themes/default
@@ -0,0 +1 @@
+24unix.net
\ No newline at end of file
diff --git a/templates/themes/default/_header.html.twig b/templates/themes/default/_header.html.twig
deleted file mode 100644
index de4e893..0000000
--- a/templates/themes/default/_header.html.twig
+++ /dev/null
@@ -1,68 +0,0 @@
-<nav class="navbar navbar-expand-md navbar-dark bg-dark sticky-top px-sm-5 border-0">
-
-    <a class="navbar-brand border-0" href="{{ path('app_main') }}">
-        <img src="{{ asset('build/images/24unix/24_logo_bg_64x64.png') }}" alt="24unix.net" id="site-logo">
-    </a>
-
-    <button class="navbar-toggler border-0" type="button" data-toggle="collapse" data-target="#CollapsingNavbar">
-        &#9776;
-    </button>
-
-    <div class="collapse navbar-collapse" id="CollapsingNavbar">
-        <ul class="navbar-nav ms-auto">
-            <!--
-            <li>
-                <form class="d-flex">
-                    <input class="form-control me-2 my-2" type="search" placeholder="Search" aria-label="Search">
-                    <button class="btn" type="submit">Search</button>
-                </form>
-            </li>
-            -->
-            {% if is_granted('ROLE_USER') %}
-                <li class="nav-item dropdown me-auto">
-                    <button type="button" id="navbar-dropdown" data-bs-target="#dropdown-menu" data-bs-toggle="dropdown"
-                            class="btn btn-outline-dark dropdown-toggle button-login">
-                        {% if app.user.avatar %}
-                            <img class="rounded-circle"
-                                 width="50px"
-                                 src="{{ avatar_asset(app.user.avatar)|imagine_filter('squared_thumbnail_small') }}"
-                                 alt="profile image"/>
-                        {% else %}
-                            {{ app.user.username }}
-                        {% endif %}
-                    </button>
-
-                    <div class="dropdown-menu dropdown-menu-dark dropdown-menu-end" id="dropdown-menu"
-                         aria-labelledby="navbar-dropdown">
-                        <a class="dropdown-item" href="{{ path('app_profile_edit') }}">
-                            <span class="fa fa-lg fa-fw fa-user" aria-hidden="true"></span>
-                            Profile</a>
-                        <a class="dropdown-item" href="#">
-                            <span class="fa fa-lg fa-fw fa-wrench" aria-hidden="true"></span>
-                            Settings</a>
-                        <div class="dropdown-divider"></div>
-                        {% if is_granted('ROLE_ADMIN') %}
-                            <a class="dropdown-item" href="{{ path('admin') }}">
-                                <span class="fa fa-lg fa-fw fa-cog" aria-hidden="true"></span>
-                                Administration
-                            </a>
-                            <div class="dropdown-divider"></div>
-                        {% endif %}
-
-                        <a class="dropdown-item" href="{{ path('security_logout') }}">
-                            <span class="fa fa-lg fa-fw fa-sign-out" aria-hidden="true"></span>&nbsp;
-                            Logout
-                        </a>
-                    </div>
-                </li>
-            {% else %}
-                <li class="nav-item">
-                    <a class="btn btn-primary button-login" href="{{ path('security_login') }}" role="button"
-                       id="buttonLogin">
-                        <span class="fa fa-sign-in fa-lg fa-fw" aria-hidden="true"></span>Log In
-                    </a>
-                </li>
-            {% endif %}
-        </ul>
-    </div>
-</nav>
diff --git a/templates/themes/default/base.html.twig b/templates/themes/default/base.html.twig
deleted file mode 100644
index 2036b29..0000000
--- a/templates/themes/default/base.html.twig
+++ /dev/null
@@ -1,105 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>{% block title %}Spookie{% endblock %}</title>
-    <link rel="icon"
-          href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
-
-    {% block stylesheets %}
-        {{ encore_entry_link_tags('app') }}
-    {% endblock %}
-
-    {% block javascripts %}
-        {{ encore_entry_script_tags('app') }}
-
-        <!-- Matomo -->
-        <script>
-            let _paq = window._paq = window._paq || []
-            /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
-            _paq.push(["trackPageView"])
-            _paq.push(["enableLinkTracking"]);
-            (function () {
-                const u = "https://analytics.24unix.net/"
-                _paq.push(["setTrackerUrl", u + "matomo.php"])
-                _paq.push(["setSiteId", "1"])
-                const d = document, g = d.createElement("script"), s = d.getElementsByTagName("script")[0]
-                g.async = true
-                g.src = u + "matomo.js"
-                s.parentNode.insertBefore(g, s)
-            })()
-        </script>
-        <!-- End Matomo Code -->
-    {% endblock %}
-</head>
-<body>
-
-{% include '@default/_header.html.twig' %}
-
-<!-- sidebar -->
-<nav id="sidebar" class="m-3">
-    <ul class="list-group ml1" id="main-menu">
-        <li class="list-group-item list-group-item-action">
-            <a href="{{ path('app_projects') }}" class="text-decoration-none">
-                <span class="fa fa-lg fa-fw fa-file-code-o" aria-hidden="true" title="Projects"></span>
-                <span class="menuText">&nbsp;Projects</span>
-            </a>
-        </li>
-        <li class="list-group-item list-group-item-action">
-            <a href="//git.24unix.net" class="text-decoration-none" target="_blank">
-                <span class="fa fa-lg fa-fw fa-gitea" aria-hidden="true" title="Gitea"></span>
-                <span class="menuText">&nbsp;Gitea</span>
-            </a>
-            <span class="menuText"><span class="fa fa-external-link" aria-hidden="true"></span></span>
-        </li>
-        <li class="list-group-item list-group-item-action">
-            <a href="//cloud.24unix.net" class="text-decoration-none" target="_blank">
-                <span class="fa fa-lg fa-fw fa-nextcloud" aria-hidden="true" title="NextCloud"></span>
-                <span class="menuText">&nbsp;NextCloud</span>
-            </a>
-            <span class="menuText"><span class="fa fa-external-link" aria-hidden="true"></span></span>
-        </li>
-        <li class="list-group-item list-group-item-action">
-            <a href="#" id="toggleSidebar">
-                <span id="toggleIcon" class="fa fa-lg fa-caret-square-o-left"></span>
-            </a>
-        </li>
-        <!--                <a href="//pastebin.24unix.net">pastebin.24unix.net</a>-->
-    </ul>
-</nav>
-
-<div class="container-fluid" id="content">
-
-
-    <div class="col m-3" id="main_content">
-
-        {% for message in app.flashes('success') %}
-            <div class="alert alert-success">
-                {{ message }}
-            </div>
-        {% endfor %}
-
-        {% for message in app.flashes('error') %}
-            <div class="alert alert-danger">
-                {{ message }}
-            </div>
-        {% endfor %}
-
-        {% block body %}
-            <div class="m-5">
-                <h1 class="title-show">Quote of the Moment</h1>
-                <div class="quote-box p-3">
-                    <span class="fa fa-quote-left"></span><br>
-                    {{ quote | raw | nl2br }}
-                </div>
-            </div>
-        {% endblock %}
-    </div>
-
-</div>
-
-{% include '@default/_footer.html.twig' %}
-
-</body>
-</html>
\ No newline at end of file
diff --git a/templates/themes/default/pages/index.html.twig b/templates/themes/default/pages/index.html.twig
deleted file mode 100644
index 7eb1db0..0000000
--- a/templates/themes/default/pages/index.html.twig
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}page_name{% endblock %}
-
-{% block body %}
-    {{ page.content | raw }}
-    <br>
-    Page created by: <a href="{{ path('app_profile', { 'username': page.owner.username }) }}">{{ page.owner.username }}</a>
-    {{ page.createdAt | ago }}
-    {% if page.modifiedAt %}
-        (last update: {{ page.modifiedAt | ago }})
-    {% endif %}
-{% endblock %}
\ No newline at end of file
diff --git a/templates/themes/default/projects/index.html.twig b/templates/themes/default/projects/index.html.twig
deleted file mode 100644
index 003002d..0000000
--- a/templates/themes/default/projects/index.html.twig
+++ /dev/null
@@ -1,74 +0,0 @@
-{# templates/projects/index.html.twig #}
-{% extends '@default/base.html.twig' %}
-
-{% block title %} Projects {% endblock %}
-
-{% block body %}
-    <div class="container-fluid box">
-        <div class="row">
-            <h2>This is an overview of my current public (and open source) projects.</h2>
-
-            <!-- projects List -->
-            <div class="col-sm-12">
-                {% for project in projects %}
-                    <div class="project-container bg-dark my-4">
-                        <div class="row">
-                            <div class="col-sm-3">
-                                <a href="{{ path('app_projects', { name: project.name }) }}">
-                                    {% if project.teaserImage %}
-                                        <img
-                                                class="project-image"
-                                                src="/uploads/projects/{{  project.teaserImage  }}"
-                                                alt="Teaser">
-                                    {% else %}
-                                        <img
-                                                class="project-image"
-                                                src="{{ asset('build/images/24unix/24_logo_bg_96x96.png') }}"
-                                                alt="Teaser">
-                                    {% endif %}
-                                </a>
-                                <br>
-                                <div>
-                                    {% for developer in project.developer %}
-                                        <a class="align-left blog-details"
-                                           href="{{ path('app_profile', { 'username':developer.username }) }}">
-                                            {% if developer.avatar is not null %}
-                                                <img class="rounded-circle mt-5 mb-4 ms-5"
-                                                     src="{{ avatar_asset(developer.avatar)|imagine_filter('squared_thumbnail_small') }}" alt="profile image"/>
-                                                <br>
-                                            {% endif %}
-                                            <div class="ms-5 mb-4">
-                                                <a href="{{ path('app_profile', { 'username':developer.username }) }}">{{ developer.username }}</a>
-                                            </div>
-                                    {% endfor %}
-                                </div>
-
-                            </div>
-                            <div class="col-sm-8 mt-2">
-                                <a href="{{ path('app_projects', { name: project.name }) }}">
-                                    <div class="article-title d-inline-block pl-3 align-middle">
-                                        <h2>{{ project.name }}</h2>
-                                    </div>
-                                </a>
-                                <br>
-                                <div class="blog-teaser mb-2 pb-2 text-xl-start">
-                                    {{ project.description }}
-                                    <br>
-                                    <br>
-                                    started: {{ project.createdAt | ago }}
-                                </div>
-                            </div>
-                        </div>
-
-                    </div>
-                {% endfor %}
-            </div>
-
-            {% if is_granted('ROLE_ADMIN') %}
-                <div class="text-xl-start">
-                    <a href="{{ path('app_main') }}"><span class="fa fa-plus-circle"></span></a>
-                </div>
-            {%  endif %}
-        </div>
-    </div>
-{% endblock %}
\ No newline at end of file
diff --git a/templates/themes/default/projects/show.html.twig b/templates/themes/default/projects/show.html.twig
deleted file mode 100644
index 4c586f1..0000000
--- a/templates/themes/default/projects/show.html.twig
+++ /dev/null
@@ -1,35 +0,0 @@
-{# templates/blog/blog_show.html.twig #}
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Projects{% endblock %}
-
-{% block body %}
-
-    <div class="row">
-        <div class="col-sm-12">
-            {% if is_granted('ROLE_ADMIN') %}
-                <div class="d-flex justify-content-end">
-                    <a href="{{ path('app_main', { id : project.id }) }}"><span class="fa fa-3x fa-fw fa-edit"></span></a>
-                    <a href="{{ path('app_main', { id : project.id }) }}"><span class="fa fa-3x fa-fw fa-trash"></span></a>
-                </div>
-            {% endif %}
-
-            <div class="show-article-container p-3 mt-4">
-                <div class="show-article-title-container d-inline-block pl-3 align-middle">
-                    <h2>{{ project.name }}</h2>
-                </div>
-                <div>
-                    Source: <a href="{{ project.url }}" target="_blank">{{ project.url }}</a>&nbsp;
-                    <span class="fa fa-external-link" aria-hidden="true"></span>
-                </div>
-                <div class="row">
-                    <div class="col-sm-12">
-                        <div class="article-text">
-                            {{ readme | markdown_to_html }}
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-{%  endblock %}
\ No newline at end of file
diff --git a/templates/themes/default/security/forgot_password.html.twig b/templates/themes/default/security/forgot_password.html.twig
deleted file mode 100644
index c397c08..0000000
--- a/templates/themes/default/security/forgot_password.html.twig
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Reset your password{% endblock %}
-
-{% block body %}
-    {% for flash_error in app.flashes('reset_password_error') %}
-        <div class="alert alert-danger" role="alert">{{ flash_error }}</div>
-    {% endfor %}
-    <h1>Reset your password</h1>
-
-    {{ form_start(requestForm) }}
-        {{ form_row(requestForm.account) }}
-        <div>
-            <small>
-                Enter your email address or your username and we will send you a
-                link to reset your password.
-            </small>
-        </div>
-
-        <button class="btn btn-primary float-end">Send password reset email</button>
-    {{ form_end(requestForm) }}
-{% endblock %}
diff --git a/templates/themes/default/security/login.html.twig b/templates/themes/default/security/login.html.twig
deleted file mode 100644
index 869a19c..0000000
--- a/templates/themes/default/security/login.html.twig
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Log In!{% endblock %}
-
-{% block body %}
-    <div class="container rounded">
-        <div class="row">
-            <div class="login-form bg-dark mt-4 p-4">
-                <form method="post" class="row g-3">
-                    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
-
-                    {% if error %}
-                        <div class="alert-dark alert-danger">
-                            {{ error.messageKey|trans(error.messageData, 'security') }}
-                        </div>
-                    {% endif %}
-
-
-                    <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
-
-                    <div class="col-12">
-                        <label for="inputUsername">Username or eMail</label>
-                        <input type="text" name="username" id="inputUsername"
-                               class="form-control" {#  value="{{ last_username }}" #} required autofocus>
-                    </div>
-                    <div class="col-12">
-                        <label for="inputPassword">Password</label>
-                        <input type="password" name="password" id="inputPassword" class="form-control" required>
-                    </div>
-                    <div class="form-check mx-3">
-                        <label>
-                            <input type="checkbox" name="_remember_me" class="form-check-input">Remember me
-                        </label>
-                    </div>
-                    <div>
-                        <a href="{{ path('security_forgot_password') }}">Forgot password?</a>
-                        <button class="btn btn-primary float-end" type="submit">
-                            Sign in
-                        </button>
-                    </div>
-                    <a href="{{ path('security_register') }}">Need an account? Register now.</a>
-                </form>
-            </div>
-        </div>
-    </div>
-{% endblock %}
diff --git a/templates/themes/default/security/mail/recovery.html.twig b/templates/themes/default/security/mail/recovery.html.twig
deleted file mode 100644
index a651258..0000000
--- a/templates/themes/default/security/mail/recovery.html.twig
+++ /dev/null
@@ -1,9 +0,0 @@
-<h1>Hi!</h1>
-
-<p>To reset your password, please visit the following link</p>
-
-<a href="{{ url('security_recovery_reset', {token: resetToken.token}) }}">Reset password</a>
-
-<p>This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.</p>
-
-<p>Cheers!</p>
diff --git a/templates/themes/default/security/mail/registration.html.twig b/templates/themes/default/security/mail/registration.html.twig
deleted file mode 100644
index 665506f..0000000
--- a/templates/themes/default/security/mail/registration.html.twig
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html lang="en" style="margin:0;padding:0;">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-    <meta name="viewport" content="width=device-width, initial-scale=1"/>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
-    <meta name="format-detection" content="telephone=no"/>
-    <title></title>
-    <style type="text/css">
-        @media screen and (max-width: 599px) {
-            div {
-                max-width: 100% !important;
-            }
-        }
-        body {
-            background-color: #1e1f1e;
-            margin:25px;
-            padding:0;
-        }
-    </style>
-</head>
-<body>
-<div style="border-collapse:collapse;border-spacing:0;">
-    <h1>Hi {{ username }}, please confirm your email!</h1>
-
-    <p>
-        Please confirm your email address by clicking the following link: <br><br>
-        <a href="{{ signedUrl|raw }}">Confirm my Email</a>.
-        <br>
-        This link will expire in {{ expiresAtMessageKey|trans(expiresAtMessageData, 'VerifyEmailBundle') }}.
-    </p>
-
-    <p>
-        Kind regards,
-        24unix.net
-    </p>
-</div>
-</body>
-</html>
diff --git a/templates/themes/default/security/recovery_mail_sent.html.twig b/templates/themes/default/security/recovery_mail_sent.html.twig
deleted file mode 100644
index 8786623..0000000
--- a/templates/themes/default/security/recovery_mail_sent.html.twig
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Password Reset Email Sent{% endblock %}
-
-{% block body %}
-    <p>
-        If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
-        This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
-    </p>
-    <p>If you don't receive an email please check your spam folder or <a href="{{ path('security_forgot_password') }}">try again</a>.</p>
-{% endblock %}
diff --git a/templates/themes/default/security/register.html.twig b/templates/themes/default/security/register.html.twig
deleted file mode 100644
index 6b3a3bc..0000000
--- a/templates/themes/default/security/register.html.twig
+++ /dev/null
@@ -1,60 +0,0 @@
-{% extends '@default/base.html.twig' %}
-{% form_theme registrationForm _self %}
-
-{% block title %}Register{% endblock %}
-
-
-{% block form_row %}
-    {%- set widget_attr = {} -%}
-    {%- if help is not empty -%}
-        {%- set widget_attr = {attr: {'aria-describedby': id ~"_help"}} -%}
-    {%- endif -%}
-
-    {{- form_label(form, null, {
-        label_attr: { class: 'sr-only' }
-    }) -}}
-    {{- form_errors(form) -}}
-    {{- form_widget(form, widget_attr) -}}
-    {{- form_help(form) -}}
-{% endblock %}
-
-
-{% block body %}
-    {% for flash_error in app.flashes('verify_email_error') %}
-        <div class="alert alert-danger" role="alert">{{ flash_error }}</div>
-    {% endfor %}
-
-    <div class="container rounded">
-        <div class="row">
-            <div class="login-form bg-dark mt-4 p-4">
-                <h1 class="h3 mb-3 font-weight-normal">Register</h1>
-
-                {{ form_start(registrationForm, {
-                    attr: { class: 'form-signin' }
-                }) }}
-                <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
-                {{ form_row(registrationForm.username, {
-                    attr: { placeholder: 'User name' }
-                }) }}
-                {{ form_row(registrationForm.email, {
-                    attr: { placeholder: 'eMail' }
-                }) }}
-                {{ form_row(registrationForm.password.first, {
-                    attr: { placeholder: 'Password'}
-                }) }}
-                {{ form_row(registrationForm.password.second, {
-                    attr: { placeholder: 'Confirm password'}
-                }) }}
-
-                {{ form_row(registrationForm.agreeTerms) }}
-
-                <button type="submit" class="btn btn-primary float-end">Register</button>
-                {{ form_end(registrationForm) }}
-
-            </div>
-        </div>
-    </div>
-
-{% endblock %}
-
-
diff --git a/templates/themes/default/security/registration_finished.html.twig b/templates/themes/default/security/registration_finished.html.twig
deleted file mode 100644
index 5f12a74..0000000
--- a/templates/themes/default/security/registration_finished.html.twig
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Registration finished{% endblock %}
-
-{% block body %}
-
-    <div class="container rounded">
-        <div class="row">
-            <div class="login-form bg-dark mt-4 p-4">
-                <h1 class="h3 mb-3 font-weight-normal">Registration finished</h1>
-                <p>You'll receive an email soon to confirm your identity.</p>
-
-            </div>
-        </div>
-    </div>
-
-{% endblock %}
-
-
diff --git a/templates/themes/default/security/resend_activation.html.twig b/templates/themes/default/security/resend_activation.html.twig
deleted file mode 100644
index c0e89c2..0000000
--- a/templates/themes/default/security/resend_activation.html.twig
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Verify eMail{% endblock %}
-
-{% block body %}
-    <div class="container">
-        <div class="row">
-            <div class="login-form bg-dark mt-4 p-4">
-                <h1 class="h3 mb-3 font-weight-normal">Verify your Email</h1>
-                <p>
-                    A verification email was sent - please check it to enable your
-                    account before logging in.
-                </p>
-                <form method="POST">
-                    <button type="submit" class="btn btn-primary">Re-send Email</button>
-                </form>
-            </div>
-        </div>
-    </div>
-{% endblock %}
diff --git a/templates/themes/default/security/reset_password.html.twig b/templates/themes/default/security/reset_password.html.twig
deleted file mode 100644
index ed95f9b..0000000
--- a/templates/themes/default/security/reset_password.html.twig
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}Reset your password{% endblock %}
-
-{% block body %}
-    <h1>Reset your password</h1>
-
-    {{ form_start(resetForm) }}
-        {{ form_row(resetForm.plainPassword) }}
-        <button class="btn btn-primary">Update password</button>
-    {{ form_end(resetForm) }}
-{% endblock %}
diff --git a/templates/themes/default/user/edit_profile.html.twig b/templates/themes/default/user/edit_profile.html.twig
deleted file mode 100644
index e1e28c1..0000000
--- a/templates/themes/default/user/edit_profile.html.twig
+++ /dev/null
@@ -1,55 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}
-    Profile of {{ user.username }}
-{% endblock %}
-
-{% block body %}
-
-    <div class="container box rounded bg-dark mt-5 mb-5">
-
-        <div class="row">
-            <div class="col-md-3 border-right">
-                <div class="d-flex flex-column align-items-center text-center p-3 py-5">
-                    {% if user.avatar is not null %}
-                        <img class="rounded-circle mt-5 mb-4"
-                             src="{{ avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') }}"
-                             alt="profile image"/>
-                    {% endif %}
-                    {#             {{ form_row(userForm.avatarName, { 'label': false }) }} #}
-                    <form
-                            action="{{ path('user_upload_avatar', { id: user.id}) }}"
-                            method="POST"
-                            enctype="multipart/form-data"
-                            class="dropzone" id="dropzoneForm">
-                    </form>
-                    <div id="preview-content">{{ avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') }}</div>
-                    <span class="font-weight-bold">{{ user.username }}</span>
-                    <span class="text-white-50"><span class="fa fa-lg fa-envelope me-1"></span>{{ user.email }}</span>
-                </div>
-            </div>
-            <div class="col-md-8 border-right">
-                <div class="p-3 py-5">
-                    <div class="d-flex justify-content-between align-items-center mb-3">
-                        <h4 class="text-right">User Profile</h4>
-                    </div>
-                    <div class="row mt-2">
-                        {{ form_start(userForm) }}
-                        {{ form_row(userForm.username) }}
-                        {{ form_row(userForm.firstName) }}
-                        {{ form_row(userForm.lastName) }}
-                        {{ form_row(userForm.email) }}
-                        {{ form_row(userForm.newPassword.first) }}
-                        {{ form_row(userForm.newPassword.second) }}
-                        {{ form_rest(userForm) }}
-                        {{ form_end(userForm) }}
-                    </div>
-                    <div class="mb-5 text-center float-end">
-                        <button class="btn btn-primary profile-button" type="submit">Save Profile</button>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-
-{% endblock %}
diff --git a/templates/themes/default/user/list_users.html.twig b/templates/themes/default/user/list_users.html.twig
deleted file mode 100644
index 7893a17..0000000
--- a/templates/themes/default/user/list_users.html.twig
+++ /dev/null
@@ -1,49 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}
-    Userlist
-{% endblock %}
-
-{% block body %}
-
-
-    <div class="container box rounded bg-dark mt-5 mb-5">
-        <div class="row">
-            <table class="table table-striped">
-                <thead>
-                <tr>
-                    <th scope="col">Username</th>
-                    <th scope="col">First</th>
-                    <th scope="col">Last</th>
-                    <th scope="col">eMail</th>
-                    <th scope="col">Registered</th>
-                    <th scope="col">Manage</th>
-                </tr>
-                </thead>
-                {% for user in users %}
-                    <tr>
-                        <td>
-                            <a href="{{ path('app_main', { '_switch_user': user.username }) }}">{{ user.username }}</a>
-                        </td>
-                        <td>
-                            {{ user.firstName }}
-                        </td>
-                        <td>
-                            {{ user.lastName }}
-                        </td>
-                        <td>
-                            {{ user.email }}
-                        </td>
-                        <td>
-                            {{ user.createdAt|ago }}
-                        </td>
-                        <td>
-                            <button type="button" class="btn btn-outline-primary btn-lg btn-circle ml-2"><i class="fa fa-edit"></i> </button>
-                        </td>
-
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
-    </div>
-{% endblock %}
diff --git a/templates/themes/default/user/show_profile.html.twig b/templates/themes/default/user/show_profile.html.twig
deleted file mode 100644
index abf06e0..0000000
--- a/templates/themes/default/user/show_profile.html.twig
+++ /dev/null
@@ -1,63 +0,0 @@
-{% extends '@default/base.html.twig' %}
-
-{% block title %}
-    Profile of {{ user.username }}
-{% endblock %}
-
-{% block body %}
-
-    <div class="container box rounded bg-dark mt-5 mb-5">
-        <div class="row">
-            <div class="col-md-3 border-right">
-                <div class="d-flex flex-column align-items-center text-center p-3 py-5">
-                    {% if user.avatar is not null %}
-                        <img class="rounded-circle mt-5 mb-4"
-                             src="{{ avatar_asset(user.avatar)|imagine_filter('squared_thumbnail_small') }}" alt="profile image"/>
-                    {% endif %}
-                    <span class="font-weight-bold">{{ user.username }}</span>
-                    {% if is_granted('ROLE_ADMIN') %}
-                        <span class="font-weight-bold">
-                            <a href="{{ path('app_main', { '_switch_user': app.user.username }) }}">switch user {{ user.username }}</a>
-                        </span>
-                    {% endif %}
-
-                    <span class="text-white-50"><i class="fa fa-lg fa-envelope me-1"></i>{{ user.email }}</span>
-                </div>
-            </div>
-            <div class="col-md-5 border-right">
-                <div class="p-3 py-5">
-                    <div class="d-flex justify-content-between align-items-center mb-3">
-                        <h4 class="text-right">User Profile</h4>
-                    </div>
-                    <div class="row mt-2">
-                        <div class="col-md-6">
-                            <label class="labels" for="first-name">First Name</label>
-                            <input type="text" disabled id="first-name" class="form-control" placeholder="First Name"
-                                   value="{{ user.firstName }}">
-                        </div>
-
-                        <div class="col-md-6">
-                            <label class="labels" for="last-name">Last Name</label>
-                            <input type="text" disabled id="last-name" class="form-control" value="{{ user.lastName }}"
-                                   placeholder="Last Name">
-                        </div>
-
-                        <div class="col-md-6">
-                            <label class="labels" for="username">Username</label>
-                            <input type="text" disabled class="form-control" id="username" placeholder="eMail address"
-                                   value="{{ user.email }}">
-                        </div>
-                    </div>
-
-                    <!--
-                    <div class="mt-5 text-center">
-                        <button class="btn btn-primary profile-button" type="button">Save Profile</button>
-                    </div>
-                    -->
-                </div>
-            </div>
-        </div>
-    </div>
-
-
-{% endblock %}

From 3a322a707481d1df185e2d7d28d2a1bc8ca9a11a Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 16:43:20 +0100
Subject: [PATCH 04/17] symlinked

---
 templates/themes/24unix.net/pages/index.html.twig | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/templates/themes/24unix.net/pages/index.html.twig b/templates/themes/24unix.net/pages/index.html.twig
index 7eb1db0..f0f6b6f 100644
--- a/templates/themes/24unix.net/pages/index.html.twig
+++ b/templates/themes/24unix.net/pages/index.html.twig
@@ -10,4 +10,5 @@
     {% if page.modifiedAt %}
         (last update: {{ page.modifiedAt | ago }})
     {% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+

From 4deaf39e899c33c48b6229d1c747a0c3eaca5e38 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 16:59:31 +0100
Subject: [PATCH 05/17] symlinked

---
 .../controllers/upload-avatar_controller.js   | 51 ++++---------------
 1 file changed, 9 insertions(+), 42 deletions(-)

diff --git a/assets/controllers/upload-avatar_controller.js b/assets/controllers/upload-avatar_controller.js
index 96d9dd3..81206cb 100644
--- a/assets/controllers/upload-avatar_controller.js
+++ b/assets/controllers/upload-avatar_controller.js
@@ -9,21 +9,6 @@ export default class extends Controller {
 	}
 
 	connect() {
-		console.log('image', this.avatarImageValue)
-		console.log('id', this.userIdValue)
-
-		/*
-		const formElement = document.getElementById('avatarDropzone')
-		const dzMessage = document.getElementsByClassName('dz-message')
-		formElement.style.backgroundImage = `url('${this.avatarImageValue}')`
-		formElement.style.backgroundRepeat = 'no-repeat'
-		formElement.style.backgroundPosition = 'center center'
-		formElement.style.backgroundSize = 'auto'
-		formElement.style.borderRadius = '25px'
-		console.log('formelement', formElement)
-*/
-
-		console.log('init Dropzone')
 		let avatarDropzone = new Dropzone('#avatarDropzone', {
 			url: `/user/upload/avatar/${this.userIdValue}`,
 			avatarUrl: this.avatarImageValue,
@@ -37,32 +22,14 @@ export default class extends Controller {
 				const callback = null // Optional callback when it's done
 				const crossOrigin = null // Added to the `img` tag for crossOrigin handling
 				const resizeThumbnail = false // Tells Dropzone whether it should resize the image first
-				avatarDropzone.displayExistingFile(mockFile, avatarDropzone.options.avatarUrl, callback, crossOrigin, resizeThumbnail);
-				avatarDropzone.files.push(mockFile); // line missing in official docs
-
-				/*
-				console.log('url', avatarDropzone.options.foo)
-				this.hiddenFileInput.removeAttribute('multiple')
-				this.on('maxfilesexceeded', (file) => {
-					this.removeAllFiles()
-					this.addFile(file)
-				})
-				this.on('error', (file, data) => {
-					console.log('error')
-					if (data.detail) {
-						this.emit('error', file, data.detail)
-					}
-				})
-				console.log('beforemock')
-				const mockFile = { name: 'Name Image', size: 12345, type: 'image/jpeg' }
-				this.emit('addedfile', mockFile)
-				this.emit('success', mockFile)
-				console.log('avi', this.avatarImageValue)
-				this.emit('thumbnail', mockFile, avatarDropzone.options.avatarUrl)
-				console.log('aftermock')
-				this.emit('complete', mockFile);
-				this.files.push(mockFile);
-				*/
+				avatarDropzone.displayExistingFile(
+					mockFile,
+					avatarDropzone.options.avatarUrl,
+					callback,
+					crossOrigin,
+					resizeThumbnail
+				)
+				avatarDropzone.files.push(mockFile) // line missing in official docs
 			},
 			transformFile(file, done) {
 				// Create the image editor overlay
@@ -84,7 +51,7 @@ export default class extends Controller {
 				editor.appendChild(image)
 
 				// Create Cropper.js
-				const cropper = new Cropper(image, {aspectRatio: 1})
+				const cropper = new Cropper(image, { aspectRatio: 1 })
 
 				// Create confirm button at the top left of the viewport
 				const buttonCrop = document.createElement('button')

From 42a4fc1d8a5a4ddaf8f09fbff7e877e9f54ecfc7 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:02:20 +0100
Subject: [PATCH 06/17] dropzone styling

---
 assets/styles/app.scss | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/assets/styles/app.scss b/assets/styles/app.scss
index fbf9f2f..1319010 100644
--- a/assets/styles/app.scss
+++ b/assets/styles/app.scss
@@ -27,9 +27,9 @@ $list-group-hover-bg: darken($list-group, 10%);
 $list-group-active-bg: $list-group;
 $list-group-action-active-bg: $list-group;
 
-@import 'bootstrap/scss/bootstrap';
+@import '~bootstrap';
 @import 'dropzone/dist/dropzone';
-
+@import 'cropperjs';
 @import './components/sidebar';
 
 
@@ -40,19 +40,35 @@ html, body {
   background: #0e0e10;
 }
 
-// Dropzone
 
-.dropzone .dz-preview.dz-image-preview {
+// Dropzone
+.dropzone .dz-preview {
+  background: transparent !important;
+  margin: 0 !important;
+}
+
+.dropzone-container {
+  width: 160px;
+  height: 160px;
+}
+
+.dropzone-preview-filename {
+  word-wrap: anywhere;
+  display: none;
+}
+
+.dropzone-preview-image {
+  flex-basis: 0;
+  min-width: 150px;
+  max-width: 150px;
+  min-height: 150px;
+  max-height: 150px;
+}
+
+.dz-image {
   background: transparent !important;
 }
 
-.article-author-img {
-  width: 80px;
-  margin: 15px;
-}
-
-///
-
 
 #main_content {
   overflow-y: auto;

From 5247d71050765372b4e4096bb6a70a886fd9e184 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:04:31 +0100
Subject: [PATCH 07/17] refactored dropzone stuff into controller

---
 assets/app.js | 55 +++++++++++----------------------------------------
 1 file changed, 11 insertions(+), 44 deletions(-)

diff --git a/assets/app.js b/assets/app.js
index 0bb46da..b98a7ee 100644
--- a/assets/app.js
+++ b/assets/app.js
@@ -11,57 +11,24 @@ import './styles/app.scss'
 import $ from 'jquery'
 import 'bootstrap'
 
-// Dropzone stuff move to component
-import {Dropzone} from 'dropzone'
-
-// TODO handle error (Chapter 26)
-const formElement = $('#dropzoneForm')
-if (formElement) {
-    const previewContent = $('#preview-content').html()
-    console.log(previewContent)
-    const dropzone = new Dropzone('#dropzoneForm', {
-        acceptedFiles: '.jpg, .jpeg, .png',
-        maxFiles: 1,
-        init: function () {
-            this.hiddenFileInput.removeAttribute('multiple')
-            this.on('maxfilesexceeded', (file) => {
-                this.removeAllFiles()
-                this.addFile(file)
-            })
-            this.on('error', (file, data) => {
-                console.log('error');
-                if (data.detail) {
-                    this.emit('error', file, data.detail)
-                }
-
-            })
-        }
-    })
-    console.log('filename', previewContent)
-    const mockFile = { name: previewContent }
-    console.log('file', mockFile)
-    dropzone.displayExistingFile(mockFile)
-}
-
-// End Dropzone stuff move to component
+// start the Stimulus application
+import './bootstrap'
 
 //import './js/index'
+
 // needed for legacy code
 //global.$ = $
 
 if (window.matchMedia('(prefers-color-scheme)').media !== 'not all') {
-    console.log('🎉 Dark mode is supported')
+	console.log('🎉 Dark mode is supported')
 }
 
 $(document).ready(() => {
-    console.log('ready')
-    $('#toggleSidebar').on('click', () => {
-        const toggleIcon = $('#toggleIcon')
-        toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-left')
-        toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-right')
-        $('#sidebar').toggleClass('active')
-    })
+	console.log('ready')
+	$('#toggleSidebar').on('click', () => {
+		const toggleIcon = $('#toggleIcon')
+		toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-left')
+		toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-right')
+		$('#sidebar').toggleClass('active')
+	})
 })
-
-// start the Stimulus application
-//import './bootstrap'

From 28d77f7ec5bb9a29e4a9b6008a658ad93b2abb44 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:05:12 +0100
Subject: [PATCH 08/17] new controllers added by bundles

---
 assets/controllers.json | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/assets/controllers.json b/assets/controllers.json
index a1c6e90..1620b19 100644
--- a/assets/controllers.json
+++ b/assets/controllers.json
@@ -1,4 +1,30 @@
 {
-    "controllers": [],
+    "controllers": {
+        "@symfony/ux-cropperjs": {
+            "cropper": {
+                "enabled": true,
+                "fetch": "eager",
+                "autoimport": {
+                    "cropperjs/dist/cropper.min.css": true,
+                    "@symfony/ux-cropperjs/src/style.css": true
+                }
+            }
+        },
+        "@symfony/ux-dropzone": {
+            "dropzone": {
+                "enabled": true,
+                "fetch": "eager",
+                "autoimport": {
+                    "@symfony/ux-dropzone/src/style.css": true
+                }
+            }
+        },
+        "@symfony/ux-turbo": {
+            "turbo-core": {
+                "enabled": true,
+                "fetch": "eager"
+            }
+        }
+    },
     "entrypoints": []
 }

From d8aca181922f5eaf79a5471ada3e32aaef695b84 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:07:27 +0100
Subject: [PATCH 09/17] changed size of small

---
 config/packages/liip_imagine.yaml | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/config/packages/liip_imagine.yaml b/config/packages/liip_imagine.yaml
index 79bd742..1e7c42f 100644
--- a/config/packages/liip_imagine.yaml
+++ b/config/packages/liip_imagine.yaml
@@ -1,13 +1,15 @@
 # Documentation on how to configure the bundle can be found at: https://symfony.com/doc/current/bundles/LiipImagineBundle/basic-usage.html
 liip_imagine:
     # valid drivers options include "gd" or "gmagick" or "imagick"
-    driver: "gd"
+    driver: "imagick"
+    twig:
+        mode: lazy
 
     filter_sets:
         squared_thumbnail_small:
             filters:
                 thumbnail:
-                    size: [100, 100]
+                    size: [120, 120]
                     mode: outbound
                     allow_upscale: true
         squared_thumbnail_medium:

From f1a8821ba743082b956a6c04e0c4e361fe8d3f9a Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:07:58 +0100
Subject: [PATCH 10/17] some bundles were added

---
 config/bundles.php | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/config/bundles.php b/config/bundles.php
index 08dbdfd..56b2619 100644
--- a/config/bundles.php
+++ b/config/bundles.php
@@ -19,4 +19,9 @@ return [
     SymfonyCasts\Bundle\VerifyEmail\SymfonyCastsVerifyEmailBundle::class => ['all' => true],
     SymfonyCasts\Bundle\ResetPassword\SymfonyCastsResetPasswordBundle::class => ['all' => true],
     Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true],
+    Symfony\UX\Dropzone\DropzoneBundle::class => ['all' => true],
+    Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
+    Flasher\Symfony\FlasherSymfonyBundle::class => ['all' => true],
+    Flasher\SweetAlert\Symfony\FlasherSweetAlertSymfonyBundle::class => ['all' => true],
+    Symfony\UX\Cropperjs\CropperjsBundle::class => ['all' => true],
 ];

From 481849b530c86367d8dfaace1a53123c17081a48 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:08:44 +0100
Subject: [PATCH 11/17] refactored from render to renderForm

---
 src/Controller/SecurityController.php | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php
index d9e0c0d..3ac3761 100644
--- a/src/Controller/SecurityController.php
+++ b/src/Controller/SecurityController.php
@@ -92,8 +92,8 @@ class SecurityController extends AbstractController
             return $this->render(view: '@default/security/registration_finished.html.twig');
         }
 
-        return $this->render(view: '@default/security/register.html.twig', parameters: [
-            'registrationForm' => $form->createView(),
+        return $this->renderForm(view: '@default/security/register.html.twig', parameters: [
+            'registrationForm' => $form,
         ]);
     }
 
@@ -142,8 +142,8 @@ class SecurityController extends AbstractController
             );
         }
 
-        return $this->render(view: '@default/security/forgot_password.html.twig', parameters: [
-            'requestForm' => $form->createView(),
+        return $this->renderForm(view: '@default/security/forgot_password.html.twig', parameters: [
+            'requestForm' => $form,
         ]);
     }
 
@@ -220,8 +220,8 @@ class SecurityController extends AbstractController
             return $this->redirectToRoute(route: 'app_main');
         }
 
-        return $this->render(view: '@default/security/reset_password.html.twig', parameters: [
-            'resetForm' => $form->createView(),
+        return $this->renderForm(view: '@default/security/reset_password.html.twig', parameters: [
+            'resetForm' => $form,
         ]);
     }
 

From f48529b743b65025c801919e147bb2f6e533277e Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:10:54 +0100
Subject: [PATCH 12/17] working version of edit profile

---
 src/Controller/UserController.php | 106 +++++++++++++++++-------------
 1 file changed, 60 insertions(+), 46 deletions(-)

diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php
index 9f20143..0edddab 100644
--- a/src/Controller/UserController.php
+++ b/src/Controller/UserController.php
@@ -6,6 +6,8 @@ use App\Entity\User;
 use App\Form\EditProfileFormType;
 use App\Repository\UserRepository;
 use Doctrine\ORM\EntityManagerInterface;
+use Flasher\Prime\FlasherInterface;
+use Flasher\SweetAlert\Prime\SweetAlertFactory;
 use Sunrise\Slugger\Slugger;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -24,50 +26,6 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
  */
 class UserController extends BaseController
 {
-
-    #[Route(path: '/profile/edit/{username}', name: 'app_profile_edit')]
-    public function editProfile(Request $request, UserRepository $userRepository, UserPasswordHasherInterface $userPasswordHasher, EntityManagerInterface $entityManager, string $username = ''): Response
-    {
-        if ($username !== '') {
-            if ($this->isGranted(attribute: 'ROLE_ADMIN')) {
-                $user = $userRepository->findOneBy([
-                    'username' => $username,
-                ]);
-            } else {
-                throw new AccessDeniedException(message: 'Only admins are allowed to edit foreign profiles.');
-            }
-        } else {
-            $user = $this->getUser();
-        }
-
-        $form = $this->createForm(type: EditProfileFormType::class, data: $user);
-        $form->handleRequest(request: $request);
-
-        if ($form->isSubmitted() && $form->isValid()) {
-            $user = $form->getData();
-            // if there's a new password, use it
-
-            if ($form->get(name: 'newPassword')->getData())
-                $user->setPassword(
-                    password: $userPasswordHasher->hashPassword(
-                        user: $user,
-                        plainPassword: $form->get(name: 'newPassword')->getData()
-                    )
-                );
-
-            $entityManager->persist(entity: $user);
-            $entityManager->flush();
-
-            return $this->redirectToRoute(route: 'app_main');
-        };
-
-        return $this->renderForm(view: '@default/user/edit_profile.html.twig', parameters: [
-            'user' => $user,
-            'userForm' => $form
-        ]);
-    }
-
-
     #[Route(path: '/profile/{username}', name: 'app_profile')]
     public function showProfile(UserRepository $userRepository, string $username = ''): Response
     {
@@ -85,6 +43,61 @@ class UserController extends BaseController
         ]);
     }
 
+    #[Route(path: '/profile/edit/{username}', name: 'app_profile_edit')]
+    public function editProfile(Request $request,
+                                UserRepository $userRepository,
+                                UserPasswordHasherInterface $userPasswordHasher,
+                                EntityManagerInterface $entityManager,
+                                string $username = ''): Response
+    {
+        $user = $this->getUser();
+
+        $editUser = $userRepository->findOneBy(['username' => $username]);
+
+        if ($username !== $editUser->getUsername()) {
+            if (!$this->isGranted(attribute: 'ROLE_ADMIN')) {
+                $this->addFlash(type: 'error', message: 'Only admins are allowed to edit foreign profiles.');
+                return $this->redirectToRoute(route: 'app_main');
+
+            }
+        }
+
+        $form = $this->createForm(type: EditProfileFormType::class, data: $user);
+        $form->handleRequest(request: $request);
+
+        if ($form->isSubmitted() && $form->isValid()) {
+            $user = $form->getData();
+            // if there's a new password, use it
+
+            if ($form->get(name: 'newPassword')->getData()) {
+                $user->setPassword(
+                    password: $userPasswordHasher->hashPassword(
+                        user: $user,
+                        plainPassword: $form->get(name: 'newPassword')->getData()
+                    )
+                );
+            }
+
+            if ($user->getTmpAvatar()) {
+                $user->setAvatar($user->getTmpAvatar());
+                $user->setTmpAvatar('');
+            }
+
+            $entityManager->persist(entity: $user);
+            $entityManager->flush();
+
+            $this->addFlash(type: 'success', message: 'Profile successfully updated.');
+
+            return $this->redirectToRoute(route: 'app_main');
+        };
+
+        return $this->renderForm(view: '@default/user/edit_profile.html.twig', parameters: [
+            'user' => $editUser,
+            'userForm' => $form
+        ]);
+    }
+
+
     #[Route(path: '/list_users/', name: 'app_list_user')]
     public function listUsers(UserRepository $userRepository): Response
     {
@@ -96,7 +109,7 @@ class UserController extends BaseController
     }
 
     // TODO move to a helper class
-    function humanFilesize($bytes, $decimals = 2)
+    function humanFilesize($bytes, $decimals = 2): string
     {
         $sz = 'BKMGTP';
         $factor = floor((strlen($bytes) - 1) / 3);
@@ -132,7 +145,8 @@ class UserController extends BaseController
         $cleanFilename = $slugger->slugify($originalFilename);
         $newFilename = $cleanFilename . '-' . uniqid() . '.' . $uploadedAvatar->guessExtension();
         $uploadedAvatar->move($destination, $newFilename);
-        $user->setAvatar($newFilename);
+        // Store the tmp name, use it on real form submit.
+        $user->setTmpAvatar($newFilename);
         $entityManager->persist(entity: $user);
         $entityManager->flush();
 

From 763e3f49185d4529311d690dd7d44fbfa49294fe Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:11:25 +0100
Subject: [PATCH 13/17] small cli tool to purge unused avatar uploads

---
 src/Command/CronRunCommand.php | 72 ++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)
 create mode 100644 src/Command/CronRunCommand.php

diff --git a/src/Command/CronRunCommand.php b/src/Command/CronRunCommand.php
new file mode 100644
index 0000000..8704968
--- /dev/null
+++ b/src/Command/CronRunCommand.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Command;
+
+use App\Repository\UserRepository;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+#[AsCommand(
+    name: 'cron:run',
+    description: 'Cleanup stale avatars',
+)]
+class CronRunCommand extends Command
+{
+    public function __construct(private readonly UserRepository $userRepository)
+    {
+        parent::__construct();
+    }
+
+    protected function configure(): void
+    {
+        /*
+        $this
+            ->addArgument('arg1', InputArgument::OPTIONAL, 'Argument description')
+            ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description')
+        ;
+        */
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $io = new SymfonyStyle($input, $output);
+
+        $verbose = $input->getOption('verbose');
+
+        $staleCount = $this->deleteStaleAvatars();
+
+        if ($verbose) {
+            if ($staleCount > 0) {
+                $io->writeln("There were " . $staleCount . " stale avatars.");
+            } else {
+                $io->writeln("There were no stale avatars younger than 24 hours.");
+            }
+        }
+
+        return Command::SUCCESS;
+    }
+
+    private function deleteStaleAvatars(): int
+    {
+        $avatarDir = dirname(__DIR__, 2) . '/public/uploads/avatars';
+        $keepTime = 86400; // 24 hours
+        $deletedFiles = 0;
+
+        foreach (glob($avatarDir . '/*') as $entry) {
+            $filectime = filectime($entry);
+            if ($filectime && $filectime + $keepTime < time()) {
+                // check if it in use
+                if ($user = $this->userRepository->findOneBy(['avatar' => basename($entry)])) {
+                    echo basename($entry) . " is in use by: " . $user->getUserIdentifier() . PHP_EOL;
+                }
+                unlink($entry);
+                $deletedFiles++;
+            }
+
+        }
+        return $deletedFiles;
+    }
+}

From a64fa311a001f1619bce04461bbebac5456a18f0 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:12:37 +0100
Subject: [PATCH 14/17] added result type to resendVerifyEmail

---
 src/Controller/SecurityController.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php
index 3ac3761..32ed070 100644
--- a/src/Controller/SecurityController.php
+++ b/src/Controller/SecurityController.php
@@ -330,7 +330,7 @@ class SecurityController extends AbstractController
 
 
     #[Route('/security/resend/verify_email', name: 'security_resend_verify_email')]
-    public function resendVerifyEmail(Request $request, UserRepository $userRepository)
+    public function resendVerifyEmail(Request $request, UserRepository $userRepository): Response
     {
 
         if ($request->isMethod('POST')) {

From 995f47a8883415223299964e5eae3dd236780134 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:13:03 +0100
Subject: [PATCH 15/17] added tmpAvatar

---
 src/Entity/User.php | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/Entity/User.php b/src/Entity/User.php
index 8540453..1b11469 100644
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -64,6 +64,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
     #[ORM\Column]
     private ?DateTimeImmutable $agreedTermsAt = null;
 
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $tmpAvatar = null;
+
     public function __construct()
     {
         $this->projects = new ArrayCollection();
@@ -315,4 +318,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
     {
         // TODO: Implement eraseCredentials() method.
     }
+
+    public function getTmpAvatar(): ?string
+    {
+        return $this->tmpAvatar;
+    }
+
+    public function setTmpAvatar(?string $tmpAvatar): self
+    {
+        $this->tmpAvatar = $tmpAvatar;
+
+        return $this;
+    }
 }

From cd5f4e6590cb219c46c337d72b3bfa9de5894f3d Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:14:43 +0100
Subject: [PATCH 16/17] disabled UX-Dropzone

---
 src/Form/EditProfileFormType.php | 51 +++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 10 deletions(-)

diff --git a/src/Form/EditProfileFormType.php b/src/Form/EditProfileFormType.php
index a60f68b..5ba3b9c 100644
--- a/src/Form/EditProfileFormType.php
+++ b/src/Form/EditProfileFormType.php
@@ -5,34 +5,65 @@ namespace App\Form;
 use App\Entity\User;
 use Symfony\Component\Form\AbstractType;
 use Symfony\Component\Form\Extension\Core\Type\EmailType;
-use Symfony\Component\Form\Extension\Core\Type\FileType;
 use Symfony\Component\Form\Extension\Core\Type\PasswordType;
 use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
 use Symfony\Component\Form\FormBuilderInterface;
 use Symfony\Component\OptionsResolver\OptionsResolver;
-use Symfony\Component\Validator\Constraints\Image;
 use Symfony\Component\Validator\Constraints\Length;
+use Symfony\UX\Cropperjs\Form\CropperType;
+use Symfony\UX\Dropzone\Form\DropzoneType;
 
 class EditProfileFormType extends AbstractType
 {
     public function buildForm(FormBuilderInterface $builder, array $options): void
     {
         $builder
-            ->add(child: 'username')
-            ->add(child: 'firstName')
-            ->add(child: 'lastName')
-            ->add(child: 'email', type: EmailType::class)
+            ->add(child: 'username', options: [
+                'attr' => [
+                    'autocomplete' => 'nickname'
+                ]
+            ])
+            ->add(child: 'firstName', options: [
+                'required' => false,
+                'attr' => [
+                    'autocomplete' => 'given-name'
+                ]
+            ])
+            ->add(child: 'lastName', options: [
+                'required' => false,
+                'attr' => [
+                    'autocomplete' => 'family-name'
+                ]
+
+            ])
+            ->add(child: 'email', type: EmailType::class, options: [
+                'attr' => [
+                    'autocomplete' => 'email'
+                ]
+            ])
+            /*
+            ->add(child: 'avatarName', type: DropzoneType::class, options: [
+                'mapped' => false,
+                'required' => false,
+                'attr' => [
+                    'data-controller' => 'upload-avatar',
+                ]
+            ])
+            */
             ->add(child: 'newPassword', type: RepeatedType::class, options: [
                 'type' => PasswordType::class,
                 'mapped' => false,
                 'invalid_message' => 'The password fields must match.',
-                'options' => ['attr' => ['class' => 'password-field', 'autocomplete' => 'off']],
                 'required' => false,
                 'first_options' => ['label' => 'Password'],
                 'second_options' => ['label' => 'Repeat Password (only needed if you want to update the password)'],
-                'constraints' => [new Length(exactly: ['min' => 6])]
-            ])
-        ;
+                'constraints' => [new Length(exactly: ['min' => 6])],
+                'options' => [
+                    'attr' => [
+                        'autocomplete' => 'new-password'
+                    ]
+                ]
+            ]);
     }
 
     public function configureOptions(OptionsResolver $resolver): void

From 27454709af167f8ff7649b3a635d8b65fc26db84 Mon Sep 17 00:00:00 2001
From: tracer <tracer@24unix.net>
Date: Tue, 15 Nov 2022 17:17:47 +0100
Subject: [PATCH 17/17] before remerve from turbo to master

---
 assets/controllers/crop_avatar_controller.js |  34 -
 composer.json                                |   8 +-
 composer.lock                                | 930 ++++++++++++++++++-
 migrations/Version20221115104149.php         |  31 +
 package.json                                 |  10 +-
 symfony.lock                                 |  15 +
 webpack.config.js                            |   4 +
 yarn.lock                                    |  81 ++
 8 files changed, 1076 insertions(+), 37 deletions(-)
 delete mode 100644 assets/controllers/crop_avatar_controller.js
 create mode 100644 migrations/Version20221115104149.php

diff --git a/assets/controllers/crop_avatar_controller.js b/assets/controllers/crop_avatar_controller.js
deleted file mode 100644
index 2393657..0000000
--- a/assets/controllers/crop_avatar_controller.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Controller } from '@hotwired/stimulus'
-import Swal from 'sweetalert2'
-
-export default class extends Controller {
-	static values = {
-		cropImage: String
-	}
-
-	connect() {
-		console.log('crop', this.cropImageValue)
-		window.CropAvatar = this
-	}
-
-	open() {
-		console.log('open crop')
-		Swal.fire({
-			title: 'Are you sure?',
-			text: "You won't be able to revert this!",
-			icon: 'warning',
-			showCancelButton: true,
-			confirmButtonColor: '#3085d6',
-			cancelButtonColor: '#d33',
-			confirmButtonText: 'Yes, delete it!',
-		}).then((result) => {
-			if (result.isConfirmed) {
-				Swal.fire(
-					'Deleted!',
-					'Your file has been deleted.',
-					'success',
-				)
-			}
-		})
-	}
-}
diff --git a/composer.json b/composer.json
index b632310..c380b50 100644
--- a/composer.json
+++ b/composer.json
@@ -16,6 +16,8 @@
         "league/commonmark": "^2.3",
         "liip/imagine-bundle": "^2.9",
         "nelmio/cors-bundle": "^2.2",
+        "php-flasher/flasher-sweetalert-symfony": "^1.7",
+        "php-flasher/flasher-symfony": "^1.7",
         "phpdocumentor/reflection-docblock": "^5.3",
         "phpstan/phpdoc-parser": "^1.4",
         "rector/rector": "^0.12.21",
@@ -48,6 +50,9 @@
         "symfony/string": "6.1.*",
         "symfony/translation": "6.1.*",
         "symfony/twig-bundle": "6.1.*",
+        "symfony/ux-cropperjs": "^2.5",
+        "symfony/ux-dropzone": "^2.5",
+        "symfony/ux-turbo": "^2.5",
         "symfony/validator": "6.1.*",
         "symfony/web-link": "6.1.*",
         "symfony/webapp-meta": "^1.0",
@@ -57,7 +62,8 @@
         "symfonycasts/verify-email-bundle": "^1.12",
         "twig/extra-bundle": "^3.4",
         "twig/markdown-extra": "^3.4",
-        "twig/twig": "3.4.*"
+        "twig/twig": "3.4.*",
+        "ext-http": "*"
     },
     "config": {
         "allow-plugins": {
diff --git a/composer.lock b/composer.lock
index ccaa754..e4ad933 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "1c940d128112ec366d0f81bc6eccee45",
+    "content-hash": "e475387f74f1428bb35d9943950aa2f8",
     "packages": [
         {
             "name": "dflydev/dot-access-data",
@@ -1689,6 +1689,125 @@
             ],
             "time": "2022-10-17T19:48:16+00:00"
         },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "2.4.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "67c26b443f348a51926030c83481b85718457d3d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d",
+                "reference": "67c26b443f348a51926030c83481b85718457d3d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-factory": "^1.0",
+                "psr/http-message": "^1.0",
+                "ralouphie/getallheaders": "^3.0"
+            },
+            "provide": {
+                "psr/http-factory-implementation": "1.0",
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8.1",
+                "http-interop/http-factory-tests": "^0.9",
+                "phpunit/phpunit": "^8.5.29 || ^9.5.23"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": false
+                },
+                "branch-alias": {
+                    "dev-master": "2.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "George Mponos",
+                    "email": "gmponos@gmail.com",
+                    "homepage": "https://github.com/gmponos"
+                },
+                {
+                    "name": "Tobias Nyholm",
+                    "email": "tobias.nyholm@gmail.com",
+                    "homepage": "https://github.com/Nyholm"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://github.com/sagikazarmark"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "email": "webmaster@tubo-world.de",
+                    "homepage": "https://github.com/Tobion"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/guzzle/psr7/issues",
+                "source": "https://github.com/guzzle/psr7/tree/2.4.3"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-10-26T14:07:24+00:00"
+        },
         {
             "name": "imagine/imagine",
             "version": "1.3.2",
@@ -1751,6 +1870,90 @@
             },
             "time": "2022-04-01T11:58:30+00:00"
         },
+        {
+            "name": "intervention/image",
+            "version": "2.7.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Intervention/image.git",
+                "reference": "04be355f8d6734c826045d02a1079ad658322dad"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad",
+                "reference": "04be355f8d6734c826045d02a1079ad658322dad",
+                "shasum": ""
+            },
+            "require": {
+                "ext-fileinfo": "*",
+                "guzzlehttp/psr7": "~1.1 || ^2.0",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "mockery/mockery": "~0.9.2",
+                "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15"
+            },
+            "suggest": {
+                "ext-gd": "to use GD library based image processing.",
+                "ext-imagick": "to use Imagick based image processing.",
+                "intervention/imagecache": "Caching extension for the Intervention Image library"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.4-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Intervention\\Image\\ImageServiceProvider"
+                    ],
+                    "aliases": {
+                        "Image": "Intervention\\Image\\Facades\\Image"
+                    }
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Intervention\\Image\\": "src/Intervention/Image"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Oliver Vogel",
+                    "email": "oliver@intervention.io",
+                    "homepage": "https://intervention.io/"
+                }
+            ],
+            "description": "Image handling and manipulation library with support for Laravel integration",
+            "homepage": "http://image.intervention.io/",
+            "keywords": [
+                "gd",
+                "image",
+                "imagick",
+                "laravel",
+                "thumbnail",
+                "watermark"
+            ],
+            "support": {
+                "issues": "https://github.com/Intervention/image/issues",
+                "source": "https://github.com/Intervention/image/tree/2.7.2"
+            },
+            "funding": [
+                {
+                    "url": "https://paypal.me/interventionio",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/Intervention",
+                    "type": "github"
+                }
+            ],
+            "time": "2022-05-21T17:30:32+00:00"
+        },
         {
             "name": "knplabs/knp-time-bundle",
             "version": "v1.20.0",
@@ -2489,6 +2692,316 @@
             },
             "time": "2022-09-12T23:36:20+00:00"
         },
+        {
+            "name": "php-flasher/flasher",
+            "version": "v1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-flasher/flasher.git",
+                "reference": "10071abc73d0311a99e1860e88d5895ac4d7535c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-flasher/flasher/zipball/10071abc73d0311a99e1860e88d5895ac4d7535c",
+                "reference": "10071abc73d0311a99e1860e88d5895ac4d7535c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Flasher\\Prime\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Younes KHOUBZA",
+                    "email": "younes.khoubza@gmail.com",
+                    "homepage": "https://www.linkedin.com/in/younes-khoubza",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A powerful and flexible flash notification system for PHP",
+            "homepage": "https://php-flasher.io",
+            "keywords": [
+                "Flasher",
+                "alerts",
+                "flash",
+                "laravel",
+                "lumen",
+                "messages",
+                "notifications",
+                "notify",
+                "noty",
+                "notyf",
+                "php",
+                "pnotify",
+                "sweet alert",
+                "symfony",
+                "toastr"
+            ],
+            "support": {
+                "source": "https://github.com/php-flasher/flasher/tree/v1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/yoeunes",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://ko-fi.com/yoeunes",
+                    "type": "ko_fi"
+                },
+                {
+                    "url": "https://opencollective.com/php-flasher",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/yoeunes",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-07-05T19:33:26+00:00"
+        },
+        {
+            "name": "php-flasher/flasher-sweetalert",
+            "version": "v1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-flasher/flasher-sweetalert.git",
+                "reference": "abab36c6a70405ad9838238338a76853541baa34"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-flasher/flasher-sweetalert/zipball/abab36c6a70405ad9838238338a76853541baa34",
+                "reference": "abab36c6a70405ad9838238338a76853541baa34",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "php-flasher/flasher": "^1.7"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Flasher\\SweetAlert\\Prime\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Younes KHOUBZA",
+                    "email": "younes.khoubza@gmail.com",
+                    "homepage": "https://www.linkedin.com/in/younes-khoubza",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP Flasher adapter for Sweet Alert 2",
+            "homepage": "https://php-flasher.io",
+            "keywords": [
+                "alerts",
+                "laravel",
+                "lumen",
+                "messages",
+                "notifications",
+                "notify",
+                "php",
+                "pnotify",
+                "sweetalert",
+                "symfony"
+            ],
+            "support": {
+                "source": "https://github.com/php-flasher/flasher-sweetalert/tree/v1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/yoeunes",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://ko-fi.com/yoeunes",
+                    "type": "ko_fi"
+                },
+                {
+                    "url": "https://opencollective.com/php-flasher",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/yoeunes",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-07-05T19:36:36+00:00"
+        },
+        {
+            "name": "php-flasher/flasher-sweetalert-symfony",
+            "version": "v1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-flasher/flasher-sweetalert-symfony.git",
+                "reference": "f8f10ad9760b30c8fc1fe4e9891b595a5cd8c51e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-flasher/flasher-sweetalert-symfony/zipball/f8f10ad9760b30c8fc1fe4e9891b595a5cd8c51e",
+                "reference": "f8f10ad9760b30c8fc1fe4e9891b595a5cd8c51e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "php-flasher/flasher-sweetalert": "^1.7",
+                "php-flasher/flasher-symfony": "^1.7"
+            },
+            "type": "symfony-bundle",
+            "autoload": {
+                "psr-4": {
+                    "Flasher\\SweetAlert\\Symfony\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Younes KHOUBZA",
+                    "email": "younes.khoubza@gmail.com",
+                    "homepage": "https://www.linkedin.com/in/younes-khoubza",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP Flasher Symfony adapter for Sweet Alert 2",
+            "homepage": "https://php-flasher.io",
+            "keywords": [
+                "alerts",
+                "bundle",
+                "flex",
+                "laravel",
+                "lumen",
+                "messages",
+                "notifications",
+                "notify",
+                "php",
+                "pnotify",
+                "sweetalert",
+                "symfony",
+                "toastr",
+                "yoeunes"
+            ],
+            "support": {
+                "source": "https://github.com/php-flasher/flasher-sweetalert-symfony/tree/v1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/yoeunes",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://ko-fi.com/yoeunes",
+                    "type": "ko_fi"
+                },
+                {
+                    "url": "https://opencollective.com/php-flasher",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/yoeunes",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-07-05T19:36:36+00:00"
+        },
+        {
+            "name": "php-flasher/flasher-symfony",
+            "version": "v1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-flasher/flasher-symfony.git",
+                "reference": "1e003819402f415d07b8fedabfff527bd164822b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-flasher/flasher-symfony/zipball/1e003819402f415d07b8fedabfff527bd164822b",
+                "reference": "1e003819402f415d07b8fedabfff527bd164822b",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3",
+                "php-flasher/flasher": "^1.7",
+                "symfony/config": "^2.0|^3.0|^4.0|^5.0|^6.0",
+                "symfony/console": "^2.0|^3.0|^4.0|^5.0|^6.0",
+                "symfony/dependency-injection": "^2.0|^3.0|^4.0|^5.0|^6.0",
+                "symfony/http-kernel": "^2.0|^3.0|^4.0|^5.0|^6.0",
+                "symfony/translation": "^2.0|^3.0|^4.0|^5.0|^6.0",
+                "symfony/twig-bundle": "^2.0|^3.0|^4.0|^5.0|^6.0"
+            },
+            "type": "symfony-bundle",
+            "autoload": {
+                "psr-4": {
+                    "Flasher\\Symfony\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Younes KHOUBZA",
+                    "email": "younes.khoubza@gmail.com",
+                    "homepage": "https://www.linkedin.com/in/younes-khoubza",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Symfony bundle integrating PHP Flasher into Symfony applications",
+            "homepage": "https://php-flasher.io",
+            "keywords": [
+                "alerts",
+                "bundle",
+                "flex",
+                "laravel",
+                "lumen",
+                "messages",
+                "notifications",
+                "notify",
+                "php",
+                "pnotify",
+                "symfony",
+                "toastr",
+                "yoeunes"
+            ],
+            "support": {
+                "source": "https://github.com/php-flasher/flasher-symfony/tree/v1.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.paypal.com/paypalme/yoeunes",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://ko-fi.com/yoeunes",
+                    "type": "ko_fi"
+                },
+                {
+                    "url": "https://opencollective.com/php-flasher",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://www.patreon.com/yoeunes",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2022-07-05T19:36:36+00:00"
+        },
         {
             "name": "phpdocumentor/reflection-common",
             "version": "2.2.0",
@@ -2910,6 +3423,114 @@
             },
             "time": "2019-01-08T18:20:26+00:00"
         },
+        {
+            "name": "psr/http-factory",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-factory/tree/master"
+            },
+            "time": "2019-04-30T12:38:16+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/master"
+            },
+            "time": "2016-08-06T14:39:51+00:00"
+        },
         {
             "name": "psr/link",
             "version": "2.0.1",
@@ -3016,6 +3637,50 @@
             },
             "time": "2021-07-14T16:46:02+00:00"
         },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "support": {
+                "issues": "https://github.com/ralouphie/getallheaders/issues",
+                "source": "https://github.com/ralouphie/getallheaders/tree/develop"
+            },
+            "time": "2019-03-08T08:55:37+00:00"
+        },
         {
             "name": "rector/rector",
             "version": "0.12.23",
@@ -8175,6 +8840,269 @@
             ],
             "time": "2022-09-09T09:34:27+00:00"
         },
+        {
+            "name": "symfony/ux-cropperjs",
+            "version": "v2.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/ux-cropperjs.git",
+                "reference": "123b30ff6b539625bcf50413b945a16de6bc9d6e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/ux-cropperjs/zipball/123b30ff6b539625bcf50413b945a16de6bc9d6e",
+                "reference": "123b30ff6b539625bcf50413b945a16de6bc9d6e",
+                "shasum": ""
+            },
+            "require": {
+                "intervention/image": "^2.5",
+                "php": ">=7.2.5",
+                "symfony/config": "^4.4.17|^5.0|^6.0",
+                "symfony/dependency-injection": "^4.4.17|^5.0|^6.0",
+                "symfony/form": "^4.4.17|^5.0|^6.0",
+                "symfony/http-kernel": "^4.4.17|^5.0|^6.0",
+                "symfony/validator": "^4.4.17|^5.0|^6.0"
+            },
+            "conflict": {
+                "symfony/flex": "<1.13"
+            },
+            "require-dev": {
+                "symfony/framework-bundle": "^4.4.17|^5.0|^6.0",
+                "symfony/phpunit-bridge": "^5.2|^6.0",
+                "symfony/twig-bundle": "^4.4.17|^5.0|^6.0",
+                "symfony/var-dumper": "^4.4.17|^5.0|^6.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/ux",
+                    "url": "https://github.com/symfony/ux"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\UX\\Cropperjs\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Titouan Galopin",
+                    "email": "galopintitouan@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Cropper.js integration for Symfony",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "symfony-ux"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/ux-cropperjs/tree/v2.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-11-01T17:19:11+00:00"
+        },
+        {
+            "name": "symfony/ux-dropzone",
+            "version": "v2.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/ux-dropzone.git",
+                "reference": "897dbc3a8e61510c3f9c2ee7fee5596eb9cbf1d6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/ux-dropzone/zipball/897dbc3a8e61510c3f9c2ee7fee5596eb9cbf1d6",
+                "reference": "897dbc3a8e61510c3f9c2ee7fee5596eb9cbf1d6",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/config": "^4.4.17|^5.0|^6.0",
+                "symfony/dependency-injection": "^4.4.17|^5.0|^6.0",
+                "symfony/form": "^4.4.17|^5.0|^6.0",
+                "symfony/http-kernel": "^4.4.17|^5.0|^6.0"
+            },
+            "require-dev": {
+                "symfony/framework-bundle": "^4.4.17|^5.0|^6.0",
+                "symfony/phpunit-bridge": "^5.2|^6.0",
+                "symfony/twig-bundle": "^4.4.17|^5.0|^6.0",
+                "symfony/var-dumper": "^4.4.17|^5.0|^6.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/ux",
+                    "url": "https://github.com/symfony/ux"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\UX\\Dropzone\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Titouan Galopin",
+                    "email": "galopintitouan@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "File input dropzones for Symfony Forms",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "symfony-ux"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/ux-dropzone/tree/v2.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-09-08T14:28:44+00:00"
+        },
+        {
+            "name": "symfony/ux-turbo",
+            "version": "v2.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/ux-turbo.git",
+                "reference": "3476bf0030937824137ad0aa1c9797d7c6085882"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/3476bf0030937824137ad0aa1c9797d7c6085882",
+                "reference": "3476bf0030937824137ad0aa1c9797d7c6085882",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/webpack-encore-bundle": "^1.11"
+            },
+            "conflict": {
+                "symfony/flex": "<1.13"
+            },
+            "require-dev": {
+                "doctrine/annotations": "^1.12",
+                "doctrine/doctrine-bundle": "^2.2",
+                "doctrine/orm": "^2.8 | 3.0",
+                "phpstan/phpstan": "^0.12",
+                "symfony/debug-bundle": "^5.2|^6.0",
+                "symfony/expression-language": "^5.4|^6.0",
+                "symfony/form": "^5.2|^6.0",
+                "symfony/framework-bundle": "^5.2|^6.0",
+                "symfony/mercure-bundle": "^0.3",
+                "symfony/messenger": "^5.2|^6.0",
+                "symfony/panther": "^1.0|^2.0",
+                "symfony/phpunit-bridge": "^5.2.1|^6.0",
+                "symfony/property-access": "^5.2|^6.0",
+                "symfony/security-core": "^5.2|^6.0",
+                "symfony/stopwatch": "^5.2|^6.0",
+                "symfony/twig-bundle": "^5.2|^6.0",
+                "symfony/web-profiler-bundle": "^5.2|^6.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "thanks": {
+                    "name": "symfony/ux",
+                    "url": "https://github.com/symfony/ux"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\UX\\Turbo\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kévin Dunglas",
+                    "email": "kevin@dunglas.fr"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Hotwire Turbo integration for Symfony",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "hotwire",
+                "javascript",
+                "mercure",
+                "symfony-ux",
+                "turbo",
+                "turbo-stream"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/ux-turbo/tree/v2.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-09-08T14:28:44+00:00"
+        },
         {
             "name": "symfony/validator",
             "version": "v6.1.7",
diff --git a/migrations/Version20221115104149.php b/migrations/Version20221115104149.php
new file mode 100644
index 0000000..3ed9d62
--- /dev/null
+++ b/migrations/Version20221115104149.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace DoctrineMigrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\Migrations\AbstractMigration;
+
+/**
+ * Auto-generated Migration: Please modify to your needs!
+ */
+final class Version20221115104149 extends AbstractMigration
+{
+    public function getDescription(): string
+    {
+        return '';
+    }
+
+    public function up(Schema $schema): void
+    {
+        // this up() migration is auto-generated, please modify it to your needs
+        $this->addSql('ALTER TABLE user ADD tmp_avatar VARCHAR(255) DEFAULT NULL');
+    }
+
+    public function down(Schema $schema): void
+    {
+        // this down() migration is auto-generated, please modify it to your needs
+        $this->addSql('ALTER TABLE user DROP tmp_avatar');
+    }
+}
diff --git a/package.json b/package.json
index 74f8f8f..3a00bb9 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,17 @@
 {
     "devDependencies": {
+        "@babel/preset-react": "^7.0.0",
         "@hotwired/stimulus": "^3.0.0",
+        "@hotwired/turbo": "^7.0.1",
         "@popperjs/core": "^2.11.6",
         "@symfony/stimulus-bridge": "^3.0.0",
+        "@symfony/ux-cropperjs": "file:vendor/symfony/ux-cropperjs/Resources/assets",
+        "@symfony/ux-dropzone": "file:vendor/symfony/ux-dropzone/Resources/assets",
+        "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/Resources/assets",
         "@symfony/webpack-encore": "^2.0.0",
         "autoprefixer": "^10.4.7",
         "core-js": "^3.0.0",
+        "cropperjs": "^1.5.9",
         "file-loader": "^6.0.0",
         "jquery": "^3.6.0",
         "less-loader": "^10.0.0",
@@ -13,6 +19,7 @@
         "regenerator-runtime": "^0.13.2",
         "sass": "^1.50.0",
         "sass-loader": "^12.0.0",
+        "sweetalert2": "^11.6.8",
         "ts-loader": "^9.0.0",
         "typescript": "^4.6.4",
         "webpack-manifest-plugin": "^5.0.0",
@@ -44,6 +51,7 @@
         "postcss-import": "^14.1.0",
         "postcss-loader": "^7.0.0",
         "quill": "^1.3.7",
-        "quill-html-edit-button": "^2.2.12"
+        "quill-html-edit-button": "^2.2.12",
+        "toastify-js": "^1.12.0"
     }
 }
diff --git a/symfony.lock b/symfony.lock
index 687a9a1..8fceaae 100644
--- a/symfony.lock
+++ b/symfony.lock
@@ -72,6 +72,12 @@
             "config/packages/nelmio_cors.yaml"
         ]
     },
+    "php-flasher/flasher-sweetalert-symfony": {
+        "version": "v1.7.0"
+    },
+    "php-flasher/flasher-symfony": {
+        "version": "v1.7.0"
+    },
     "sensio/framework-extra-bundle": {
         "version": "6.2",
         "recipe": {
@@ -271,6 +277,15 @@
             "templates/base.html.twig"
         ]
     },
+    "symfony/ux-cropperjs": {
+        "version": "v2.5.0"
+    },
+    "symfony/ux-dropzone": {
+        "version": "v2.5.0"
+    },
+    "symfony/ux-turbo": {
+        "version": "v2.5.0"
+    },
     "symfony/validator": {
         "version": "6.1",
         "recipe": {
diff --git a/webpack.config.js b/webpack.config.js
index 379811b..2555d3e 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -43,5 +43,9 @@ Encore
 		}
 	})
 	.autoProvidejQuery()
+	.enableStimulusBridge(
+		'./assets/controllers.json'
+	)
+	.enableReactPreset()
 
 module.exports = Encore.getWebpackConfig()
diff --git a/yarn.lock b/yarn.lock
index aca37f6..35d4f86 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -461,6 +461,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
+"@babel/plugin-syntax-jsx@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
+  integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.6"
+
 "@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -701,6 +708,39 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
+"@babel/plugin-transform-react-display-name@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415"
+  integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-transform-react-jsx-development@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5"
+  integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==
+  dependencies:
+    "@babel/plugin-transform-react-jsx" "^7.18.6"
+
+"@babel/plugin-transform-react-jsx@^7.18.6":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9"
+  integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-module-imports" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.19.0"
+    "@babel/plugin-syntax-jsx" "^7.18.6"
+    "@babel/types" "^7.19.0"
+
+"@babel/plugin-transform-react-pure-annotations@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844"
+  integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.18.6"
+
 "@babel/plugin-transform-regenerator@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73"
@@ -859,6 +899,18 @@
     "@babel/types" "^7.4.4"
     esutils "^2.0.2"
 
+"@babel/preset-react@^7.0.0":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d"
+  integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-validator-option" "^7.18.6"
+    "@babel/plugin-transform-react-display-name" "^7.18.6"
+    "@babel/plugin-transform-react-jsx" "^7.18.6"
+    "@babel/plugin-transform-react-jsx-development" "^7.18.6"
+    "@babel/plugin-transform-react-pure-annotations" "^7.18.6"
+
 "@babel/runtime@^7.8.4":
   version "7.20.1"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9"
@@ -935,6 +987,11 @@
   resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.1.1.tgz#652f08a8e1d5edcb407340e58818fcff463b5848"
   integrity sha512-e0JpzIaYLsRRXevRDVs0yevabiCvieIWWCwh7VqVXjXM5AOHdjb7AjaKIj34zYFmY1N6HIRRfk915WVMYlHnDA==
 
+"@hotwired/turbo@^7.0.1":
+  version "7.2.4"
+  resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.2.4.tgz#0d35541be32cfae3b4f78c6ab9138f5b21f28a21"
+  integrity sha512-c3xlOroHp/cCZHDOuLp6uzQYEbvXBUVaal0puXoGJ9M8L/KHwZ3hQozD4dVeSN9msHWLxxtmPT1TlCN7gFhj4w==
+
 "@humanwhocodes/config-array@^0.11.6":
   version "0.11.7"
   resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f"
@@ -1059,6 +1116,15 @@
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
 
+"@symfony/ux-cropperjs@file:vendor/symfony/ux-cropperjs/Resources/assets":
+  version "1.1.0"
+
+"@symfony/ux-dropzone@file:vendor/symfony/ux-dropzone/Resources/assets":
+  version "1.1.0"
+
+"@symfony/ux-turbo@file:vendor/symfony/ux-turbo/Resources/assets":
+  version "0.1.0"
+
 "@symfony/webpack-encore@^2.0.0":
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/@symfony/webpack-encore/-/webpack-encore-2.1.0.tgz#353a1b8bc38022046cbbc3d627c4076aca2e28c3"
@@ -2042,6 +2108,11 @@ cosmiconfig@^7.0.0:
     path-type "^4.0.0"
     yaml "^1.10.0"
 
+cropperjs@^1.5.9:
+  version "1.5.12"
+  resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50"
+  integrity sha512-re7UdjE5UnwdrovyhNzZ6gathI4Rs3KGCBSc8HCIjUo5hO42CtzyblmWLj6QWVw7huHyDMfpKxhiO2II77nhDw==
+
 cross-spawn@^7.0.2, cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -5111,6 +5182,11 @@ svgo@^2.7.0:
     picocolors "^1.0.0"
     stable "^0.1.8"
 
+sweetalert2@^11.6.8:
+  version "11.6.8"
+  resolved "https://registry.yarnpkg.com/sweetalert2/-/sweetalert2-11.6.8.tgz#5a515284367bc21b9f7830cbfbbfd09341576cca"
+  integrity sha512-0YHMaqF3DC67EI9uZzHpbU34rQV3acEFlnUCYmDSDNkeNOSFtSlF4DbWilfln+iUYv9s9aqbREXmKZRJqh5G5w==
+
 sync-rpc@^1.3.6:
   version "1.3.6"
   resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7"
@@ -5173,6 +5249,11 @@ to-regex-range@^5.0.1:
   dependencies:
     is-number "^7.0.0"
 
+toastify-js@^1.12.0:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.12.0.tgz#cc1c4f5c7e7380e854e20bedceb51980ea29f64d"
+  integrity sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==
+
 toidentifier@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"