Fixes
Some checks failed
Gitea Actions / Run-Tests-On-Arm64 (push) Failing after 17s
Gitea Actions / Run-Tests-On-Amd64 (push) Failing after 29s

This commit is contained in:
Thomas Peterson 2025-04-11 09:23:07 +02:00
parent f9ce89f6e7
commit 43264fce3b
29 changed files with 1271 additions and 657 deletions

View File

@ -26,6 +26,16 @@
} }
} }
}, },
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
"fetch": "eager"
},
"mercure-turbo-stream": {
"enabled": false,
"fetch": "eager"
}
},
"@symfony/ux-vue": { "@symfony/ux-vue": {
"vue": { "vue": {
"enabled": true, "enabled": true,

View File

@ -0,0 +1,69 @@
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['dialog', 'dynamicContent'];
observer = null;
connect() {
if (this.hasDynamicContentTarget) {
// when the content changes, call this.open()
this.observer = new MutationObserver(() => {
const shouldOpen = this.dynamicContentTarget.innerHTML.trim().length > 0;
if (shouldOpen && !this.dialogTarget.open) {
this.open();
} else if (!shouldOpen && this.dialogTarget.open) {
this.close();
}
});
this.observer.observe(this.dynamicContentTarget, {
childList: true,
characterData: true,
subtree: true
});
}
}
disconnect() {
if (this.observer) {
this.observer.disconnect();
}
if (this.dialogTarget.open) {
this.close();
}
}
open() {
this.dialogTarget.showModal();
document.body.classList.add('overflow-hidden', 'blur-sm');
}
close() {
this.dialogTarget.close();
document.body.classList.remove('overflow-hidden', 'blur-sm');
}
useMedia(e) {
let dataset = e.target.dataset;
document.getElementById(e.target.dataset.htmlId).value = e.target.dataset.id;
// Update preview
document.getElementById(e.target.dataset.htmlId + "-widget").classList.add('media-chooser--choosen');
document.getElementById(e.target.dataset.htmlId + "__preview__title").innerHtml = this.title;
if (e.target.dataset.thumbPath === "") {
} else {
document.getElementById(e.target.dataset.htmlId + "__preview__img").src = e.target.dataset.thumbPath;
}
// Close modal
this.close();
}
clickOutside(event) {
if (event.target === this.dialogTarget) {
this.dialogTarget.close();
}
}
}

View File

@ -18,7 +18,7 @@ import {
Paragraph, Paragraph,
PasteFromOffice, PasteFromOffice,
PictureEditing, PictureEditing,
Table, ImageResize, Table,
TableToolbar, TableToolbar,
TextTransformation, TextTransformation,
ClassicEditor ClassicEditor
@ -45,7 +45,6 @@ EnhancedEditor.defaultConfig = {
ImageCaption, ImageCaption,
ImageStyle, ImageStyle,
ImageToolbar, ImageToolbar,
ImageUpload,
ImageInsertUI, ImageInsertUI,
Indent, Indent,
Link, Link,
@ -57,6 +56,7 @@ EnhancedEditor.defaultConfig = {
Table, Table,
TableToolbar, TableToolbar,
TextTransformation, TextTransformation,
ImageResize,
Flmngr, Flmngr,
], ],
toolbar: { toolbar: {
@ -72,15 +72,15 @@ EnhancedEditor.defaultConfig = {
'outdent', 'outdent',
'indent', 'indent',
'|', '|',
'uploadImage',
'blockQuote', 'blockQuote',
'insertTable', 'insertTable',
'mediaEmbed', 'mediaEmbed',
'undo', 'undo',
'redo', 'redo',
'undo',
'redo',
'|', '|',
'upload', 'filemanager',
'flmngr',
] ]
}, },
image: { image: {
@ -91,9 +91,9 @@ EnhancedEditor.defaultConfig = {
'|', '|',
'toggleImageCaption', 'toggleImageCaption',
'imageTextAlternative', 'imageTextAlternative',
'resizeImage',
'|', '|',
'upload', 'filemanager',
'flmngr',
] ]
}, },
table: { table: {

View File

@ -1,158 +0,0 @@
;(function (factory) {
var registeredInModuleLoader = false;
if (typeof define === 'function' && define.amd) {
define(factory);
registeredInModuleLoader = true;
}
if (typeof exports === 'object') {
module.exports = factory();
registeredInModuleLoader = true;
}
if (!registeredInModuleLoader) {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
if (typeof document === 'undefined') {
return;
}
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
// We're using "expires" because "max-age" is not supported by IE
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue;
}
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue;
}
stringifiedAttributes += '=' + attributes[attributeName];
}
return (document.cookie = key + '=' + value + stringifiedAttributes);
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (!this.json && cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = parts[0].replace(rdecode, decodeURIComponent);
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.set = api;
api.get = function (key) {
return api.call(api, key);
};
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

View File

@ -1,160 +1,33 @@
import { Plugin, Notification, ButtonView } from 'ckeditor5'; import { Plugin, Notification, ButtonView } from 'ckeditor5';
import * as Cookies from "./cookie.js";
//import UploadIcon from "../icons/upload.svg";
import FlmngrCommand from "./flmngrcommand.js";
import UploadCommand from "./uploadcommand.js";
export default class Flmngr extends Plugin { export default class Flmngr extends Plugin {
listenersFlmngrIsReady = [];
static get pluginName() {
return 'Flmngr';
}
static get requires() {
return [
Notification,
//'Image',
// Default Drupal 9 CKEditor 5 will fail to attach Flmngr if these lines are not commented
// due to Link/LinkEditing plugins are not enabled until user adds Link button onto toolbar.
// So these two plugins are optional dependency for Flmngr, it can work without them.
//
// 'Link',
// 'LinkEditing',
];
}
setFlmngr(flmngr) {
const options = this.editor.config.get('flmngr') || this.editor.config.get('Flmngr') || {};
options.integration = options["integration"] || "ckeditor5";
options.integrationType = "flmngr";
let flmngrInstance = flmngr.create(options);
FlmngrCommand.flmngr = flmngrInstance;
let apiLegacy = flmngrInstance; // flmngr
// New API exists only in Flmngr v2
let apiNew = !!apiLegacy.getNewAPI && apiLegacy.getNewAPI(); // Flmngr but without isFlmngrReady & isImgPenReady
this.editor["getFlmngr"] = (onFlmngrIsReady) => {
onFlmngrIsReady(apiNew, apiLegacy); // new way to receive Flmngr
return apiLegacy; // old way to receive Flmngr
};
// Call all previous listeners
for (const l of this.listenersFlmngrIsReady)
l(apiNew, apiLegacy);
window.FlmngrCKEditor5 = flmngrInstance.getNewAPI();
}
init() { init() {
const editor = this.editor;
this.editor["getFlmngr"] = (onFlmngrIsReady) => { // The button must be registered among the UI components of the editor
!!onFlmngrIsReady && this.listenersFlmngrIsReady.push(onFlmngrIsReady); // a new way to receive Flmngr // to be displayed in the toolbar.
return null; // an old way to receive Flmngr, but it is not loaded yet, 'getFlmngr' will be overridden later to return existing values editor.ui.componentFactory.add( 'filemanager', () => {
}; // The button will be an instance of ButtonView.
const button = new ButtonView();
// Include Flmngr JS lib into the document if it was not added by 3rd party code
const apiKey = this.editor.config.get('apiKey') || this.editor.config.get('flmngr.apiKey') || this.editor.config.get('Flmngr.apiKey') || 'FLMNFLMN';
if (window.flmngr) {
// Already loaded by another instance or by using flmngr.js manually
this.setFlmngr(window.flmngr);
} else {
// We will load it and wait
if (!window.onFlmngrAndImgPenLoadedArray)
window.onFlmngrAndImgPenLoadedArray = [];
window.onFlmngrAndImgPenLoadedArray.push(() => {
this.setFlmngr(window.flmngr);
});
let delay = this.editor.config.get('libLoadDelay') || this.editor.config.get('flmngr.libLoadDelay') || this.editor.config.get('Flmngr.libLoadDelay');
if (!delay || parseInt(delay) != delay)
delay = 1;
setTimeout(() => {
// let host = "http" + (Cookies.get("N1ED_HTTPS") === "false" ? "" : "s") + "://" + (!!Cookies.get("N1ED_PREFIX") ? (Cookies.get("N1ED_PREFIX") + ".") : "") + "cloud.n1ed.com";
// Flmngr.includeJS(host + "/v/latest/sdk/flmngr.js?apiKey=" + apiKey);
// Flmngr.includeJS(host + "/v/latest/sdk/imgpen.js?apiKey=" + apiKey);
}, delay);
}
/*if ( !this.editor.plugins.has( 'ImageBlockEditing' ) && !this.editor.plugins.has( 'ImageInlineEditing' ) ) {
throw new CKEditorError( 'flmngr-missing-image-plugin', this.editor );
}*/
// Add the commands
this.editor.commands.add( 'upload', new UploadCommand( this.editor ) );
this.editor.commands.add( 'flmngr', new FlmngrCommand( this.editor ) );
// Add UI button
const componentFactory = this.editor.ui.componentFactory;
const t = this.editor.t;
componentFactory.add( 'upload', locale => {
const command = this.editor.commands.get( 'upload' );
const button = new ButtonView( locale );
button.set( { button.set( {
label: t( 'Upload image or file' ), label: 'Media',
withText: true, withText: true
tooltip: true
} ); } );
button.bind( 'isEnabled' ).to( command );
window.addEventListener(editor.id + '_insertImage', (event) => {
editor.execute( 'insertImage', { source: event.detail } );
}, false);
button.on( 'execute', () => { button.on( 'execute', () => {
this.editor.execute( 'upload' ); window.open(
this.editor.editing.view.focus(); "/apps/backend/media/list/folder/cke/" + editor.id,
"",
"width=800, height=600, resizable=yes, scrollbars=no, status=no, toolbar=no"
);
} ); } );
return button; return button;
} ); } );
componentFactory.add( 'flmngr', locale => {
const command = this.editor.commands.get( 'flmngr' );
const button = new ButtonView( locale );
button.set( {
label: t( 'Browse image or file' ),
withText: true,
tooltip: true
} );
button.bind( 'isEnabled' ).to( command );
button.on( 'execute', () => {
this.editor.execute( 'flmngr' );
this.editor.editing.view.focus();
} );
return button;
} );
}
static includeJS(urlJS) {
let scripts = document.getElementsByTagName("script");
let alreadyExists = false;
let existingScript = null;
for (let i = 0; i < scripts.length; i++) {
let src = decodeURI(scripts[i].getAttribute("src"));
if (src != null && src.indexOf(urlJS) !== -1) {
alreadyExists = true;
existingScript = scripts[i];
}
}
if (!alreadyExists) {
let script = document.createElement("script");
script.type = "text/javascript";
script.src = urlJS;
script.setAttribute("data-by-n1ed", "true");
document.getElementsByTagName("head")[0].appendChild(script);
return script;
} else {
return null;
}
} }
} }

View File

@ -1,205 +0,0 @@
import { Command, findAttributeRange, first } from 'ckeditor5';
import {showWarning} from "./utils.js";
export default class FlmngrCommand extends Command {
static flmngr;
imageExtensions = ['jpeg', 'jpg', 'png', 'bmp', 'svg', 'webp'];
constructor( editor ) {
super( editor );
// Remove default document listener to lower its priority.
this.stopListening( this.editor.model.document, 'change' );
// Lower this command listener priority to be sure that refresh() will be called after link & image refresh.
this.listenTo( this.editor.model.document, 'change', () => this.refresh(), { priority: 'low' } );
}
refresh() {
const imageCommand = this.editor.commands.get( 'insertImage' );
const linkCommand = this.editor.commands.get( 'link' );
this.isEnabled = !imageCommand || // if there is no image command, the button IS always enabled: we will show a message
(imageCommand.isEnabled || (!!linkCommand && linkCommand.isEnabled));
}
isImage(filepath) {
let i = filepath.lastIndexOf(".");
if (i > -1 && i < filepath.length-1) {
let ext = filepath.substr(i + 1).toLowerCase();
return ext === 'jpeg' || ext === 'jpg' || ext === 'png' || ext === 'gif' || ext === "bmp" || ext === "svg" || ext === "webp";
}
return false;
}
// Call a dialog to select local file and upload them ("Upload" action)
executeUpload() {
this.execute2(true);
}
// Call Flmngr ("Browse" action)
execute() {
this.execute2(false);
}
execute2(doUpload) { // false = browse
const imageCommand = this.editor.commands.get( 'insertImage' );
if (!imageCommand) {
let msg = "Please enable CKEditor 5 `Image` plugin in order to use Flmngr file manager";
if (!!window.Drupal)
msg += ":\n\nDrupal users must set `Image Upload` -> `Enable image uploads` checkbox on the page of CKEditor 5 text format";
alert(msg);
return;
}
if (!FlmngrCommand.flmngr) {
// console.log("File manager is not loaded yet");
// return;
}
const selection = this.editor.model.document.selection;
const el = selection.getSelectedElement() || first( selection.getSelectedBlocks() );
let currentUrl = null;
let elA = null;
const position = selection.getFirstPosition();
if ( selection.hasAttribute( 'linkHref' ) ) {
elA = findAttributeRange(position, 'linkHref', selection.getAttribute('linkHref'), this.editor.model).getItems().next().value.textNode;
currentUrl = elA.getAttribute("linkHref");
}
let elImg = null;
if (!!el && (el.name === 'imageBlock' || el.name === 'imageInline')) {
elImg = el;
currentUrl = elImg.getAttribute("src");
elA = null;
}
if (doUpload) {
FlmngrCommand.flmngr.selectFiles({
acceptExtensions: !!elImg ? this.imageExtensions : null,
isMultiple: false,
onFinish: (files) => {
FlmngrCommand.flmngr.upload({
filesOrLinks: files,
onFinish: (urls, paths) => {
this.createOrChange(el, elImg, elA, urls);
},
onFail: (error) => {
showWarning(this.editor, 'Unable to upload files', true, error, false);
}
});
}
})
} else {
mediaBundleBrowser(context.$note.attr('id'));
FlmngrCommand.flmngr.pickFiles({
acceptExtensions: !!elImg ? this.imageExtensions : null,
isMultiple: false,
list: currentUrl ? [currentUrl] : null,
onFinish: (files) => {
this.createOrChange(el, elImg, elA, files.map(f => f.url));
}
});
}
}
createOrChange(el, elImg, elA, urls) {
if (!!elImg) {
this.changeImgSrc(elImg, FlmngrCommand.flmngr.getNoCacheUrl(urls[0]));
} else if (!!elA) {
this.changeAHref(elA, urls[0]);
} else {
// Create new IMG and A elements
let urlsImages = [];
let urlsFiles = [];
for (let url of urls) {
if (this.isImage(url))
urlsImages.push(FlmngrCommand.flmngr.getNoCacheUrl(url));
else
urlsFiles.push(url);
}
for (const url of urlsFiles)
this.createNewA(url);
for (const url of urlsImages)
this.createNewImg(url);
}
}
createNewImg(url) {
this.editor.model.change( writer => {
const imageCommand = this.editor.commands.get( 'insertImage' );
// Check if inserting an image is actually possible - it might be possible to only insert a link.
if ( !imageCommand.isEnabled ) {
showWarning(this.editor, 'Inserting image failed', true, 'Could not insert image at the current position.', true);
return;
}
this.editor.execute( 'insertImage', { source: [url] } );
} );
};
createNewA(url) {
this.editor.model.change( writer => {
const insertPosition = this.editor.model.document.selection.getFirstPosition();
const i = url.lastIndexOf("/");
const filename = url.substr(i + 1);
const title = "Download " + filename;
writer.insertText( title, { linkHref: url }, insertPosition );
} );
};
changeImgSrc(el, url) {
this.editor.model.change( writer => {
writer.setAttribute("src", url, el);
writer.removeAttribute("srcset", el);
writer.removeAttribute("sizes", el);
/*
In Drupal when using existing content with existing image,
CKEditor 5 sets also a custom HTML attribute "src", so
to change an image URL we need not only to set an
attribute "src" of a model of CKEditor 5,
but also and "src" attribute of HTML.
Probably this is a bug or a misconfiguration of
CKEditor 5 in Drupal 9/10.
*/
let attr = el.getAttribute("htmlAttributes");
if (attr) {
delete attr.attributes.src;
delete attr.attributes.srcset;
delete attr.attributes.sizes;
writer.setAttribute("htmlAttributes", attr, el);
}
});
};
changeAHref(el, url) {
this.editor.model.change( writer => {
writer.setAttribute( 'linkHref', url, el );
// TODO: probably change text
});
};
isImage(filepath) {
let i = filepath.lastIndexOf(".");
if (i > -1 && i < filepath.length-1) {
let ext = filepath.substr(i + 1).toLowerCase();
if (this.imageExtensions.indexOf(ext) > -1)
return true;
}
return false;
}
}

View File

@ -1,5 +0,0 @@
import Flmngr from './flmngr.js';
export default {
Flmngr
};

View File

@ -1,12 +0,0 @@
import FlmngrCommand from "./flmngrcommand.js";
export default class UploadCommand extends FlmngrCommand {
execute() {
const flmngrCommand = this.editor.commands.get( 'upload' );
flmngrCommand.executeUpload();
}
}

View File

@ -1,25 +0,0 @@
export function showWarning(
editor,
title,
doLocalizeTitle,
message,
doLocalizeMessage
) {
const notification = editor.plugins.get( 'Notification' );
const t = editor.locale.t;
if (!!notification) {
notification.showWarning(
doLocalizeMessage ? t(message) : message,
{
title: doLocalizeTitle ? t(title) : title,
namespace: 'flmngr'
}
);
} else {
alert(
(doLocalizeTitle ? t(title) : title) + "\n\n" +
doLocalizeMessage ? t(message) : message
);
}
}

View File

@ -4,7 +4,6 @@ import persist from '@alpinejs/persist'
import $ from 'jquery' import $ from 'jquery'
window.$ = window.jQuery = $; window.$ = window.jQuery = $;
import { initTabs } from 'david-ai'; import { initTabs } from 'david-ai';
// Initialize tabs functionality // Initialize tabs functionality
initTabs(); initTabs();
import { registerVueControllerComponents } from '@symfony/ux-vue'; import { registerVueControllerComponents } from '@symfony/ux-vue';

View File

@ -93,6 +93,7 @@
"symfony/ux-autocomplete": "^2.14", "symfony/ux-autocomplete": "^2.14",
"symfony/ux-chartjs": "^2.19", "symfony/ux-chartjs": "^2.19",
"symfony/ux-live-component": "^2.12", "symfony/ux-live-component": "^2.12",
"symfony/ux-turbo": "^2.24",
"symfony/ux-twig-component": "^2.12", "symfony/ux-twig-component": "^2.12",
"symfony/ux-vue": "^2.23", "symfony/ux-vue": "^2.23",
"symfony/validator": "*", "symfony/validator": "*",

235
src/new/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "a020910e9bff4a83b3f41207f9ff93ce", "content-hash": "74118bc5e9236bf6ae9d7f1891c26692",
"packages": [ "packages": [
{ {
"name": "azuyalabs/yasumi", "name": "azuyalabs/yasumi",
@ -1394,26 +1394,29 @@
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
"version": "1.1.4", "version": "1.1.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/deprecations.git", "url": "https://github.com/doctrine/deprecations.git",
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"conflict": {
"phpunit/phpunit": "<=7.5 || >=13"
},
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9 || ^12", "doctrine/coding-standard": "^9 || ^12 || ^13",
"phpstan/phpstan": "1.4.10 || 2.0.3", "phpstan/phpstan": "1.4.10 || 2.1.11",
"phpstan/phpstan-phpunit": "^1.0 || ^2", "phpstan/phpstan-phpunit": "^1.0 || ^2",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
"psr/log": "^1 || ^2 || ^3" "psr/log": "^1 || ^2 || ^3"
}, },
"suggest": { "suggest": {
@ -1433,9 +1436,9 @@
"homepage": "https://www.doctrine-project.org/", "homepage": "https://www.doctrine-project.org/",
"support": { "support": {
"issues": "https://github.com/doctrine/deprecations/issues", "issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/1.1.4" "source": "https://github.com/doctrine/deprecations/tree/1.1.5"
}, },
"time": "2024-12-07T21:18:45+00:00" "time": "2025-04-07T20:06:18+00:00"
}, },
{ {
"name": "doctrine/doctrine-bundle", "name": "doctrine/doctrine-bundle",
@ -3631,16 +3634,16 @@
}, },
{ {
"name": "jms/serializer", "name": "jms/serializer",
"version": "3.32.3", "version": "3.32.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/schmittjoh/serializer.git", "url": "https://github.com/schmittjoh/serializer.git",
"reference": "033c9beab9eb708509a3d400e9f0ffeb2d440e71" "reference": "f5c6227b2664d1e75fda65f1e6c5686a0c034b31"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/schmittjoh/serializer/zipball/033c9beab9eb708509a3d400e9f0ffeb2d440e71", "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/f5c6227b2664d1e75fda65f1e6c5686a0c034b31",
"reference": "033c9beab9eb708509a3d400e9f0ffeb2d440e71", "reference": "f5c6227b2664d1e75fda65f1e6c5686a0c034b31",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3717,15 +3720,19 @@
], ],
"support": { "support": {
"issues": "https://github.com/schmittjoh/serializer/issues", "issues": "https://github.com/schmittjoh/serializer/issues",
"source": "https://github.com/schmittjoh/serializer/tree/3.32.3" "source": "https://github.com/schmittjoh/serializer/tree/3.32.4"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/goetas", "url": "https://github.com/goetas",
"type": "github" "type": "github"
},
{
"url": "https://github.com/scyzoryck",
"type": "github"
} }
], ],
"time": "2025-02-11T23:16:25+00:00" "time": "2025-04-06T18:42:47+00:00"
}, },
{ {
"name": "jms/serializer-bundle", "name": "jms/serializer-bundle",
@ -5787,16 +5794,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "3.8.6", "version": "3.9.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/CarbonPHP/carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd" "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6d16a8a015166fe54e22c042e0805c5363aef50d",
"reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd", "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -5889,7 +5896,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-02-20T17:33:38+00:00" "time": "2025-03-27T12:57:33+00:00"
}, },
{ {
"name": "nicolab/php-ftp-client", "name": "nicolab/php-ftp-client",
@ -12919,16 +12926,16 @@
}, },
{ {
"name": "symfony/stimulus-bundle", "name": "symfony/stimulus-bundle",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/stimulus-bundle.git", "url": "https://github.com/symfony/stimulus-bundle.git",
"reference": "254f4e05cbaa349d4ae68b9b2e6a22995e0887f9" "reference": "e09840304467cda3324cc116c7f4ee23c8ff227c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/254f4e05cbaa349d4ae68b9b2e6a22995e0887f9", "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/e09840304467cda3324cc116c7f4ee23c8ff227c",
"reference": "254f4e05cbaa349d4ae68b9b2e6a22995e0887f9", "reference": "e09840304467cda3324cc116c7f4ee23c8ff227c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -12968,7 +12975,7 @@
"symfony-ux" "symfony-ux"
], ],
"support": { "support": {
"source": "https://github.com/symfony/stimulus-bundle/tree/v2.23.0" "source": "https://github.com/symfony/stimulus-bundle/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -12984,7 +12991,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-01-16T21:55:09+00:00" "time": "2025-03-09T21:10:04+00:00"
}, },
{ {
"name": "symfony/string", "name": "symfony/string",
@ -13506,16 +13513,16 @@
}, },
{ {
"name": "symfony/ux-autocomplete", "name": "symfony/ux-autocomplete",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ux-autocomplete.git", "url": "https://github.com/symfony/ux-autocomplete.git",
"reference": "063926d4eeb07edec0789ef6f9b597fe9027102b" "reference": "27379e70b6dc6079b0cef1c559217ba06a75f1a5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ux-autocomplete/zipball/063926d4eeb07edec0789ef6f9b597fe9027102b", "url": "https://api.github.com/repos/symfony/ux-autocomplete/zipball/27379e70b6dc6079b0cef1c559217ba06a75f1a5",
"reference": "063926d4eeb07edec0789ef6f9b597fe9027102b", "reference": "27379e70b6dc6079b0cef1c559217ba06a75f1a5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -13576,7 +13583,7 @@
"symfony-ux" "symfony-ux"
], ],
"support": { "support": {
"source": "https://github.com/symfony/ux-autocomplete/tree/v2.23.0" "source": "https://github.com/symfony/ux-autocomplete/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -13592,20 +13599,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-02-06T00:35:19+00:00" "time": "2025-03-17T21:06:48+00:00"
}, },
{ {
"name": "symfony/ux-chartjs", "name": "symfony/ux-chartjs",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ux-chartjs.git", "url": "https://github.com/symfony/ux-chartjs.git",
"reference": "bbe034301ac2a89855ec79d6d5123156071a50c0" "reference": "8fe776864ef8e08225d509be9d49cc06b90d7043"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ux-chartjs/zipball/bbe034301ac2a89855ec79d6d5123156071a50c0", "url": "https://api.github.com/repos/symfony/ux-chartjs/zipball/8fe776864ef8e08225d509be9d49cc06b90d7043",
"reference": "bbe034301ac2a89855ec79d6d5123156071a50c0", "reference": "8fe776864ef8e08225d509be9d49cc06b90d7043",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -13656,7 +13663,7 @@
"symfony-ux" "symfony-ux"
], ],
"support": { "support": {
"source": "https://github.com/symfony/ux-chartjs/tree/v2.23.0" "source": "https://github.com/symfony/ux-chartjs/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -13672,26 +13679,27 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-01-05T13:18:49+00:00" "time": "2025-03-09T21:10:04+00:00"
}, },
{ {
"name": "symfony/ux-live-component", "name": "symfony/ux-live-component",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ux-live-component.git", "url": "https://github.com/symfony/ux-live-component.git",
"reference": "840542868a8473b49036ec1ed0c5238d14b075a8" "reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ux-live-component/zipball/840542868a8473b49036ec1ed0c5238d14b075a8", "url": "https://api.github.com/repos/symfony/ux-live-component/zipball/ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
"reference": "840542868a8473b49036ec1ed0c5238d14b075a8", "reference": "ee1a8e5d01f5b3f2f8e6856941fa8c944677e41c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3.0", "symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/property-access": "^5.4.5|^6.0|^7.0", "symfony/property-access": "^5.4.5|^6.0|^7.0",
"symfony/property-info": "^5.4|^6.0|^7.0",
"symfony/stimulus-bundle": "^2.9", "symfony/stimulus-bundle": "^2.9",
"symfony/ux-twig-component": "^2.8", "symfony/ux-twig-component": "^2.8",
"twig/twig": "^3.8.0" "twig/twig": "^3.8.0"
@ -13712,7 +13720,6 @@
"symfony/framework-bundle": "^5.4|^6.0|^7.0", "symfony/framework-bundle": "^5.4|^6.0|^7.0",
"symfony/options-resolver": "^5.4|^6.0|^7.0", "symfony/options-resolver": "^5.4|^6.0|^7.0",
"symfony/phpunit-bridge": "^6.1|^7.0", "symfony/phpunit-bridge": "^6.1|^7.0",
"symfony/property-info": "^5.4|^6.0|^7.0",
"symfony/security-bundle": "^5.4|^6.0|^7.0", "symfony/security-bundle": "^5.4|^6.0|^7.0",
"symfony/serializer": "^5.4|^6.0|^7.0", "symfony/serializer": "^5.4|^6.0|^7.0",
"symfony/twig-bundle": "^5.4|^6.0|^7.0", "symfony/twig-bundle": "^5.4|^6.0|^7.0",
@ -13750,7 +13757,7 @@
"twig" "twig"
], ],
"support": { "support": {
"source": "https://github.com/symfony/ux-live-component/tree/v2.23.0" "source": "https://github.com/symfony/ux-live-component/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -13766,20 +13773,118 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-02-07T23:57:34+00:00" "time": "2025-03-12T08:41:47+00:00"
}, },
{ {
"name": "symfony/ux-twig-component", "name": "symfony/ux-turbo",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ux-twig-component.git", "url": "https://github.com/symfony/ux-turbo.git",
"reference": "f29033b95e93aea2d498dc40eac185ed14b07800" "reference": "22954300bd0b01ca46f17c7890ea15138d9cf67f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/f29033b95e93aea2d498dc40eac185ed14b07800", "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/22954300bd0b01ca46f17c7890ea15138d9cf67f",
"reference": "f29033b95e93aea2d498dc40eac185ed14b07800", "reference": "22954300bd0b01ca46f17c7890ea15138d9cf67f",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/stimulus-bundle": "^2.9.1"
},
"conflict": {
"symfony/flex": "<1.13"
},
"require-dev": {
"dbrekelmans/bdi": "dev-main",
"doctrine/doctrine-bundle": "^2.4.3",
"doctrine/orm": "^2.8 | 3.0",
"phpstan/phpstan": "^1.10",
"symfony/asset-mapper": "^6.4|^7.0",
"symfony/debug-bundle": "^5.4|^6.0|^7.0",
"symfony/expression-language": "^5.4|^6.0|^7.0",
"symfony/form": "^5.4|^6.0|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/mercure-bundle": "^0.3.7",
"symfony/messenger": "^5.4|^6.0|^7.0",
"symfony/panther": "^2.1",
"symfony/phpunit-bridge": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|6.3.*|^7.0",
"symfony/property-access": "^5.4|^6.0|^7.0",
"symfony/security-core": "^5.4|^6.0|^7.0",
"symfony/stopwatch": "^5.4|^6.0|^7.0",
"symfony/twig-bundle": "^6.4|^7.0",
"symfony/ux-twig-component": "^2.21",
"symfony/web-profiler-bundle": "^5.4|^6.0|^7.0"
},
"type": "symfony-bundle",
"extra": {
"thanks": {
"url": "https://github.com/symfony/ux",
"name": "symfony/ux"
}
},
"autoload": {
"psr-4": {
"Symfony\\UX\\Turbo\\": "src/"
}
},
"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.24.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": "2025-04-04T17:29:20+00:00"
},
{
"name": "symfony/ux-twig-component",
"version": "v2.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/ux-twig-component.git",
"reference": "48a46e4c6215d41cc97ba8dff0cff21ea9b255a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/48a46e4c6215d41cc97ba8dff0cff21ea9b255a8",
"reference": "48a46e4c6215d41cc97ba8dff0cff21ea9b255a8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -13833,7 +13938,7 @@
"twig" "twig"
], ],
"support": { "support": {
"source": "https://github.com/symfony/ux-twig-component/tree/v2.23.0" "source": "https://github.com/symfony/ux-twig-component/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -13849,20 +13954,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-01-25T02:19:26+00:00" "time": "2025-03-21T20:14:36+00:00"
}, },
{ {
"name": "symfony/ux-vue", "name": "symfony/ux-vue",
"version": "v2.23.0", "version": "v2.24.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/ux-vue.git", "url": "https://github.com/symfony/ux-vue.git",
"reference": "d96995ea2214591114c7f76e807ae0ce34b439a5" "reference": "69bea457266f425e33274a942d1c7e0d06021b73"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/ux-vue/zipball/d96995ea2214591114c7f76e807ae0ce34b439a5", "url": "https://api.github.com/repos/symfony/ux-vue/zipball/69bea457266f425e33274a942d1c7e0d06021b73",
"reference": "d96995ea2214591114c7f76e807ae0ce34b439a5", "reference": "69bea457266f425e33274a942d1c7e0d06021b73",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -13913,7 +14018,7 @@
"symfony-ux" "symfony-ux"
], ],
"support": { "support": {
"source": "https://github.com/symfony/ux-vue/tree/v2.23.0" "source": "https://github.com/symfony/ux-vue/tree/v2.24.0"
}, },
"funding": [ "funding": [
{ {
@ -13929,7 +14034,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-12-05T14:25:02+00:00" "time": "2025-03-09T21:10:04+00:00"
}, },
{ {
"name": "symfony/validator", "name": "symfony/validator",
@ -17465,16 +17570,16 @@
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",
"version": "3.12.0", "version": "3.12.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630" "reference": "ea16a1f3719783345febd3aab41beb55c8c84bfd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/2d1b63db139c3c6ea0c927698e5160f8b3b8d630", "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ea16a1f3719783345febd3aab41beb55c8c84bfd",
"reference": "2d1b63db139c3c6ea0c927698e5160f8b3b8d630", "reference": "ea16a1f3719783345febd3aab41beb55c8c84bfd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -17545,7 +17650,7 @@
"type": "thanks_dev" "type": "thanks_dev"
} }
], ],
"time": "2025-03-18T05:04:51+00:00" "time": "2025-04-04T12:57:55+00:00"
}, },
{ {
"name": "symfony/browser-kit", "name": "symfony/browser-kit",

View File

@ -64,4 +64,5 @@ return [
Symfonycasts\SassBundle\SymfonycastsSassBundle::class => ['all' => true], Symfonycasts\SassBundle\SymfonycastsSassBundle::class => ['all' => true],
Dunglas\DoctrineJsonOdm\Bundle\DunglasDoctrineJsonOdmBundle::class => ['all' => true], Dunglas\DoctrineJsonOdm\Bundle\DunglasDoctrineJsonOdmBundle::class => ['all' => true],
Symfony\UX\Vue\VueBundle::class => ['all' => true], Symfony\UX\Vue\VueBundle::class => ['all' => true],
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
]; ];

View File

@ -382,4 +382,7 @@ return [
'splitpanes' => [ 'splitpanes' => [
'version' => '4.0.3', 'version' => '4.0.3',
], ],
'@hotwired/turbo' => [
'version' => '7.3.0',
],
]; ];

View File

@ -28,6 +28,7 @@ final class CKEditor5Type extends AbstractType
$attr = $this->stimulusHelper->createStimulusAttributes(); $attr = $this->stimulusHelper->createStimulusAttributes();
$attr->addController('ckeditor5'); $attr->addController('ckeditor5');
$attr->addAttribute("flmg", '/media');
$view->vars['attr'] = $attr->toArray(); $view->vars['attr'] = $attr->toArray();
} }

View File

@ -50,10 +50,389 @@ use Symfony\Component\HttpFoundation\Request;
*/ */
class FolderController extends AbstractController class FolderController extends AbstractController
{ {
#[Route(path: '/list/folder/cke/{htmlId}/{uuid}', defaults: ['uuid' => false, 'htmlId' => false], name: 'psc_shop_media_backend_folder_cke')]
#[Template]
public function ckeAction(
Request $request,
\PSC\Shop\MediaBundle\Helper\MediaManager $mediaManager,
\PSC\System\SettingsBundle\Service\Shop $shopService,
DocumentManager $documentManager,
PaginatorInterface $paginator,
$htmlId,
$uuid
) {
$searchForm = $this->createForm(SearchType::class);
$session = $request->getSession();
if ($uuid) {
$session->set('media-folder-uuid', $uuid);
} else {
if ($session->has('media-folder-uuid')) {
$uuid = $session->get('media-folder-uuid');
;
}
}
$type = $request->get('type');
/**
* @var AbstractMediaHandler $handler
*/
$handler = null;
if ($type) {
$handler = $mediaManager->getHandlerForType($type);
}
$searchForm->handleRequest($request);
/**
* @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop
*/
$selectedShop = $shopService->getSelectedShop();
$cat = new Folder();
$folderForm = $this->createForm(FolderType::class, $cat);
$folderForm->handleRequest($request);
if ($folderForm->isSubmitted() && $folderForm->isValid()) {
$cat->setIcon($request->get('icon', 'fa-file'));
$documentManager->persist($cat);
$documentManager->flush();
$session->set('media-folder-uuid', $cat->getId());
$uuid = $cat->getId();
}
$subCat = new Folder();
$subFolderForm = $this->createForm(SubFolderType::class, $subCat);
$subFolderForm->handleRequest($request);
/**
* @var CachingIterator $folders
*/
$folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->exists(false)
->sort('title', 'ASC')->getQuery()->execute();
if (count($folders->toArray()) == 0) {
$this->createDefaultFolder($documentManager);
/**
* @var Folder[] $folders
*/
$folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->exists(false)
->sort('title', 'ASC')->getQuery()->execute();
}
$level = 1;
foreach ($folders as $cat) {
$subFolders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->equals($cat->getId())
->sort('title', 'ASC')->getQuery()->execute();
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($cat->getId()))->count()
->getQuery()->execute();
$cat->setMedia($count);
foreach($subFolders as $subFolder) {
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($subFolder->getId()))->count()
->getQuery()->execute();
$subFolder->setMedia($count);
$cat->addSubFolder($subFolder);
$subSubFolders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->equals($subFolder->getId())
->sort('title', 'ASC')->getQuery()->execute();
if($uuid == $subFolder->getId()) {
$level = 2;
}
foreach($subSubFolders as $subSubFolder) {
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($subSubFolder->getId()))->count()
->getQuery()->execute();
$subSubFolder->setMedia($count);
$subFolder->addSubFolder($subSubFolder);
if($uuid == $subSubFolder->getId()) {
$level = 3;
}
}
}
}
if (!$uuid) {
$folders->rewind();
$uuid = $folders->current()->getId();
}
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy(array('id' => $uuid));
if(!$selectedFolder) {
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy([]);
}
if ($subFolderForm->isSubmitted() && $subFolderForm->isValid()) {
$subCat->setIcon($request->get('icon', 'fa-file'));
$subCat->setParentId($selectedFolder->getId());
$documentManager->persist($subCat);
$documentManager->flush();
$session->set('media-folder-uuid', $subCat->getId());
$uuid = $subCat->getId();
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy(array('id' => $uuid));
return $this->redirectToRoute('psc_shop_media_backend_folder_show', ['uuid' => $uuid, 'htmlId' => $htmlId, 'modal' => $modal]);
}
$searchTerm = $request->query->get('term', '');
$page = $request->query->getInt('page', 1);
if ($searchForm->isSubmitted() && $searchForm->isValid()) {
$searchTerm = $searchForm->get('term')->getData();
$page = 1;
} else {
$searchForm->get('term')->setData($searchTerm);
}
if ($searchTerm != "") {
$qb = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('media.title')->where('function(){ return ((this.title && this.title.indexOf("' . $searchTerm . '") != -1) || (this.url && this.url.indexOf("' . $searchTerm . '") != -1)); }')
->field('folder.$id')->equals(new ObjectId($selectedFolder->getId()))
->sort('title', 'ASC');
} else {
$qb = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($selectedFolder->getId()))
->sort('title', 'ASC');
}
$pagination = $paginator->paginate($query = $qb->getQuery(), $page, 15);
$pagination->setParam('term', $searchTerm);
return array(
'pagination' => $pagination,
'mediamanager' => $mediaManager,
'level' => $level,
'folders' => $folders,
'selectedFolder' => $selectedFolder,
'folderForm' => $folderForm->createView(),
'subFolderForm' => $subFolderForm->createView(),
'modal' => true,
'handler' => $handler,
'htmlId' => $htmlId,
'searchForm' => $searchForm->createView(),
);
}
#[Route(path: '/list/folder/tw/{uuid}/{modal}/{htmlId}', defaults: ['uuid' => false, 'modal' => false, 'htmlId' => false], name: 'psc_shop_media_backend_folder_tw')]
#[Template]
public function twAction(
Request $request,
\PSC\Shop\MediaBundle\Helper\MediaManager $mediaManager,
\PSC\System\SettingsBundle\Service\Shop $shopService,
DocumentManager $documentManager,
PaginatorInterface $paginator,
$uuid,
$modal,
$htmlId
) {
$searchForm = $this->createForm(SearchType::class);
$session = $request->getSession();
if ($uuid) {
$session->set('media-folder-uuid', $uuid);
} else {
if ($session->has('media-folder-uuid')) {
$uuid = $session->get('media-folder-uuid');
;
}
}
$type = $request->get('type');
/**
* @var AbstractMediaHandler $handler
*/
$handler = null;
if ($type) {
$handler = $mediaManager->getHandlerForType($type);
}
$searchForm->handleRequest($request);
/**
* @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop
*/
$selectedShop = $shopService->getSelectedShop();
$cat = new Folder();
$folderForm = $this->createForm(FolderType::class, $cat);
$folderForm->handleRequest($request);
if ($folderForm->isSubmitted() && $folderForm->isValid()) {
$cat->setIcon($request->get('icon', 'fa-file'));
$documentManager->persist($cat);
$documentManager->flush();
$session->set('media-folder-uuid', $cat->getId());
$uuid = $cat->getId();
}
$subCat = new Folder();
$subFolderForm = $this->createForm(SubFolderType::class, $subCat);
$subFolderForm->handleRequest($request);
/**
* @var CachingIterator $folders
*/
$folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->exists(false)
->sort('title', 'ASC')->getQuery()->execute();
if (count($folders->toArray()) == 0) {
$this->createDefaultFolder($documentManager);
/**
* @var Folder[] $folders
*/
$folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->exists(false)
->sort('title', 'ASC')->getQuery()->execute();
}
$level = 1;
foreach ($folders as $cat) {
$subFolders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->equals($cat->getId())
->sort('title', 'ASC')->getQuery()->execute();
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($cat->getId()))->count()
->getQuery()->execute();
$cat->setMedia($count);
foreach($subFolders as $subFolder) {
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($subFolder->getId()))->count()
->getQuery()->execute();
$subFolder->setMedia($count);
$cat->addSubFolder($subFolder);
$subSubFolders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder')
->field('parent_id')->equals($subFolder->getId())
->sort('title', 'ASC')->getQuery()->execute();
if($uuid == $subFolder->getId()) {
$level = 2;
}
foreach($subSubFolders as $subSubFolder) {
$count = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($subSubFolder->getId()))->count()
->getQuery()->execute();
$subSubFolder->setMedia($count);
$subFolder->addSubFolder($subSubFolder);
if($uuid == $subSubFolder->getId()) {
$level = 3;
}
}
}
}
if (!$uuid) {
$folders->rewind();
$uuid = $folders->current()->getId();
}
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy(array('id' => $uuid));
if(!$selectedFolder) {
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy([]);
}
if ($subFolderForm->isSubmitted() && $subFolderForm->isValid()) {
$subCat->setIcon($request->get('icon', 'fa-file'));
$subCat->setParentId($selectedFolder->getId());
$documentManager->persist($subCat);
$documentManager->flush();
$session->set('media-folder-uuid', $subCat->getId());
$uuid = $subCat->getId();
$selectedFolder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->findOneBy(array('id' => $uuid));
return $this->redirectToRoute('psc_shop_media_backend_folder_tw', ['uuid' => $uuid, 'htmlId' => $htmlId, 'modal' => $modal]);
}
$searchTerm = $request->query->get('term', '');
$page = $request->query->getInt('page', 1);
if ($searchForm->isSubmitted() && $searchForm->isValid()) {
$searchTerm = $searchForm->get('term')->getData();
$page = 1;
} else {
$searchForm->get('term')->setData($searchTerm);
}
if ($searchTerm != "") {
$qb = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('media.title')->where('function(){ return ((this.title && this.title.indexOf("' . $searchTerm . '") != -1) || (this.url && this.url.indexOf("' . $searchTerm . '") != -1)); }')
->field('folder.$id')->equals(new ObjectId($selectedFolder->getId()))
->sort('title', 'ASC');
} else {
$qb = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Media')
->createQueryBuilder('media')
->field('folder.$id')->equals(new ObjectId($selectedFolder->getId()))
->sort('title', 'ASC');
}
$pagination = $paginator->paginate($query = $qb->getQuery(), $page, 15);
$pagination->setParam('term', $searchTerm);
return array(
'pagination' => $pagination,
'mediamanager' => $mediaManager,
'level' => $level,
'folders' => $folders,
'selectedFolder' => $selectedFolder,
'folderForm' => $folderForm->createView(),
'subFolderForm' => $subFolderForm->createView(),
'modal' => $modal,
'handler' => $handler,
'htmlId' => $htmlId,
'searchForm' => $searchForm->createView(),
);
}
/** /**
* Default Seite * Default Seite
* *
*
* @param Request $request * @param Request $request
* @return array * @return array
*/ */
@ -85,14 +464,18 @@ class FolderController extends AbstractController
} }
$type = $request->get('type'); $type = $request->get('type');
/** @var AbstractMediaHandler $handler */ /**
* @var AbstractMediaHandler $handler
*/
$handler = null; $handler = null;
if ($type) { if ($type) {
$handler = $mediaManager->getHandlerForType($type); $handler = $mediaManager->getHandlerForType($type);
} }
$searchForm->handleRequest($request); $searchForm->handleRequest($request);
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /**
* @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop
*/
$selectedShop = $shopService->getSelectedShop(); $selectedShop = $shopService->getSelectedShop();
$cat = new Folder(); $cat = new Folder();
$folderForm = $this->createForm(FolderType::class, $cat); $folderForm = $this->createForm(FolderType::class, $cat);
@ -109,7 +492,9 @@ class FolderController extends AbstractController
$subFolderForm = $this->createForm(SubFolderType::class, $subCat); $subFolderForm = $this->createForm(SubFolderType::class, $subCat);
$subFolderForm->handleRequest($request); $subFolderForm->handleRequest($request);
/** @var CachingIterator $folders */ /**
* @var CachingIterator $folders
*/
$folders = $documentManager $folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder') ->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder') ->createQueryBuilder('folder')
@ -117,7 +502,9 @@ class FolderController extends AbstractController
->sort('title', 'ASC')->getQuery()->execute(); ->sort('title', 'ASC')->getQuery()->execute();
if (count($folders->toArray()) == 0) { if (count($folders->toArray()) == 0) {
$this->createDefaultFolder($documentManager); $this->createDefaultFolder($documentManager);
/** @var Folder[] $folders */ /**
* @var Folder[] $folders
*/
$folders = $documentManager $folders = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder') ->getRepository('PSC\Shop\MediaBundle\Document\Folder')
->createQueryBuilder('folder') ->createQueryBuilder('folder')
@ -242,7 +629,9 @@ class FolderController extends AbstractController
#[Template] #[Template]
public function deleteAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, DocumentManager $documentManager, $uuid, $modal, $htmlId) public function deleteAction(Request $request, \PSC\System\SettingsBundle\Service\Shop $shopService, DocumentManager $documentManager, $uuid, $modal, $htmlId)
{ {
/** @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop */ /**
* @var \PSC\Shop\EntityBundle\Entity\Shop $selectedShop
*/
$selectedShop = $shopService->getSelectedShop(); $selectedShop = $shopService->getSelectedShop();
$folder = $documentManager $folder = $documentManager
->getRepository('PSC\Shop\MediaBundle\Document\Folder') ->getRepository('PSC\Shop\MediaBundle\Document\Folder')
@ -277,7 +666,6 @@ class FolderController extends AbstractController
* @param Request $request * @param Request $request
* @param int $folderId * @param int $folderId
* *
*
* @return array * @return array
*/ */
#[Route(path: '/list/folder/create', name: 'psc_shop_media_backend_folder_sub_create')] #[Route(path: '/list/folder/create', name: 'psc_shop_media_backend_folder_sub_create')]

View File

@ -37,9 +37,11 @@ class MediaHandlerCompilerPass implements CompilerPassInterface
if (!$container->hasParameter('twig.form.resources')) { if (!$container->hasParameter('twig.form.resources')) {
return; return;
} }
$container->setParameter('twig.form.resources', array_merge( $container->setParameter(
array('@PSCShopMedia/form/media_image_widget.html.twig', '@PSCShopMedia/form/media_widget.html.twig'), 'twig.form.resources', array_merge(
array('@PSCShopMedia/form/media_image_widget.html.twig', '@PSCShopMedia/form/tw_media_widget.html.twig', '@PSCShopMedia/form/media_widget.html.twig'),
$container->getParameter('twig.form.resources') $container->getParameter('twig.form.resources')
)); )
);
} }
} }

View File

@ -2,8 +2,10 @@
namespace PSC\Shop\MediaBundle\Form; namespace PSC\Shop\MediaBundle\Form;
use PSC\Shop\MediaBundle\Form\Type\TWMediaType;
use PSC\Shop\MediaBundle\Model\MediaItem; use PSC\Shop\MediaBundle\Model\MediaItem;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
@ -12,13 +14,23 @@ class MediaForm extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('name', null, [ ->add(
'name', TextType::class, [
// added because setDescription() doesn't allow null // added because setDescription() doesn't allow null
// it would be simpler to make the arg to that method nullable // it would be simpler to make the arg to that method nullable
'required' => false,
'empty_data' => '', 'empty_data' => '',
]) ]
->add('description') )
; ->add(
'media', TWMediaType::class, [
// added because setDescription() doesn't allow null
// it would be simpler to make the arg to that method nullable
'required' => false,
'empty_data' => '',
]
)
->add('description', TextType::class, ['required' => false]);
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver): void

View File

@ -0,0 +1,107 @@
<?php
namespace PSC\Shop\MediaBundle\Form\Type;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\EntityManagerInterface;
use PSC\Shop\MediaBundle\Helper\MediaManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* MediaType
*/
class TWMediaType extends AbstractType
{
/**
* @var MediaManager
*/
protected $mediaManager;
/**
* @var \Doctrine\ODM\MongoDB\DocumentManager
*/
protected $mongoManager;
/**
* @param MediaManager $mediaManager The media manager
* @param EntityManagerInterface $objectManager The media manager
* @param DocumentManager $mongoManager The media manager
*/
public function __construct(MediaManager $mediaManager, DocumentManager $mongoManager)
{
$this->mediaManager = $mediaManager;
$this->mongoManager = $mongoManager;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting form the
* top most type. Type extensions can further modify the form.
*
* @param FormBuilderInterface $builder The form builder
* @param array $options The options
*
* @see FormTypeExtensionInterface::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer(
new IdToMediaTransformer($this->mongoManager, $options['current_value_container']),
true
);
$builder->setAttribute('chooser', $options['chooser']);
$builder->setAttribute('mediatype', $options['mediatype']);
}
/**
* @return string
*/
public function getParent()
{
return FormType::class;
}
/**
* Sets the default options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
array(
'compound' => false,
'chooser' => 'psc_shop_media_backend_folder_tw',
'mediatype' => null,
'current_value_container' => new CurrentValueContainer(),
)
);
}
/**
* Returns the name of this type.
*
* @return string The name of this type
*/
public function getBlockPrefix(): string
{
return 'tw_media';
}
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['chooser'] = $form->getConfig()->getAttribute('chooser');
$view->vars['mediatype'] = $form->getConfig()->getAttribute('mediatype');
$view->vars['mediamanager'] = $this->mediaManager;
}
}

View File

@ -4,9 +4,16 @@ namespace PSC\Shop\MediaBundle\Model;
class MediaItem class MediaItem
{ {
public string $name; public ?string $name;
public string $description; public ?string $description;
public ?string $media;
public function __construct()
{
$this->name = "Name";
$this->description = "";
}
public string $media;
} }

View File

@ -1,5 +1,6 @@
parameters: parameters:
psc.shop.media.form.type.media.class: 'PSC\Shop\MediaBundle\Form\Type\MediaType' psc.shop.media.form.type.media.class: 'PSC\Shop\MediaBundle\Form\Type\MediaType'
psc.shop.media.form.type.twmedia.class: 'PSC\Shop\MediaBundle\Form\Type\TWMediaType'
psc.shop.media.folder_manager.class: 'PSC\Shop\MediaBundle\Helper\FolderManager' psc.shop.media.folder_manager.class: 'PSC\Shop\MediaBundle\Helper\FolderManager'
services: services:
@ -49,6 +50,14 @@ services:
public: true public: true
arguments: ['@doctrine_mongodb.odm.default_document_manager', '@@PSC\\System\\SettingsBundle\\Service\\Shop'] arguments: ['@doctrine_mongodb.odm.default_document_manager', '@@PSC\\System\\SettingsBundle\\Service\\Shop']
form.type.twmedia:
class: '%psc.shop.media.form.type.twmedia.class%'
arguments:
- '@PSC\Shop\MediaBundle\Helper\MediaManager'
- '@doctrine_mongodb.odm.default_document_manager'
tags:
- { name: form.type}
form.type.media: form.type.media:
class: '%psc.shop.media.form.type.media.class%' class: '%psc.shop.media.form.type.media.class%'
arguments: arguments:

View File

@ -0,0 +1,193 @@
{% extends 'backend_layout.html.twig' %}
{% block header %}
<div class="header">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6">
<h3>
<i class="fas fa-image"></i>
Media <span>>
Dashboard </span>
</h3>
</div>
</div>
</div>
{% endblock %}
{% block appButtons %}
{% endblock %}
{% block appContent %}
<div class="row">
<div class="col-md-3">
{{ form_start(folderForm, { 'attr': {'class': ''}}) }}
<div class="form-group form-group-sm row">
<div class="col-md-12">
<label>Neuen Ordner anlegen</label>
<div class="input-group input-group-sm">
{{ form_widget(folderForm.title, {attr: {'class': 'form-control'}}) }}
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm"><i class="fa fa-save"></i></button>
</span>
</div>
</div>
</div>
{{ form_end(folderForm) }}
<hr/>
<ul class="nav nav-pills flex-column">
{% for cat in folders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == cat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_cke', {uuid: cat.id, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ cat.media }})</span> <i class="fa {{ cat.icon }}"></i> {{ cat.title }}</a>
{% if cat.subFolders|length > 0 %}
<ul>
{% for subCat in cat.subFolders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == subCat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_cke', {uuid: subCat.id, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ subCat.media }})</span> <i class="fa {{ subCat.icon }}"></i> {{ subCat.title }}</a>
{% if subCat.subFolders|length > 0 %}
<ul>
{% for subSubCat in subCat.subFolders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == subSubCat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_cke', {uuid: subSubCat.id, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ subSubCat.media }})</span> <i class="fa {{ subSubCat.icon }}"></i> {{ subSubCat.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-12">{% if selectedFolder %}<h3>{{ selectedFolder.title }} <small>{{ selectedFolder.id }}</small></h3>{% endif %}</div>
</div>
<br/>
<div class="row">
<div class="col-md-6">{% if selectedFolder %}<a href="{{ path("psc_shop_media_backend_upload_add", {uuid: selectedFolder.id, modal: true, htmlId: htmlId}) }}" class="btn btn-info btn-sm"><span class="fa fa-edit"></span> Dateien hinzufügen (Einzelupload)</a> {% if pagination|length == 0 %} <a href="{{ path("psc_shop_media_backend_folder_delete", {uuid: selectedFolder.id, htmlId: htmlId}) }}" class="btn btn-danger btn-sm"><span class="fa fa-remove"></span> Ordner löschen</a>{% endif %}{% endif %}</div>
<div class="col-md-6">
{{ form_start(searchForm, { 'attr': {'class': ''}}) }}
<div class="input-group input-group-sm mb-3">
{{ form_widget(searchForm.term) }}
<div class="input-group-append">
<button class="btn btn-outline-secondary btn-sm" type="submit">Suche</button>
</div>
</div>
{{ form_end(searchForm) }}
</div>
</div>
{% if level < 3 %}
{{ form_start(subFolderForm, { 'attr': {'class': ''}}) }}
<div class="form-group form-group-sm row">
<div class="col-md-12">
<label>Neuen Unterordner anlegen</label>
<div class="input-group input-group-sm">
{{ form_widget(subFolderForm.title, {attr: {'class': 'form-control'}}) }}
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm"><i class="fa fa-save"></i></button>
</span>
</div>
</div>
</div>
{{ form_end(subFolderForm) }}
{% endif %}
<div class="panel">
<div class="body">
<table class="table">
<thead class="thead-dark">
<tr>
<th></th>
<th>{{ knp_pagination_sortable(pagination, 'Uid', 'uid') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Titel', 'title') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Erzeugt', 'createdAt') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Dateiname', 'originalFilename') }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for media in pagination %}
<tr {% if loop.index is odd %}class="color"{% endif %}>
{% set handler = mediamanager.getHandler(media) %}
{% set imageurlorg = handler.getImageUrl(media, app.request.basePath) %}
{% if imageurlorg is not empty and media.location == 'local' %}
{% if imageurlorg|lower|split('.')|last == 'svg' or 'image/svg' in media.contentType %}
{% set imageurlretina = imageurlorg %}
{% set imageurlinsert = imageurlorg %}
{% set isImage = false %}
{% else %}
{% set imageurlretina = asset(imageurlorg | imagine_filter('psc_backend_media_image')) %}
{% set imageurl = asset(imageurlorg | imagine_filter('psc_backend_media_image')) %}
{% set imageurlinsert = asset(imageurlorg | imagine_filter('psc_backend_media_summernote_image')) %}
{% set isImage = true %}
{% endif %}
{% else %}
{% set imageurlinsert = handler.getImageUrl(media, app.request.basePath) %}
{% set isImage = false %}
{% endif %}
{% set path = "[%s]" | format("M" ~ media.id) %}
<td>
{% if imageurlorg %}
<img src="{{ imageurl }}" srcset="{{ imageurl }} 1x {{ imageurlretina is defined ? ', ' ~ imageurlretina ~ " 2x" }}" alt="{{ media.title }}" class="media-thumbnail__img">
{% else %}
<i class="fas fa-file-o media-thumbnail__icon"></i>
{% endif %}
</td>
<td>{{ media.id }}</td>
<td>{{ media.title }}</td>
<td>{{ media.createdAt|date("d.m.Y") }}</td>
<td>{{ media.originalFilename }}</td>
<td class="text-end">
<button class="btn btn-sm btn-success {% if isImage %}cke_insertImage{% else %}cke_insertFile{% endif %}" data-uuid="{{ media.id }}" data-src-html="{{ imageurl }}" data-src="{{ imageurlinsert }}">einfügen</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(pagination) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script type="module">
import $ from 'jquery'
$(document).ready(function() {
{% if htmlId %}
$(".cke_insertImage").click(function() {
const event = new CustomEvent("{{ htmlId }}_insertImage", {detail: $(this).attr('data-src')});
window.opener.dispatchEvent(event);
window.close();
});
$(".cke_insertFile").click(function() {
window.opener.$('#{{ htmlId }}').summernote('createLink', {
text: 'Link to File',
url: $(this).attr('data-src'),
newWindow: true
});
window.close();
});
{% endif %}
});
</script>
{% endblock %}

View File

@ -0,0 +1,157 @@
{% extends 'modalFrame.html.twig' %}
{% block body %}
<div class="row">
<div class="col-md-3">
{{ form_start(folderForm, { 'attr': {'class': ''}}) }}
<div class="form-group form-group-sm row">
<div class="col-md-12">
<label>Neuen Ordner anlegen</label>
<div class="input-group input-group-sm">
{{ form_widget(folderForm.title, {attr: {'class': 'form-control'}}) }}
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm"><i class="fa fa-save"></i></button>
</span>
</div>
</div>
</div>
{{ form_end(folderForm) }}
<hr/>
<ul class="nav nav-pills flex-column">
{% for cat in folders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == cat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_tw', {uuid: cat.id, modal: modal, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ cat.media }})</span> <i class="fa {{ cat.icon }}"></i> {{ cat.title }}</a>
{% if cat.subFolders|length > 0 %}
<ul>
{% for subCat in cat.subFolders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == subCat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_tw', {uuid: subCat.id, modal: modal, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ subCat.media }})</span> <i class="fa {{ subCat.icon }}"></i> {{ subCat.title }}</a>
{% if subCat.subFolders|length > 0 %}
<ul>
{% for subSubCat in subCat.subFolders %}
<li class="nav-item">
<a class="nav-link {% if selectedFolder.id == subSubCat.id %}active{% endif %}" href="{{ path('psc_shop_media_backend_folder_tw', {uuid: subSubCat.id, modal: modal, htmlId: htmlId}) }}">
<span class="badge bg-dark pull-right">({{ subSubCat.media }})</span> <i class="fa {{ subSubCat.icon }}"></i> {{ subSubCat.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-12">{% if selectedFolder %}<h3>{{ selectedFolder.title }} <small>{{ selectedFolder.id }}</small></h3>{% endif %}</div>
</div>
<br/>
<div class="row">
<div class="col-md-6">{% if selectedFolder %}<a href="{{ path("psc_shop_media_backend_upload_add", {uuid: selectedFolder.id, modal: modal, htmlId: htmlId}) }}" class="btn btn-info btn-sm"><span class="fa fa-edit"></span> Dateien hinzufügen (Einzelupload)</a> {% if pagination|length == 0 %} <a href="{{ path("psc_shop_media_backend_folder_delete", {uuid: selectedFolder.id, modal: modal, htmlId: htmlId}) }}" class="btn btn-danger btn-sm"><span class="fa fa-remove"></span> Ordner löschen</a>{% endif %}{% endif %}</div>
<div class="col-md-6">
{{ form_start(searchForm, { 'attr': {'class': ''}}) }}
<div class="input-group input-group-sm mb-3">
{{ form_widget(searchForm.term) }}
<div class="input-group-append">
<button class="btn btn-outline-secondary btn-sm" type="submit">Suche</button>
</div>
</div>
{{ form_end(searchForm) }}
</div>
</div>
{% if level < 3 %}
{{ form_start(subFolderForm, { 'attr': {'class': ''}}) }}
<div class="form-group form-group-sm row">
<div class="col-md-12">
<label>Neuen Unterordner anlegen</label>
<div class="input-group input-group-sm">
{{ form_widget(subFolderForm.title, {attr: {'class': 'form-control'}}) }}
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm"><i class="fa fa-save"></i></button>
</span>
</div>
</div>
</div>
{{ form_end(subFolderForm) }}
{% endif %}
<div class="panel">
<div class="body">
<table class="table">
<thead class="thead-dark">
<tr>
<th></th>
<th>{{ knp_pagination_sortable(pagination, 'Uid', 'uid') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Titel', 'title') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Erzeugt', 'createdAt') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Dateiname', 'originalFilename') }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for media in pagination %}
<tr {% if loop.index is odd %}class="color"{% endif %}>
{% set handler = mediamanager.getHandler(media) %}
{% set imageurlorg = handler.getImageUrl(media, app.request.basePath) %}
{% if imageurlorg is not empty and media.location == 'local' %}
{% if imageurlorg|lower|split('.')|last == 'svg' or 'image/svg' in media.contentType %}
{% set imageurlretina = imageurlorg %}
{% set imageurlinsert = imageurlorg %}
{% set isImage = false %}
{% else %}
{% set imageurlretina = asset(imageurlorg | imagine_filter('psc_backend_media_image')) %}
{% set imageurl = asset(imageurlorg | imagine_filter('psc_backend_media_image')) %}
{% set imageurlinsert = asset(imageurlorg | imagine_filter('psc_backend_media_summernote_image')) %}
{% set isImage = true %}
{% endif %}
{% else %}
{% set imageurlinsert = handler.getImageUrl(media, app.request.basePath) %}
{% set isImage = false %}
{% endif %}
{% set path = "[%s]" | format("M" ~ media.id) %}
<td>
{% if imageurlorg %}
<img src="{{ imageurl }}" srcset="{{ imageurl }} 1x {{ imageurlretina is defined ? ', ' ~ imageurlretina ~ " 2x" }}" alt="{{ media.title }}" class="media-thumbnail__img">
{% else %}
<i class="fas fa-file-o media-thumbnail__icon"></i>
{% endif %}
</td>
<td>{{ media.id }}</td>
<td>{{ media.title }}</td>
<td>{{ media.createdAt|date("d.m.Y") }}</td>
<td>{{ media.originalFilename }}</td>
<td class="text-end">
{% if modal %}
{% if isImage %}
<a href="javascript:void(0)" alt="{{ media.originalFilename }}" class="js-url-chooser-media-select btn btn-primary btn-sm" data-thumb-path="{{ imageurl }}" data-path="{{ path }}" data-title="{{ media.title|escape('js') }}" data-id="{{ media.id }}">Einfügen</a>
{% else %}
<a href="javascript:void(0)" alt="{{ media.originalFilename }}" class="js-url-chooser-media-select btn btn-primary btn-sm" data-path="{{ path }}" data-title="{{ media.title|escape('js') }}" data-id="{{ media.id }}">Einfügen</a>
{% endif %}
{% endif %}
<a href="{{ path('psc_shop_media_backend_upload_swap', {folder: selectedFolder.id, media: media.id, modal: modal, htmlId: htmlId}) }}" class="btn btn-sm btn-warning">Tauschen</a>
<a href="{{ path('psc_shop_media_backend_media_detail', {folder: selectedFolder.id, uuid: media.id, modal: modal, htmlId: htmlId}) }}" class="btn btn-sm btn-info">Bearbeiten</a>
<button type="button" data-action="modal#useMedia" data-thumb-path="{{ imageurl }}" data-path="{{ path }}" data-title="{{ media.title|escape('js') }}" data-id="{{ media.id }}" data-html-id="{{ htmlId }}">Close</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(pagination) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -59,9 +59,7 @@
</h5> </h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"> <button type="button" class="btn-close" data-bs-dismiss="modal">
</button> </button>
</div> </div>
<!-- Body --> <!-- Body -->
<div class="js-ajax-modal-body modal-body ajax-modal__body"></div> <div class="js-ajax-modal-body modal-body ajax-modal__body"></div>
</div> </div>

View File

@ -0,0 +1,54 @@
{% block tw_media_widget %}
{% apply spaceless %}
<div id="{{ id }}-widget" class="media-chooser {% if(value.ent is defined) %}media-chooser--choosen{% endif %}">
<!-- Hidden input -->
<input id="{{ id }}" type="hidden" name="{{ full_name }}" value="{% if(value.id is defined) %}{{ value.id }}{% endif %}">
<!-- Preview -->
<div class="media-chooser__preview">
<figure class="thumbnail">
{% if(value.ent is defined) %}
{% set media = value.ent %}
{% set handler = mediamanager.getHandler(media) %}
{% set imageurl = handler.getImageUrl(media, app.request.basePath) %}
{% if imageurl is not empty and media.location == 'local' %}
{% if imageurl|lower|split('.')|last == 'svg' or 'image/svg' in media.contentType %}
{% set imageurlretina = imageurl %}
{% else %}
{% set imageurlretina = asset(imageurl | imagine_filter('media_list_thumbnail_retina')) %}
{% set imageurl = asset(imageurl | imagine_filter('media_list_thumbnail')) %}
{% endif %}
{% endif %}
{% if imageurl %}
<img src="{{ imageurl }}" srcset="{{ imageurl }} 1x, {{ imageurlretina is defined ? ', ' ~ imageurlretina ~ " 2x" }}" alt="{{ media.title }}" id="{{ id }}__preview__img" class="thumbnail-img media-chooser__preview__img">
<figcaption id="{{ id }}__preview__title" class="media-chooser__preview__title">
{{ media.title }}
</figcaption>
{% else %}
<i class="fas fa-file-o media-thumbnail__icon"></i>
<figcaption id="{{ id }}__preview__title" class="media-chooser__preview__title">
{{ media.title }}
</figcaption>
{% endif %}
{% else %}
<img id="{{ id }}__preview__img" class="thumbnail-img media-chooser__preview__img">
<figcaption id="{{ id }}__preview__title" class="media-chooser__preview__title"></figcaption>
{% endif %}
</figure>
</div>
<a
data-turbo-frame="modal"
data-action="modal#mediaSelect"
href="{{ path(chooser, {'uuid': 0, 'modal': 1, 'htmlId' : id}) }}"
class="flex items-center space-x-1 bg-psc-500 hover:bg-blue-700 text-white text-sm font-bold px-4 rounded"
>Auswählen</a>
<button type="button" id="{{ id }}__preview__del-btn" class="js-media-chooser-del-preview-btn btn btn-danger media-chooser__preview__del-btn" data-linked-id="{{ id }}">
<i class="fa fa-trash"></i>
</button>
<!-- Select Button -->
</div>
{% endapply %}
{% endblock %}

View File

@ -65,9 +65,6 @@
</div> </div>
</div> </div>
<div class="w-full px-4"> <div class="w-full px-4">
<div {{ vue_component('PackageSearch', {news: news}) }}>
Loading...
</div>
{{ form_row(form.introduction)}} {{ form_row(form.introduction)}}
</div> </div>
</div> </div>
@ -81,6 +78,8 @@
<tr> <tr>
<td>Name</td> <td>Name</td>
<td>Description</td> <td>Description</td>
<td>Media</td>
<td></td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -92,6 +91,9 @@
<td> <td>
{{ form_row(media_form.description) }} {{ form_row(media_form.description) }}
</td> </td>
<td>
{{ form_row(media_form.media) }}
</td>
<td> <td>
{{ form_row(media_form.vars.button_delete, {label: 'X', attr: {class: 'btn btn-outline-danger'}}) }} {{ form_row(media_form.vars.button_delete, {label: 'X', attr: {class: 'btn btn-outline-danger'}}) }}
</td> </td>

View File

@ -937,6 +937,15 @@
"config/routes/ux_live_component.yaml" "config/routes/ux_live_component.yaml"
] ]
}, },
"symfony/ux-turbo": {
"version": "2.24",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.19",
"ref": "9dd2778a116b6e5e01e5e1582d03d5a9e82630de"
}
},
"symfony/ux-twig-component": { "symfony/ux-twig-component": {
"version": "v2.12.0" "version": "v2.12.0"
}, },

View File

@ -20,6 +20,22 @@
</head> </head>
<body class="min-h-screen bg-slate-100 text-gray-900 overflow-y-auto dark:text-gray-100 dark:bg-gray-900 antialiased"> <body class="min-h-screen bg-slate-100 text-gray-900 overflow-y-auto dark:text-gray-100 dark:bg-gray-900 antialiased">
<div
data-controller="modal"
data-action="turbo:before-cache@window->modal#close"
>
<dialog
class="open:flex bg-gray-800 rounded-lg shadow-xl inset-0 w-full md:w-fit md:max-w-[50%] md:min-w-[50%] animate-fade-in backdrop:bg-slate-600 backdrop:opacity-80"
data-modal-target="dialog"
data-action="close->modal#close click->modal#clickOutside"
>
<div class="flex grow p-5">
<div class="grow overflow-auto p-1">
<turbo-frame id="modal" data-modal-target="dynamicContent"></turbo-frame>
</div>
</div>
</dialog>
</div>
<div class="flex h-full w-full overflow-x-clip"> <div class="flex h-full w-full overflow-x-clip">
<div x-data x-show="$store.sideBar.isOpen" x-transition.opacity.500ms="" @click="$store.sideBar.close()" class="fixed inset-0 z-20 w-full h-full bg-gray-900/50 lg:hidden"></div> <div x-data x-show="$store.sideBar.isOpen" x-transition.opacity.500ms="" @click="$store.sideBar.close()" class="fixed inset-0 z-20 w-full h-full bg-gray-900/50 lg:hidden"></div>
<aside x-data :class="$store.sideBar.isOpen ? 'translate-x-0 max-w-[20em] shadow-2xl lg:max-w-[var(--sidebar-width)]' : '-translate-x-full lg:translate-x-0 lg:max-w-[var(--collapsed-sidebar-width)] lg:shadow-2xl rtl:lg:-translate-x-0 rtl:translate-x-full'" class="fixed inset-y-0 left-0 z-20 flex h-screen flex-col overflow-hidden w-[var(--sidebar-width)] bg-white transition-all rtl:left-auto rtl:right-0 lg:z-0 lg:border-r rtl:lg:border-l rtl:lg:border-r-0 dark:bg-gray-800 dark:border-gray-700 -translate-x-full lg:translate-x-0 lg:shadow-2xl rtl:lg:-translate-x-0 rtl:translate-x-full"> <aside x-data :class="$store.sideBar.isOpen ? 'translate-x-0 max-w-[20em] shadow-2xl lg:max-w-[var(--sidebar-width)]' : '-translate-x-full lg:translate-x-0 lg:max-w-[var(--collapsed-sidebar-width)] lg:shadow-2xl rtl:lg:-translate-x-0 rtl:translate-x-full'" class="fixed inset-y-0 left-0 z-20 flex h-screen flex-col overflow-hidden w-[var(--sidebar-width)] bg-white transition-all rtl:left-auto rtl:right-0 lg:z-0 lg:border-r rtl:lg:border-l rtl:lg:border-r-0 dark:bg-gray-800 dark:border-gray-700 -translate-x-full lg:translate-x-0 lg:shadow-2xl rtl:lg:-translate-x-0 rtl:translate-x-full">

View File

@ -0,0 +1,3 @@
<turbo-frame id="modal">
{% block body %}{% endblock %}
</turbo-frame>