Skip to content

Commit

Permalink
Customize elements for upload: multi upload page and drop upload sect…
Browse files Browse the repository at this point in the history
…ion (#1220)

* chore fix: avoid js error

* feat: custom multiupload and dropupload

* tests: unit tests + doc

* chore refactor: formatDate in BEDITA object
  • Loading branch information
didoda authored Jan 28, 2025
1 parent 3b9c21a commit f30eb8c
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 52 deletions.
8 changes: 8 additions & 0 deletions config/app_local.example.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@
* for a complete list of icons
* 'sidebar' - additional custom sidebar links added in modules index and single item view,
* defined as associative array with 'index' and 'view' keys
* 'dropupload' - custom dropupload element to use for this module, f.i. 'MyPlugin.Form/dropupload'
* 'multiupload' - custom multiupload element to use for this module, f.i. 'MyPlugin.Form/multiupload'
*/
// 'Modules' => [
// 'objects' => [
Expand All @@ -283,6 +285,12 @@
// ],
// 'documents' => [
// 'color' => '#cc4700',
// 'dropupload' => [
// '_element' => 'MyPlugin.Form/dropupload',
// ],
// 'multiupload' => [
// '_element' => 'MyPlugin.Form/multiupload',
// ],
// ],
// 'events' => [
// 'color' => '#09c',
Expand Down
1 change: 1 addition & 0 deletions resources/js/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const _vueInstance = new Vue({
BEDITA.success = success;
BEDITA.prompt = prompt;
BEDITA.warning = warning;
BEDITA.formatDate = this.$helpers.formatDate;
this.selectedTypes = this.queryFilter?.filter?.type || [];
});
},
Expand Down
11 changes: 11 additions & 0 deletions resources/js/app/helpers/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ export default {
return false;
},

getObjectTypeFromMime(mimeType) {
const mimes = BEDITA.uploadConfig?.accepted;
for (let type in mimes) {
if (this.checkAcceptedMime(mimes[type], mimeType)) {
return type;
}
}

return null;
},

titleFromFileName(filename) {
let title = filename;
title = title.replaceAll('-', ' ');
Expand Down
2 changes: 1 addition & 1 deletion resources/js/app/mixins/paginated-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const PaginatedContentMixin = {
formattedObj.type = obj.type;

// search for meta.relation using this.formatObjectsFilter
const metaRelation = obj.meta.relation;
const metaRelation = obj?.meta?.relation || {};
if (metaRelation) {
let formattedMeta = {};
this.formatObjectsFilter.forEach((filter) => {
Expand Down
3 changes: 3 additions & 0 deletions resources/js/app/pages/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ export default {

watch: {
selectedRows(val) {
if (!this.$refs.checkAllCB) {
return;
}
if (!val.length) {
this.$refs.checkAllCB.checked = false;
this.$refs.checkAllCB.indeterminate = false;
Expand Down
6 changes: 5 additions & 1 deletion src/Controller/ModulesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,13 @@ public function setObjectType(?string $objectType): void
*/
private function setupViewRelations(array $relations): void
{
// setup relations schema
$relationsSchema = $this->Schema->getRelationsSchema();
$this->set('relationsSchema', $relationsSchema);

// setup relations metadata
$this->Modules->setupRelationsMeta(
$this->Schema->getRelationsSchema(),
$relationsSchema,
$relations,
$this->Properties->relationsList($this->objectType),
$this->Properties->hiddenRelationsList($this->objectType),
Expand Down
35 changes: 35 additions & 0 deletions src/View/Helper/ElementHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,41 @@ public function custom(string $item, string $type = 'relation'): string
return (string)Configure::read($path);
}

/**
* Return dropupload element path by relation.
* This checks relation right types, and verify if there is a `Modules.<type>.dropupload._element` configuration.
* If not, return default `Form/dropupload` element.
* If there is a configuration, return it.
*
* @param array $rightTypes Right types of the relation
* @return string
*/
public function dropupload(array $rightTypes): string
{
foreach ($rightTypes as $name) {
$path = (string)Configure::read(sprintf('Modules.%s.dropupload._element', $name));
if (!empty($path)) {
return $path;
}
}

return 'Form/dropupload';
}

/**
* Return multiupload element via `Modules.<type>.multiupload._element` configuration
*
* @return string
*/
public function multiupload(): string
{
$currentModule = (array)$this->getView()->get('currentModule');
$name = (string)Hash::get($currentModule, 'name');
$path = sprintf('Modules.%s.multiupload._element', $name);

return (string)Configure::read($path, 'Form/multiupload');
}

/**
* Return sidebar element via `Modules.<type>.sidebar._element` configuration
*
Expand Down
7 changes: 7 additions & 0 deletions templates/Element/Form/dropupload.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{# :accepted-drop="[`.from-relation-${relationName}`,isRelationWithMedia && 'from-files']"> #}
<drop-upload
placeholder="{{ __('Click or drop new files here') }}"
@new-relations="appendRelations"
v-if="isRelationWithMedia"
>
</drop-upload>
42 changes: 42 additions & 0 deletions templates/Element/Form/multiupload.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{% do _view.assign('title', __('Multi upload')) %}
{% if Perms.canSave() and not readonly %}
<modules-index inline-template ids='{{ []|json_encode }}'>
<div class="module-index">
<drop-upload
object-type="{{ objectType }}"
double="1"
placeholder="{{ __('Click or drop new files here') }}"
@new-relations="appendUploaded"
>
</drop-upload>
<div v-for="item in uploaded" :key="item.id">
<a :href="`/view/${item.id}`">
<div style="display: grid; grid-template-columns: 50px 1fr; border-bottom: dashed gray 1px;">
<figure class="center-icon icon-opaque" v-if="item?.meta?.thumb_url">
<img :src="item?.meta?.thumb_url" :alt="item.attributes.title" style="height: 50px; width: 50px; object-fit: cover;"/>
</figure>
<span style="padding: 0.5rem" v-else>
<app-icon icon="carbon:document"></app-icon>
<: extension(item?.meta?.media_url) :>
</span>
<span style="padding: 1rem">
<: item.attributes.title :>
</span>
</div>
</a>
</div>
</div>
</modules-index>
{% else %}
<div class="alert alert-danger">{{ __('You do not have the required permissions to view this page.') }}</div>
{% endif %}

{# Add links to the module #}
{% do _view.append('app-module-links',
Html.link(
__('List'),
{'_name': 'modules:list', 'object_type': objectType},
{'title': __('List'), 'class': 'icon-left-dir button button-outlined button-outlined-hover-module-' ~ objectType}
)|raw
) %}

9 changes: 1 addition & 8 deletions templates/Element/Form/relation.twig
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,9 @@
</p>
</div>
<div class="mt-5">
{# DROP FILES #}
{% if uploadableNum %}
{# :accepted-drop="[`.from-relation-${relationName}`,isRelationWithMedia && 'from-files']"> #}

<drop-upload
placeholder="{{ __('Click or drop new files here') }}"
@new-relations="appendRelations"
></drop-upload>
{{ element(Element.dropupload(relationsSchema[relationName]['right'])) }}
{% endif %}

<div>
<button v-if="isPanelOpen({{object.id|json_encode}})" @click.prevent.stop="closePanel()">
<app-icon icon="carbon:close"></app-icon>
Expand Down
43 changes: 1 addition & 42 deletions templates/Pages/Multiupload/index.twig
Original file line number Diff line number Diff line change
@@ -1,42 +1 @@
{% do _view.assign('title', __('Multi upload')) %}

{% if Perms.canSave() and not readonly %}

<modules-index inline-template ids='{{ []|json_encode }}'>
<div class="module-index">

<drop-upload object-type="{{ objectType }}" double=1 placeholder="{{ __('Click or drop new files here') }}" @new-relations="appendUploaded"></drop-upload>

<div v-for="item in uploaded" :key="item.id">
<a :href="`/view/${item.id}`">
<div style="display: grid; grid-template-columns: 50px 1fr; border-bottom: dashed gray 1px;">
<figure class="center-icon icon-opaque" v-if="item?.meta?.thumb_url">
<img
:src="item?.meta?.thumb_url"
:alt="item.attributes.title"
style="height: 50px; width: 50px; object-fit: cover;" />
</figure>
<span style="padding: 0.5rem" v-else>
<app-icon icon="carbon:document"></app-icon>
<: extension(item?.meta?.media_url) :>
</span>
<span style="padding: 1rem"><: item.attributes.title :></span>
</div>
</a>
</div>

</div>
</modules-index>

{% else %}
<div class="alert alert-danger">{{ __('You do not have the required permissions to view this page.') }}</div>
{% endif %}

{# Add links to the module #}
{% do _view.append('app-module-links',
Html.link(
__('List'),
{'_name': 'modules:list', 'object_type': objectType},
{'title': __('List'), 'class': 'icon-left-dir button button-outlined button-outlined-hover-module-' ~ objectType}
)|raw
) %}
{{ element(Element.multiupload()) }}
44 changes: 44 additions & 0 deletions tests/TestCase/View/Helper/ElementHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,48 @@ public function testSidebar(): void
$actual = $element->sidebar();
static::assertSame('another_sidebar', $actual);
}

/**
* Test `dropupload` method
*
* @return void
* @covers ::dropupload()
*/
public function testDropupload(): void
{
$expected = 'Form/dropupload';
$viewVars = [
'currentModule' => ['name' => 'documents'],
];
$view = new View(new ServerRequest(), null, null, compact('viewVars'));
$element = new ElementHelper($view);
$actual = $element->dropupload(['documents', 'events']);
static::assertSame($expected, $actual);

Configure::write('Modules.documents.dropupload._element', 'another_dropupload');
$actual = $element->dropupload(['documents', 'events']);
static::assertSame('another_dropupload', $actual);
}

/**
* Test `multiupload` method
*
* @return void
* @covers ::multiupload()
*/
public function testMultiupload(): void
{
$expected = 'Form/multiupload';
$viewVars = [
'currentModule' => ['name' => 'documents'],
];
$view = new View(new ServerRequest(), null, null, compact('viewVars'));
$element = new ElementHelper($view);
$actual = $element->multiupload();
static::assertSame($expected, $actual);

Configure::write('Modules.documents.multiupload._element', 'another_multiupload');
$actual = $element->multiupload();
static::assertSame('another_multiupload', $actual);
}
}

0 comments on commit f30eb8c

Please sign in to comment.