backports murph-skeleton

This commit is contained in:
Simon Vieille 2021-09-09 13:57:54 +02:00
parent 3bf661d059
commit f1fce6d6db
23 changed files with 175 additions and 56 deletions

View File

@ -54,6 +54,7 @@ export default {
} }
if (['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].indexOf(this.mime) === -1) { if (['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].indexOf(this.mime) === -1) {
this.thumbnail = null
return return
} }

View File

@ -5,6 +5,11 @@
<li class="breadcrumb-item" v-for="item in breadcrumb"> <li class="breadcrumb-item" v-for="item in breadcrumb">
<a class="btn btn-sm" href="#" v-on:click="setDirectory(item.path)" v-html="item.label"></a> <a class="btn btn-sm" href="#" v-on:click="setDirectory(item.path)" v-html="item.label"></a>
</li> </li>
<li v-if="isLoading" class="ml-3">
<div class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">Loading...</span>
</div>
</li>
</ol> </ol>
<div class="d-flex"> <div class="d-flex">
@ -200,7 +205,8 @@ export default {
files: [], files: [],
parent: null, parent: null,
modalUrl: null, modalUrl: null,
ajax: 0 ajax: 0,
isLoading: false
} }
}, },
methods: { methods: {
@ -266,17 +272,22 @@ export default {
}, },
refresh () { refresh () {
const that = this const that = this
this.isLoading = true
this.files = []
this.directories = []
axios.get(Routing.generate('admin_file_manager_api_directory', { axios.get(Routing.generate('admin_file_manager_api_directory', {
directory: that.directory, directory: that.directory,
context: that.context, context: that.context,
ajax: this.ajax ajax: this.ajax,
time: Date.now(),
})) }))
.then((response) => { .then((response) => {
that.buildBreadcrum(response.data.breadcrumb) that.buildBreadcrum(response.data.breadcrumb)
that.parent = response.data.parent that.parent = response.data.parent
that.directories = response.data.directories that.directories = response.data.directories
that.files = response.data.files that.files = response.data.files
that.isLoading = false
const query = new URLSearchParams(window.location.search) const query = new URLSearchParams(window.location.search)
query.set('path', that.directory) query.set('path', that.directory)

View File

@ -64,8 +64,19 @@ module.exports = function () {
fileManagerBrowser((value) => { fileManagerBrowser((value) => {
value = value.replace(/^\//, '') value = value.replace(/^\//, '')
picker.parent('.form-filepicker-container').find('input.form-filepicker-picker').val(value) picker.parents('.form-filepicker-container').find('input.form-filepicker-picker').val(value)
input.val(value) input.val(value)
}) })
}) })
$('body').on('click', '.form-filepicker-reset', (e) => {
e.preventDefault()
const button = $(e.target)
const id = '#' + button.attr('data-target')
const input = $(id)
input.val('')
button.parents('.form-filepicker-container').find('input.form-filepicker-picker').val('')
})
} }

View File

@ -25,8 +25,15 @@ const openModal = function (url) {
module.exports = function () { module.exports = function () {
let click = 0 let click = 0
const body = $('body')
$('body').on('click', '*[data-modal]', (e) => { body.on('hidden.bs.modal', '.modal', (e) => {
if ($('.modal.show').length) {
$('body').addClass('modal-open')
}
})
body.on('click', '*[data-modal]', (e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()

View File

@ -15,6 +15,8 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use App\Core\Event\Page\PageEditEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class PageAdminController extends CrudController class PageAdminController extends CrudController
{ {
@ -50,12 +52,17 @@ class PageAdminController extends CrudController
EntityManager $entityManager, EntityManager $entityManager,
RepositoryQuery $repositoryQuery, RepositoryQuery $repositoryQuery,
PageLocator $pageLocator, PageLocator $pageLocator,
EventDispatcherInterface $eventDispatcher,
Request $request Request $request
): Response { ): Response {
$entity = $repositoryQuery->filterById($entity)->findOne(); $entity = $repositoryQuery->filterById($entity)->findOne();
$event = new PageEditEvent($entity);
$eventDispatcher->dispatch($event, PageEditEvent::FORM_INIT_EVENT);
$this->getConfiguration()->setFormOptions('edit', [ $this->getConfiguration()->setFormOptions('edit', [
'pageConfiguration' => $pageLocator->getPage(get_class($entity)), 'page_configuration' => $pageLocator->getPage(get_class($entity)),
'page_builder_options' => $event->getPageBuilderOptions(),
]); ]);
return $this->doEdit($entity, $entityManager, $request); return $this->doEdit($entity, $entityManager, $request);

View File

@ -28,6 +28,7 @@ class CrudConfiguration
protected array $isSortableCollection = []; protected array $isSortableCollection = [];
protected string $sortableCollectionProperty = 'sortOrder'; protected string $sortableCollectionProperty = 'sortOrder';
protected ?string $defaultLocale = null; protected ?string $defaultLocale = null;
protected bool $showActions = true;
protected static $self; protected static $self;
@ -314,4 +315,16 @@ class CrudConfiguration
{ {
return $this->sortableCollectionProperty; return $this->sortableCollectionProperty;
} }
public function setShowActions(bool $showActions): self
{
$this->showActions = $showActions;
return $this;
}
public function getShowActions(): bool
{
return $this->showActions;
}
} }

View File

@ -36,7 +36,7 @@ class FileInformation implements EntityInterface
public function getAttributes() public function getAttributes()
{ {
return json_decode($this->attributes, true); return (array) json_decode($this->attributes, true);
} }
public function setAttributes($attributes): self public function setAttributes($attributes): self

View File

@ -225,12 +225,30 @@ class Node implements EntityInterface
/** /**
* @return Collection|Node[] * @return Collection|Node[]
*/ */
public function getChildren(): Collection public function getChildren(array $criteria = []): Collection
{ {
if (null === $this->children) { if (null === $this->children) {
$this->children = new ArrayCollection(); $this->children = new ArrayCollection();
} }
if (!empty($criteria)) {
$children = new ArrayCollection();
foreach ($this->children as $child) {
$add = true;
if (isset($criteria['visible']) && $child->getIsVisible() !== $criteria['visible']) {
$add = false;
}
if ($add) {
$children->add($child);
}
}
return $children;
}
return $this->children; return $this->children;
} }
@ -256,7 +274,7 @@ class Node implements EntityInterface
return $this; return $this;
} }
public function getAllChildren(): ArrayCollection public function getAllChildren(array $criteria = []): ArrayCollection
{ {
$children = []; $children = [];
@ -274,6 +292,14 @@ class Node implements EntityInterface
return $a->getTreeLeft() < $b->getTreeLeft() ? -1 : 1; return $a->getTreeLeft() < $b->getTreeLeft() ? -1 : 1;
}); });
if (!empty($criteria)) {
foreach ($children as $key => $child) {
if (isset($criteria['visible']) && $child->getIsVisible() !== $criteria['visible']) {
unset($children[$key]);
}
}
}
return new ArrayCollection($children); return new ArrayCollection($children);
} }
@ -308,6 +334,19 @@ class Node implements EntityInterface
return $string->startsWith('http://') || $string->startsWith('https://'); return $string->startsWith('http://') || $string->startsWith('https://');
} }
public function hasAppUrl(): bool
{
$string = u($this->getUrl());
foreach (['tel:', 'fax:', 'mailto:'] as $prefix) {
if ($string->startsWith($prefix)) {
return true;
}
}
return false;
}
public function getIsVisible(): ?bool public function getIsVisible(): ?bool
{ {
return $this->isVisible; return $this->isVisible;

View File

@ -140,7 +140,7 @@ class Page implements EntityInterface
return $this; return $this;
} }
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
} }

View File

@ -133,7 +133,13 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
$generatedUrl = $generatedUrl.'-'.$number; $generatedUrl = $generatedUrl.'-'.$number;
} }
if (!u($generatedUrl)->startsWith('https://') && !u($generatedUrl)->startsWith('http://')) { if (
!u($generatedUrl)->startsWith('https://')
&& !u($generatedUrl)->startsWith('http://')
&& !u($generatedUrl)->startsWith('tel:')
&& !u($generatedUrl)->startsWith('mailto:')
&& !u($generatedUrl)->startsWith('fax:')
) {
$generatedUrl = '/'.$generatedUrl; $generatedUrl = '/'.$generatedUrl;
$generatedUrl = preg_replace('#/{2,}#', '/', $generatedUrl); $generatedUrl = preg_replace('#/{2,}#', '/', $generatedUrl);
} }

View File

@ -16,7 +16,7 @@ class CollectionBlockType extends AbstractType
{ {
$builder->add( $builder->add(
'value', 'value',
CollectionType::class, $options['collection_type'],
array_merge([ array_merge([
'required' => false, 'required' => false,
'label' => false, 'label' => false,
@ -40,6 +40,7 @@ class CollectionBlockType extends AbstractType
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'data_class' => Block::class, 'data_class' => Block::class,
'collection_type' => CollectionType::class,
'collection_name' => '', 'collection_name' => '',
'label_add' => 'Add', 'label_add' => 'Add',
'label_delete' => 'Delete', 'label_delete' => 'Delete',

View File

@ -105,7 +105,7 @@ class PageType extends AbstractType
'choices' => call_user_func(function () use ($options) { 'choices' => call_user_func(function () use ($options) {
$choices = []; $choices = [];
foreach ($options['pageConfiguration']->getTemplates() as $template) { foreach ($options['page_configuration']->getTemplates() as $template) {
$choices[$template['name']] = $template['file']; $choices[$template['name']] = $template['file'];
} }
@ -119,14 +119,15 @@ class PageType extends AbstractType
] ]
); );
$builder->getData()->buildForm($builder); $builder->getData()->buildForm($builder, $options['page_builder_options']);
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'data_class' => Page::class, 'data_class' => Page::class,
'pageConfiguration' => null, 'page_configuration' => null,
'page_builder_options' => [],
]); ]);
} }
} }

View File

@ -71,6 +71,7 @@
"Page": "Page" "Page": "Page"
"Administration": "Administration" "Administration": "Administration"
"Users": "Utilisateurs" "Users": "Utilisateurs"
"New user": "Nouvel utilisateur⋅trice"
"Back to the list": "Retour à la liste" "Back to the list": "Retour à la liste"
"Edit": "Éditer" "Edit": "Éditer"
"New password": "Nouveau mot de passe" "New password": "Nouveau mot de passe"

View File

@ -139,9 +139,11 @@
</th> </th>
{% endblock %} {% endblock %}
{% endfor %} {% endfor %}
<th class="crud-action-column col-2 miw-100 text-right"> {% if configuration.showActions %}
{{ 'Actions'|trans }} <th class="crud-action-column col-2 miw-100 text-right">
</th> {{ 'Actions'|trans }}
</th>
{% endif %}
</tr> </tr>
</thead> </thead>
{% endblock %} {% endblock %}
@ -205,34 +207,36 @@
</td> </td>
{% endfor %} {% endfor %}
<td class="crud-action-column col-2 miw-200 text-right"> {% if configuration.showActions %}
{% block list_item_actions_before %}{% endblock %} <td class="crud-action-column col-2 miw-200 text-right">
{% block list_item_actions_before %}{% endblock %}
{% if configuration.action(context, 'show', true) %} {% if configuration.action(context, 'show', true) %}
<a href="{{ path(configuration.pageRoute('show'), {entity: item.id}|merge(configuration.pageRouteParams('show'))) }}" class="btn btn-sm btn-secondary mr-1"> <a href="{{ path(configuration.pageRoute('show'), {entity: item.id}|merge(configuration.pageRouteParams('show'))) }}" class="btn btn-sm btn-secondary mr-1">
<span class="fa fa-eye"></span> <span class="fa fa-eye"></span>
</a> </a>
{% endif %} {% endif %}
{% if configuration.action(context, 'edit', true) %} {% if configuration.action(context, 'edit', true) %}
<a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}|merge(configuration.pageRouteParams('edit'))) }}" class="btn btn-sm btn-primary mr-1"> <a href="{{ path(configuration.pageRoute('edit'), {entity: item.id}|merge(configuration.pageRouteParams('edit'))) }}" class="btn btn-sm btn-primary mr-1">
<span class="fa fa-edit"></span> <span class="fa fa-edit"></span>
</a> </a>
{% endif %} {% endif %}
{% if configuration.action(context, 'delete', true) %} {% if configuration.action(context, 'delete', true) %}
<button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger"> <button type="submit" form="form-delete-{{ item.id }}" class="btn btn-sm btn-danger">
<span class="fa fa-trash"></span> <span class="fa fa-trash"></span>
</button> </button>
<form method="post" action="{{ path(configuration.pageRoute('delete'), {entity: item.id}|merge(configuration.pageRouteParams('delete'))) }}" id="form-delete-{{ item.id }}" data-form-confirm> <form method="post" action="{{ path(configuration.pageRoute('delete'), {entity: item.id}|merge(configuration.pageRouteParams('delete'))) }}" id="form-delete-{{ item.id }}" data-form-confirm>
<input type="hidden" name="_method" value="DELETE"> <input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}"> <input type="hidden" name="_token" value="{{ csrf_token('delete' ~ item.id) }}">
</form> </form>
{% endif %} {% endif %}
{% block list_item_actions_after %}{% endblock %} {% block list_item_actions_after %}{% endblock %}
</td> </td>
{% endif %}
</tr> </tr>
{% endblock %} {% endblock %}

View File

@ -44,14 +44,14 @@
{% for child in item %} {% for child in item %}
{{ form_row(child) }} {{ form_row(child) }}
{% endfor %} {% endfor %}
</div>
<div class="text-right"> <div class="text-right">
<span data-collection-delete-container class="btn btn-sm btn-danger"> <span data-collection-delete-container class="btn btn-sm btn-danger">
<span data-collection-delete="{{ loop.index }}"> <span data-collection-delete="{{ loop.index }}">
{{ label_delete|trans }} {{ label_delete|trans }}
</span>
</span> </span>
</span> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -145,19 +145,27 @@
<div class="p-2 text-center"> <div class="p-2 text-center">
<span class="form-filepicker-container"> <span class="form-filepicker-container">
<input class="form-control form-filepicker-picker mb-3" data-target="{{ id }}" readonly type="text" value="{{ value }}"> <div class="input-group mb-3">
<span class="btn btn-sm btn-primary form-filepicker-picker" data-target="{{ id }}"> <input class="form-control form-filepicker-picker" data-target="{{ id }}" readonly type="text" value="{{ value }}">
<div class="input-group-append form-filepicker-reset" data-target="{{ id }}">
<div class="input-group-text btn" data-target="{{ id }}">
<span class="fa fa-backspace" data-target="{{ id }}"></span>
</div>
</div>
</div>
<span class="btn btn-sm btn-primary form-filepicker-picker mb-1" data-target="{{ id }}">
{{ 'Choose'|trans }} {{ 'Choose'|trans }}
</span> </span>
</span> </span>
{% if value %} {% if value %}
{% if value.pathname is defined %} {% if value.pathname is defined %}
<a class="btn btn-sm btn-success ml-1" href="{{ asset(value.pathname) }}" target="_blank"> <a class="btn btn-sm btn-success ml-1 mb-1" href="{{ asset(value.pathname) }}" target="_blank">
{{ 'Download'|trans }} {{ 'Download'|trans }}
</a> </a>
{% else %} {% else %}
<a class="btn btn-sm btn-success ml-1" href="{{ asset(value) }}" target="_blank"> <a class="btn btn-sm btn-success ml-1 mb-1" href="{{ asset(value) }}" target="_blank">
{{ 'Download'|trans }} {{ 'Download'|trans }}
</a> </a>
{% endif %} {% endif %}

View File

@ -87,6 +87,10 @@
<a href="{{ safe_node_url(node) }}" class="btn btn-sm border border-secondary btn-light"> <a href="{{ safe_node_url(node) }}" class="btn btn-sm border border-secondary btn-light">
<span class="fa fa-sign-out-alt text-muted"></span> <span class="fa fa-sign-out-alt text-muted"></span>
</a> </a>
{% elseif node.hasAppUrl %}
<a href="{{ node.url }}" class="btn btn-sm border border-secondary btn-light">
<span class="fa fa-sign-out-alt text-muted"></span>
</a>
{% else %} {% else %}
{% if not node.disableUrl %} {% if not node.disableUrl %}
{% if node.parameters|length %} {% if node.parameters|length %}

View File

@ -57,6 +57,10 @@ class SiteRouteLoader extends Loader
continue; continue;
} }
if ($node->hasAppUrl()) {
continue;
}
if (null !== $node->getAliasNode()) { if (null !== $node->getAliasNode()) {
continue; continue;
} }

View File

@ -25,6 +25,7 @@ class SiteStore
public function getNavigations(): array public function getNavigations(): array
{ {
return $this->navigationRepositoryQuery->create() return $this->navigationRepositoryQuery->create()
->orderBy('.sortOrder')
->find() ->find()
; ;
} }

View File

@ -13,7 +13,7 @@ use Symfony\Component\Form\FormBuilderInterface;
*/ */
class RssPage extends Page class RssPage extends Page
{ {
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder->add( $builder->add(
'title', 'title',

View File

@ -13,7 +13,7 @@ use Symfony\Component\Form\FormBuilderInterface;
*/ */
class SimplePage extends TitledPage class SimplePage extends TitledPage
{ {
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
parent::buildForm($builder); parent::buildForm($builder);

View File

@ -14,7 +14,7 @@ use App\Core\Entity\Site\Page\Page;
*/ */
class TextPage extends Page class TextPage extends Page
{ {
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder->add( $builder->add(
'content', 'content',

View File

@ -13,7 +13,7 @@ use Symfony\Component\Form\FormBuilderInterface;
*/ */
class TitledPage extends Page class TitledPage extends Page
{ {
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder->add( $builder->add(
'title', 'title',

View File

@ -36,8 +36,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<meta property="og:description" content="{{ _page.ogDescription }}" /> <meta property="og:description" content="{{ _page.ogDescription }}" />
{% if _page.ogImage %} {% if _page.ogImage %}
<meta property="og:image" content="{{ asset(_page.ogImage) }}" /> <meta property="og:image" content="{{ absolute_url(asset(_page.ogImage)) }}" />
<meta property="og:image:secure_url" content="{{ asset(_page.ogImage) }}" /> <meta property="og:image:secure_url" content="{{ absolute_url(asset(_page.ogImage)) }}" />
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endblock %} {% endblock %}