turbo #4
|
@ -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',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
|
@ -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">
|
||||||
|
☰
|
||||||
|
</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>
|
||||||
|
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>
|
|
@ -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"> 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"> 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"> 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>
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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>
|
||||||
|
<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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 %}
|
|
@ -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 %}
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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 %}
|
Loading…
Reference in New Issue