Compare commits
29 Commits
Author | SHA1 | Date |
---|---|---|
philipp lang | 424ca30932 | |
philipp lang | 9870ac8bab | |
philipp lang | e9975e9c5a | |
philipp lang | 77cfe9bbc7 | |
philipp lang | 6d8e9de3d7 | |
philipp lang | b7a617aa62 | |
philipp lang | 59d7647720 | |
philipp lang | a6c11cdc14 | |
philipp lang | d2f7f16e88 | |
philipp lang | 3a7f558755 | |
philipp lang | 35593ba37b | |
philipp lang | 46d28877d9 | |
philipp lang | 91e5cc3e3b | |
philipp lang | 3b8a129fa4 | |
philipp lang | c4eb67c09f | |
philipp lang | 5c05ab7c80 | |
philipp lang | 7c33160b13 | |
philipp lang | 8bf17784cc | |
philipp lang | c00ee87082 | |
philipp lang | 1b2b148af5 | |
philipp lang | 14ca7d312f | |
philipp lang | 17afb48028 | |
philipp lang | 22d7841d5a | |
philipp lang | 79cb5a8f58 | |
philipp lang | 2e90fde134 | |
philipp lang | 38c40c48ac | |
philipp lang | 1aeb520b06 | |
philipp lang | 78143ec0b3 | |
philipp lang | 194f1f9c14 |
|
@ -6,7 +6,7 @@ This package creates routes for the popular Medialibrary Package from Spatie ().
|
||||||
|
|
||||||
In RegisterMediaCollections, you have the following methods available:
|
In RegisterMediaCollections, you have the following methods available:
|
||||||
|
|
||||||
You can set a filename by default for the file. This accepts the associated Model, as well as the original filename. You should return the new name of the file with the extension (e.g. disc.jpg).
|
You can set a filename by default for the file. This accepts the associated Model, as well as the original basename (without extension). You should return the new name of the file without the extension (e.g. disc).
|
||||||
|
|
||||||
```
|
```
|
||||||
forceFileName(fn ($model, $path) => Str::slug($path))
|
forceFileName(fn ($model, $path) => Str::slug($path))
|
||||||
|
@ -25,4 +25,3 @@ You can call whatever you want after an image has been added, modified or delete
|
||||||
....
|
....
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<template>
|
||||||
|
<svg x="0px" y="0px" viewBox="0 0 511.626 511.627" xml:space="preserve" fill="currentColor">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d="M392.857,292.354h-18.274c-2.669,0-4.859,0.855-6.563,2.573c-1.718,1.708-2.573,3.897-2.573,6.563v91.361 c0,12.563-4.47,23.315-13.415,32.262c-8.945,8.945-19.701,13.414-32.264,13.414H82.224c-12.562,0-23.317-4.469-32.264-13.414 c-8.945-8.946-13.417-19.698-13.417-32.262V155.31c0-12.562,4.471-23.313,13.417-32.259c8.947-8.947,19.702-13.418,32.264-13.418 h200.994c2.669,0,4.859-0.859,6.57-2.57c1.711-1.713,2.566-3.9,2.566-6.567V82.221c0-2.662-0.855-4.853-2.566-6.563 c-1.711-1.713-3.901-2.568-6.57-2.568H82.224c-22.648,0-42.016,8.042-58.102,24.125C8.042,113.297,0,132.665,0,155.313v237.542 c0,22.647,8.042,42.018,24.123,58.095c16.086,16.084,35.454,24.13,58.102,24.13h237.543c22.647,0,42.017-8.046,58.101-24.13 c16.085-16.077,24.127-35.447,24.127-58.095v-91.358c0-2.669-0.856-4.859-2.574-6.57 C397.709,293.209,395.519,292.354,392.857,292.354z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M506.199,41.971c-3.617-3.617-7.905-5.424-12.85-5.424H347.171c-4.948,0-9.233,1.807-12.847,5.424 c-3.617,3.615-5.428,7.898-5.428,12.847s1.811,9.233,5.428,12.85l50.247,50.248L198.424,304.067 c-1.906,1.903-2.856,4.093-2.856,6.563c0,2.479,0.953,4.668,2.856,6.571l32.548,32.544c1.903,1.903,4.093,2.852,6.567,2.852 s4.665-0.948,6.567-2.852l186.148-186.148l50.251,50.248c3.614,3.617,7.898,5.426,12.847,5.426s9.233-1.809,12.851-5.426 c3.617-3.616,5.424-7.898,5.424-12.847V54.818C511.626,49.866,509.813,45.586,506.199,41.971z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div v-if="label" class="text-sm font-semibold text-gray-400" v-text="label"></div>
|
||||||
|
<label class="flex items-center justify-center h-[35px] border-2 border-solid border-gray-600 rounded-lg text-sm text-gray-300 bg-gray-700 relative" :for="id">
|
||||||
|
<div class="relative">Klicken oder Datei hierhin ziehen zum hochladen</div>
|
||||||
|
<input :id="id" ref="uploader" class="hidden" type="file" :name="name" multiple @change="upload($event.target.files)" />
|
||||||
|
<div class="absolute w-full h-full top-0 left-0 cursor-pointer" @drop="onDropping" @dragenter="onDragEnter" @dragover="onDragOver" @dragleave="onDragLeave"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div v-for="file in inner" :key="file.id" class="flex h-[35px] justify-between items-center space-x-2 px-2 border-2 border-solid border-gray-600 rounded-lg text-gray-300 bg-gray-700">
|
||||||
|
<img :src="file.icon" class="w-6 h-6" />
|
||||||
|
<div class="text-sm text-gray-300 leading-none grow" v-text="file.file_name"></div>
|
||||||
|
<slot name="buttons" button-class="flex justify-center items-center w-6 h-6 rounded-full" icon-class="w-3 h-3" :file="file"></slot>
|
||||||
|
<a v-tooltip="`Löschen`" href="#" class="flex justify-center items-center w-6 h-6 rounded-full bg-red-200" @click.prevent="onDelete(file)">
|
||||||
|
<trash-icon class="text-red-800 w-3 h-3"></trash-icon>
|
||||||
|
</a>
|
||||||
|
<a v-tooltip="`Öffnen`" :href="file.original_url" class="flex justify-center items-center w-6 h-6 rounded-full bg-primary-700" target="_BLANK">
|
||||||
|
<external-icon class="text-primary-200 w-3 h-3"></external-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref, inject} from 'vue';
|
||||||
|
import TrashIcon from './TrashIcon.vue';
|
||||||
|
import ExternalIcon from './ExternalIcon.vue';
|
||||||
|
import useReadFile from '../composables/useReadFile.js';
|
||||||
|
import {useToast} from 'vue-toastification';
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
const axios = inject('axios');
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const queue = ref([]);
|
||||||
|
const inner = ref([]);
|
||||||
|
const {onDragEnter, onDragOver, onDragLeave, processDrop, read} = useReadFile();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
collection: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
parentName: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
required: true,
|
||||||
|
validator: (value) => typeof value === 'number' || value === null,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
required: false,
|
||||||
|
default: () => null,
|
||||||
|
validator: (value) => value === null || typeof value === 'string',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onDropping(e) {
|
||||||
|
await upload(await processDrop(e, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upload(files) {
|
||||||
|
[...files].forEach((f) => realUpload(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function realUpload(file) {
|
||||||
|
var payload = await read(file);
|
||||||
|
var identifier = Math.random().toString(36) + Date.now() + payload.content.substring(2, 15);
|
||||||
|
queue.value = [...queue.value, {file_name: payload.name, size: payload.size, identifier: identifier}];
|
||||||
|
|
||||||
|
var response = await axios.post('/mediaupload', {
|
||||||
|
parent: {model: props.parentName, collection_name: props.collection, id: props.parentId ? props.parentId : null},
|
||||||
|
payload: [payload],
|
||||||
|
});
|
||||||
|
queue.value = queue.value.filter((f) => f.identifier !== identifier);
|
||||||
|
inner.value.push(response.data[0]);
|
||||||
|
emit('update:modelValue', inner.value);
|
||||||
|
toast.success('Dateien hochgeladen');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDelete(file) {
|
||||||
|
await axios.delete(`/mediaupload/${file.id}`);
|
||||||
|
inner.value = inner.value.filter((f) => f.id !== file.id);
|
||||||
|
emit('update:modelValue', inner.value);
|
||||||
|
toast.success('Datei entfernt');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
var response = await axios.get(`/mediaupload/${props.parentName}/${props.parentId}/${props.collection}`);
|
||||||
|
inner.value = response.data;
|
||||||
|
emit('update:modelValue', response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.parentId !== null) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<template>
|
||||||
|
<svg height="426.66667pt" viewBox="0 0 426.66667 426.66667" width="426.66667pt" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="m405.332031 192h-170.664062v-170.667969c0-11.773437-9.558594-21.332031-21.335938-21.332031-11.773437 0-21.332031 9.558594-21.332031 21.332031v170.667969h-170.667969c-11.773437 0-21.332031 9.558594-21.332031 21.332031 0 11.777344 9.558594 21.335938 21.332031 21.335938h170.667969v170.664062c0 11.777344 9.558594 21.335938 21.332031 21.335938 11.777344 0 21.335938-9.558594 21.335938-21.335938v-170.664062h170.664062c11.777344 0 21.335938-9.558594 21.335938-21.335938 0-11.773437-9.558594-21.332031-21.335938-21.332031zm0 0" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<label class="flex flex-col" :for="id">
|
||||||
|
<span v-if="label" class="text-sm font-semibold text-gray-400">
|
||||||
|
{{ label }}
|
||||||
|
<span v-show="required" class="text-red-800"> *</span>
|
||||||
|
</span>
|
||||||
|
<div class="h-[35px] border-2 border-solid relative rounded-lg cursor-pointer flex-none border-gray-600 text-gray-300 bg-gray-700">
|
||||||
|
<div v-if="inner === null" class="flex items-center justify-center h-full">
|
||||||
|
<div class="relative text-sm text-gray-300 leading-none">Klicken oder Datei hierhin ziehen zum hochladen</div>
|
||||||
|
<input :id="id" class="hidden" type="file" :name="name" :multiple="false" @change="upload($event.target.files)" />
|
||||||
|
<div class="absolute w-full h-full top-0 left-0 cursor-pointer" @drop="onDropping" @dragenter="onDragEnter" @dragover="onDragOver" @dragleave="onDragLeave"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex justify-between items-center h-full space-x-2 px-2">
|
||||||
|
<img :src="inner.icon" class="w-6 h-6" />
|
||||||
|
<div class="text-sm text-gray-300 leading-none grow" v-text="inner.file_name"></div>
|
||||||
|
<a v-tooltip="`Löschen`" href="#" class="flex justify-center items-center w-6 h-6 rounded-full bg-red-200" @click.prevent="onDelete">
|
||||||
|
<trash-icon class="text-red-800 w-3 h-3"></trash-icon>
|
||||||
|
</a>
|
||||||
|
<a v-tooltip="`Öffnen`" :href="inner.original_url" class="flex justify-center items-center w-6 h-6 rounded-full bg-primary-700" target="_BLANK">
|
||||||
|
<external-icon class="text-primary-200 w-3 h-3"></external-icon>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {watch, ref, inject} from 'vue';
|
||||||
|
import TrashIcon from './TrashIcon.vue';
|
||||||
|
import ExternalIcon from './ExternalIcon.vue';
|
||||||
|
import useReadFile from '../composables/useReadFile.js';
|
||||||
|
import {useToast} from 'vue-toastification';
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
const axios = inject('axios');
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const inner = ref(null);
|
||||||
|
const {dropping, onDragEnter, onDragOver, onDragLeave, processDrop, read} = useReadFile();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => false,
|
||||||
|
},
|
||||||
|
collection: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
parentName: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
required: true,
|
||||||
|
validator: (value) => typeof value === 'number' || value === null,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
required: false,
|
||||||
|
default: () => null,
|
||||||
|
validator: (value) => value === null || typeof value === 'string',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function parseValue(v) {
|
||||||
|
if (v === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (v.fallback) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reload() {
|
||||||
|
var response = await axios.get(`/mediaupload/${props.parentName}/${props.parentId}/${props.collection}`);
|
||||||
|
inner.value = parseValue(response.data);
|
||||||
|
emit('update:modelValue', inner.value.id);
|
||||||
|
}
|
||||||
|
async function onDropping(e) {
|
||||||
|
await upload(await processDrop(e, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upload(files) {
|
||||||
|
await realUpload(await read(files[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function realUpload(file) {
|
||||||
|
inner.value = (
|
||||||
|
await axios.post('/mediaupload', {
|
||||||
|
parent: {model: props.parentName, collection_name: props.collection, id: props.parentId ? props.parentId : null},
|
||||||
|
payload: file,
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
emit('update:modelValue', inner.value);
|
||||||
|
toast.success('Datei hochgeladen');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDelete() {
|
||||||
|
await axios.delete(`/mediaupload/${inner.value.id}`);
|
||||||
|
inner.value = null;
|
||||||
|
emit('update:modelValue', null);
|
||||||
|
toast.success('Datei entfernt');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.parentId !== null) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
style="enable-background: new 0 0 512 512"
|
||||||
|
xml:space="preserve"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d="M486.4,102.4h-128V25.6c0-15.36-10.24-25.6-25.6-25.6H179.2c-15.36,0-25.6,10.24-25.6,25.6v76.8h-128
|
||||||
|
C10.24,102.4,0,112.64,0,128s10.24,25.6,25.6,25.6h460.8c15.36,0,25.6-10.24,25.6-25.6S501.76,102.4,486.4,102.4z M307.2,102.4
|
||||||
|
H204.8V51.2h102.4V102.4z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d="M25.6,204.8l48.64,284.16c2.56,12.8,12.8,23.04,25.6,23.04h312.32c12.8,0,23.04-10.24,25.6-23.04L486.4,204.8H25.6z
|
||||||
|
M153.6,460.8c-15.36,0-25.6-10.24-25.6-25.6l-25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6l25.6,153.6
|
||||||
|
C179.2,450.56,168.96,460.8,153.6,460.8z M281.6,435.2c0,15.36-10.24,25.6-25.6,25.6s-25.6-10.24-25.6-25.6V281.6
|
||||||
|
c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6V435.2z M384,435.2c0,15.36-10.24,25.6-25.6,25.6
|
||||||
|
c-15.36,0-25.6-10.24-25.6-25.6l25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6L384,435.2z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</template>
|
|
@ -0,0 +1,77 @@
|
||||||
|
import accounting from 'accounting';
|
||||||
|
import {ref} from 'vue';
|
||||||
|
|
||||||
|
accounting.settings.currency = {
|
||||||
|
...accounting.settings.currency,
|
||||||
|
precision: 2,
|
||||||
|
format: '%v %s',
|
||||||
|
decimal: ',',
|
||||||
|
thousand: '.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const dropping = ref(false);
|
||||||
|
|
||||||
|
async function read(file) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function () {
|
||||||
|
var r = reader.result;
|
||||||
|
resolve({
|
||||||
|
content: r.substr(r.search(',') + 1),
|
||||||
|
name: file.name,
|
||||||
|
type: file.type,
|
||||||
|
size: file.size,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function onDragEnter(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dropping.value = true;
|
||||||
|
}
|
||||||
|
function onDragOver(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
function onDragLeave(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dropping.value = false;
|
||||||
|
}
|
||||||
|
function processDrop(e, maxFiles) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
let dt = e.dataTransfer;
|
||||||
|
let files = [...dt.files].slice(0, maxFiles ? maxFiles : dt.files.length);
|
||||||
|
let result = [];
|
||||||
|
for (const f in files) {
|
||||||
|
if (typeof files[f] === 'object') {
|
||||||
|
result.push(files[f]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dropping.value = false;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function size(file) {
|
||||||
|
if (file.size < 1000) {
|
||||||
|
return accounting.formatMoney(file.size, {symbol: 'B'});
|
||||||
|
}
|
||||||
|
if (file.size < 1000000) {
|
||||||
|
return accounting.formatMoney(file.size / 1000, {symbol: 'KB'});
|
||||||
|
}
|
||||||
|
if (file.size < 1000000000) {
|
||||||
|
return accounting.formatMoney(file.size / 1000000, {symbol: 'MB'});
|
||||||
|
}
|
||||||
|
if (file.size < 1000000000000) {
|
||||||
|
return accounting.formatMoney(file.size / 1000000000, {symbol: 'GB'});
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {dropping, onDragLeave, onDragOver, onDragEnter, processDrop, read};
|
||||||
|
}
|
|
@ -5,10 +5,16 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Zoomyboy\\MedialibraryHelper\\Tests\\": "tests/",
|
|
||||||
"Zoomyboy\\MedialibraryHelper\\": "src/"
|
"Zoomyboy\\MedialibraryHelper\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Zoomyboy\\MedialibraryHelper\\Tests\\": "tests/",
|
||||||
|
"Workbench\\App\\": "tests/workbench/app/",
|
||||||
|
"Database\\Factories\\": "tests/workbench/database/factories/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Philipp Lang",
|
"name": "Philipp Lang",
|
||||||
|
@ -16,20 +22,31 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"ext-imagick": ">=3.6.0",
|
"spatie/laravel-medialibrary": "^11.0",
|
||||||
"spatie/laravel-medialibrary": "^10.7",
|
"laravel/framework": "^11.0",
|
||||||
"laravel/framework": "^9.50",
|
"spatie/laravel-data": "^4.0",
|
||||||
"spatie/laravel-data": "^3.1",
|
"pestphp/pest": "^3.0"
|
||||||
"pestphp/pest": "^1.22"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.6",
|
"orchestra/testbench": "^9.0",
|
||||||
"orchestra/testbench": "^7.0",
|
"illuminate/console": "^11.0"
|
||||||
"illuminate/console": "^9.2"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"post-autoload-dump": [
|
"post-autoload-dump": [
|
||||||
|
"@clear",
|
||||||
|
"@prepare",
|
||||||
"@php vendor/bin/testbench package:discover --ansi"
|
"@php vendor/bin/testbench package:discover --ansi"
|
||||||
|
],
|
||||||
|
"clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
|
||||||
|
"prepare": "@php vendor/bin/testbench package:discover --ansi",
|
||||||
|
"build": "@php vendor/bin/testbench workbench:build --ansi",
|
||||||
|
"serve": [
|
||||||
|
"Composer\\Config::disableProcessTimeout",
|
||||||
|
"@build",
|
||||||
|
"@php vendor/bin/testbench serve"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"@php vendor/bin/pest"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'temp_disk' => 'temp',
|
||||||
|
'middleware' => ['web', 'auth:web'],
|
||||||
|
];
|
12
phpunit.xml
12
phpunit.xml
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
|
||||||
bootstrap="vendor/autoload.php"
|
bootstrap="vendor/autoload.php"
|
||||||
colors="true"
|
colors="true"
|
||||||
>
|
>
|
||||||
|
@ -9,11 +9,6 @@
|
||||||
<directory suffix="Test.php">./tests/Feature</directory>
|
<directory suffix="Test.php">./tests/Feature</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<coverage>
|
|
||||||
<include>
|
|
||||||
<directory suffix=".php">./src</directory>
|
|
||||||
</include>
|
|
||||||
</coverage>
|
|
||||||
<php>
|
<php>
|
||||||
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
|
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
|
||||||
<env name="APP_ENV" value="testing"/>
|
<env name="APP_ENV" value="testing"/>
|
||||||
|
@ -27,4 +22,9 @@
|
||||||
<env name="SESSION_DRIVER" value="array"/>
|
<env name="SESSION_DRIVER" value="array"/>
|
||||||
<env name="TELESCOPE_ENABLED" value="false"/>
|
<env name="TELESCOPE_ENABLED" value="false"/>
|
||||||
</php>
|
</php>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -2,97 +2,96 @@
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper;
|
namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
|
||||||
|
|
||||||
class CollectionExtension
|
class CollectionExtension
|
||||||
{
|
{
|
||||||
public function boot(): void
|
public function forceFileName()
|
||||||
{
|
{
|
||||||
MediaCollection::mixin(new class()
|
return fn ($callback) => $this->registerCustomCallback('forceFileName', $callback);
|
||||||
{
|
}
|
||||||
public function forceFileName()
|
|
||||||
{
|
public function storing()
|
||||||
return fn ($callback) => $this->registerCustomCallback('forceFileName', $callback);
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('storing', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroyed()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('destroyed', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function convert()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('convert', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function after()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('after', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withDefaultProperties()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('withDefaultProperties', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stored()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('stored', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withPropertyValidation()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('withPropertyValidation', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withFallback()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('withFallback', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maxWidth()
|
||||||
|
{
|
||||||
|
return fn ($callback) => $this->registerCustomCallback('maxWidth', $callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runCallback()
|
||||||
|
{
|
||||||
|
return function (string $callback, ...$parameters) {
|
||||||
|
$this->setDefaultCustomCallbacks();
|
||||||
|
|
||||||
|
return call_user_func($this->customCallbacks->get($callback), ...$parameters);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerCustomCallback()
|
||||||
|
{
|
||||||
|
return function (string $name, callable $callback) {
|
||||||
|
$this->setDefaultCustomCallbacks();
|
||||||
|
$this->customCallbacks->put($name, $callback);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setDefaultCustomCallbacks()
|
||||||
|
{
|
||||||
|
return function () {
|
||||||
|
if (property_exists($this, 'customCallbacks')) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
$this->convertTo = null;
|
||||||
public function storing()
|
$this->customCallbacks = collect([
|
||||||
{
|
'forceFileName' => fn ($model, $name) => $name,
|
||||||
return fn ($callback) => $this->registerCustomCallback('storing', $callback);
|
'convert' => fn ($extension) => $extension,
|
||||||
}
|
'maxWidth' => fn ($size) => null,
|
||||||
|
'stored' => fn ($event) => true,
|
||||||
public function destroyed()
|
'after' => fn ($event) => true,
|
||||||
{
|
'destroyed' => fn ($event) => true,
|
||||||
return fn ($callback) => $this->registerCustomCallback('destroyed', $callback);
|
'storing' => fn ($adder, $name) => $adder,
|
||||||
}
|
'withDefaultProperties' => fn ($path) => [],
|
||||||
|
'withPropertyValidation' => fn ($path) => [],
|
||||||
public function after()
|
'withFallback' => fn ($parent) => null,
|
||||||
{
|
]);
|
||||||
return fn ($callback) => $this->registerCustomCallback('after', $callback);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
public function withDefaultProperties()
|
|
||||||
{
|
|
||||||
return fn ($callback) => $this->registerCustomCallback('withDefaultProperties', $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stored()
|
|
||||||
{
|
|
||||||
return fn ($callback) => $this->registerCustomCallback('stored', $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withPropertyValidation()
|
|
||||||
{
|
|
||||||
return fn ($callback) => $this->registerCustomCallback('withPropertyValidation', $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withFallback()
|
|
||||||
{
|
|
||||||
return fn ($callback) => $this->registerCustomCallback('withFallback', $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function maxWidth()
|
|
||||||
{
|
|
||||||
return fn ($callback) => $this->registerCustomCallback('maxWidth', $callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runCallback()
|
|
||||||
{
|
|
||||||
return function (string $callback, ...$parameters) {
|
|
||||||
$this->setDefaultCustomCallbacks();
|
|
||||||
|
|
||||||
return call_user_func($this->customCallbacks->get($callback), ...$parameters);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function registerCustomCallback()
|
|
||||||
{
|
|
||||||
return function (string $name, callable $callback) {
|
|
||||||
$this->setDefaultCustomCallbacks();
|
|
||||||
$this->customCallbacks->put($name, $callback);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDefaultCustomCallbacks()
|
|
||||||
{
|
|
||||||
return function () {
|
|
||||||
if (property_exists($this, 'customCallbacks')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->customCallbacks = collect([
|
|
||||||
'forceFileName' => fn ($model, $name) => $name,
|
|
||||||
'maxWidth' => fn ($size) => null,
|
|
||||||
'stored' => fn ($event) => true,
|
|
||||||
'after' => fn ($event) => true,
|
|
||||||
'destroyed' => fn ($event) => true,
|
|
||||||
'storing' => fn ($adder, $name) => $adder,
|
|
||||||
'withDefaultProperties' => fn ($path, $pathinfo) => [],
|
|
||||||
'withPropertyValidation' => fn ($path) => [],
|
|
||||||
'withFallback' => fn ($parent) => null,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Spatie\LaravelData\Attributes\MapInputName;
|
||||||
|
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||||
|
use Spatie\LaravelData\Data;
|
||||||
|
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||||
|
use Spatie\LaravelData\DataCollection;
|
||||||
|
use Spatie\LaravelData\Lazy;
|
||||||
|
|
||||||
|
#[MapInputName(SnakeCaseMapper::class)]
|
||||||
|
#[MapOutputName(SnakeCaseMapper::class)]
|
||||||
|
class DeferredMediaData extends Data
|
||||||
|
{
|
||||||
|
public string $collectionName;
|
||||||
|
|
||||||
|
public Lazy|string $path;
|
||||||
|
|
||||||
|
public static function fromPath(string $path, MediaCollection $collection): self
|
||||||
|
{
|
||||||
|
return self::factory()->withoutMagicalCreation()->from([
|
||||||
|
'path' => Lazy::create(fn () => $path),
|
||||||
|
'collection_name' => $collection->name,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function collectionFromPaths(Collection $paths, MediaCollection $collection): Collection
|
||||||
|
{
|
||||||
|
return static::collect($paths->map(fn ($path) => static::fromPath($path, $collection)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function with(): array
|
||||||
|
{
|
||||||
|
$file = new MediaFile(Storage::disk(config('media-library.temp_disk'))->path($this->path->resolve()));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'is_deferred' => true,
|
||||||
|
'original_url' => $this->storage()->url($this->path->resolve()),
|
||||||
|
'name' => $file->getBasename(),
|
||||||
|
'size' => $file->getSize(),
|
||||||
|
'file_name' => $file->getFilename(),
|
||||||
|
'mime_type' => $file->getMimeType(),
|
||||||
|
'icon' => Storage::disk('public')->url('filetypes/' . str()->slug($file->getMimeType()) . '.svg'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function storage(): FilesystemAdapter
|
||||||
|
{
|
||||||
|
return Storage::disk(config('media-library.temp_disk'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
trait DefersUploads
|
||||||
|
{
|
||||||
|
public function setDeferredUploads(array $uploads): void
|
||||||
|
{
|
||||||
|
if (array_key_exists('collection_name', $uploads)) {
|
||||||
|
$uploads = [$uploads];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uploads as $upload) {
|
||||||
|
$this->runMediaUploadForSingleFile($upload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runMediaUploadForSingleFile(array $fileData): void
|
||||||
|
{
|
||||||
|
$collection = $this->getMediaCollection($fileData['collection_name']);
|
||||||
|
|
||||||
|
$file = new MediaFile($this->storage()->path($fileData['file_name']));
|
||||||
|
$file->setBasename($collection->runCallback('forceFileName', $this, $file->getBasename()));
|
||||||
|
|
||||||
|
$adder = $this->addMediaFromDisk('media-library/' . $fileData['file_name'], config('media-library.temp_disk'))
|
||||||
|
->usingName($file->getBasename())
|
||||||
|
->usingFileName($file->getFilename())
|
||||||
|
->withCustomProperties($collection->runCallback('withDefaultProperties', $file->getFilename()));
|
||||||
|
|
||||||
|
tap(
|
||||||
|
$collection->runCallback('storing', $adder, $file->getFilename())->toMediaCollection($collection->name),
|
||||||
|
fn ($media) => $collection->runCallback('stored', $media)
|
||||||
|
);
|
||||||
|
|
||||||
|
$collection->runCallback('after', $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function storage(): FilesystemAdapter
|
||||||
|
{
|
||||||
|
return Storage::disk(config('media-library.temp_disk'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,19 +2,20 @@
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper;
|
namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
use Imagick;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Support\Collection;
|
||||||
use Spatie\Image\Image;
|
use Spatie\Image\Image;
|
||||||
use Spatie\LaravelData\DataCollection;
|
use Spatie\LaravelData\DataCollection;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
|
||||||
use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidBase64Data;
|
use Spatie\MediaLibrary\MediaCollections\Exceptions\InvalidBase64Data;
|
||||||
use Spatie\MediaLibrary\MediaCollections\FileAdder;
|
use Spatie\MediaLibrary\MediaCollections\FileAdder;
|
||||||
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
use Zoomyboy\MedialibraryHelper\Rules\ModelRule;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
|
||||||
class MediaController
|
class MediaController
|
||||||
{
|
{
|
||||||
|
@ -23,48 +24,83 @@ class MediaController
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'name' => 'string',
|
'parent' => ['required', new ModelRule()],
|
||||||
'model' => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$model = $this->validateModel($request);
|
if (is_null($request->input('parent.id'))) {
|
||||||
$collection = $model->getMediaCollection($request->input('collection'));
|
return $this->storeDeferred($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = ModelRule::getModel($request->input('parent'));
|
||||||
|
$collection = ModelRule::getCollection($request->input('parent'));
|
||||||
$isSingle = 1 === $collection->collectionSizeLimit;
|
$isSingle = 1 === $collection->collectionSizeLimit;
|
||||||
$this->authorize('storeMedia', [$model, $collection->name]);
|
$this->authorize('storeMedia', [$model, $collection->name]);
|
||||||
|
|
||||||
$request->validate($isSingle ? [
|
$request->validate($isSingle ? [
|
||||||
'payload' => 'array',
|
'payload' => 'array',
|
||||||
'payload.*' => '',
|
'payload.name' => 'required|string|regex:/\..*$/|max:255',
|
||||||
'payload.name' => 'required|string|max:255',
|
|
||||||
'payload.content' => 'required|string',
|
'payload.content' => 'required|string',
|
||||||
] : [
|
] : [
|
||||||
'payload' => 'required|array|min:1',
|
'payload' => 'required|array|min:1',
|
||||||
'payload.*' => 'array',
|
'payload.*' => 'array',
|
||||||
'payload.*.name' => 'string',
|
'payload.*.name' => 'required|string|regex:/\..*$/|max:255',
|
||||||
'payload.*.content' => 'string',
|
'payload.*.content' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$content = $isSingle ? [$request->input('payload')] : $request->input('payload');
|
$content = $isSingle ? [$request->input('payload')] : $request->input('payload');
|
||||||
|
|
||||||
$medias = collect($content)->map(function ($c) use ($collection, $model) {
|
$medias = collect($content)->map(function ($c) use ($collection, $model) {
|
||||||
$pathinfo = pathinfo($c['name']);
|
$file = new MediaFile($c['name']);
|
||||||
$basename = $collection->runCallback('forceFileName', $model, $pathinfo['filename']);
|
$file->setBasename($collection->runCallback('forceFileName', $model, $file->getBasename()));
|
||||||
$path = $basename . '.' . $pathinfo['extension'];
|
$file->setExtension(MediaFile::extensionFromData($c['content']));
|
||||||
|
|
||||||
|
$file->setExtension($collection->runCallback('convert', $file->getExtension()));
|
||||||
$adder = $this->fileAdderFromData($model, $c['content'], $collection)
|
$adder = $this->fileAdderFromData($model, $c['content'], $collection)
|
||||||
->usingName($basename)
|
->usingName($file->getBasename())
|
||||||
->usingFileName($path)
|
->usingFileName($file->getFilename())
|
||||||
->withCustomProperties($collection->runCallback('withDefaultProperties', $path, $pathinfo));
|
->withCustomProperties($collection->runCallback('withDefaultProperties', $file->getFilename()));
|
||||||
|
|
||||||
return tap(
|
return tap(
|
||||||
$collection->runCallback('storing', $adder, $path)->toMediaCollection($collection->name),
|
$collection->runCallback('storing', $adder, $file->getFilename())->toMediaCollection($collection->name),
|
||||||
fn ($media) => $collection->runCallback('stored', $media)
|
fn ($media) => $collection->runCallback('stored', $media)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$collection->runCallback('after', $model->fresh());
|
$collection->runCallback('after', $model->fresh());
|
||||||
|
|
||||||
return $isSingle ? MediaData::from($medias->first()) : MediaData::collection($medias);
|
return $isSingle ? MediaData::from($medias->first()) : response(MediaData::collect($medias), 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function storeDeferred(Request $request)
|
||||||
|
{
|
||||||
|
$modelName = ModelRule::getModelClassName($request->input('parent'));
|
||||||
|
$collection = ModelRule::getCollection($request->input('parent'));
|
||||||
|
$isSingle = 1 === $collection->collectionSizeLimit;
|
||||||
|
|
||||||
|
$request->validate($isSingle ? [
|
||||||
|
'payload' => 'array',
|
||||||
|
'payload.name' => 'required|string|regex:/\..*$/|max:255',
|
||||||
|
'payload.content' => 'required|string',
|
||||||
|
] : [
|
||||||
|
'payload' => 'required|array|min:1',
|
||||||
|
'payload.*' => 'array',
|
||||||
|
'payload.*.name' => 'required|string|regex:/\..*$/|max:255',
|
||||||
|
'payload.*.content' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = $isSingle ? [$request->input('payload')] : $request->input('payload');
|
||||||
|
|
||||||
|
$tempPaths = collect($content)->map(function ($c) use ($collection) {
|
||||||
|
$file = new MediaFile($c['name']);
|
||||||
|
$file->setExtension(MediaFile::extensionFromData($c['content']));
|
||||||
|
$file->setExtension($collection->runCallback('convert', $file->getExtension()));
|
||||||
|
$tmpFile = $this->storeTemporaryFile($c['content'], $collection);
|
||||||
|
Storage::disk(config('media-library.temp_disk'))->move($tmpFile, 'media-library/' . $file->getFilename());
|
||||||
|
return 'media-library/' . $file->getFilename();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->authorize('storeMedia', [$modelName, null, $collection->name]);
|
||||||
|
return $isSingle ? DeferredMediaData::fromPath($tempPaths->first(), $collection) : response(DeferredMediaData::collectionFromPaths($tempPaths, $collection), 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Media $media): MediaData
|
public function update(Request $request, Media $media): MediaData
|
||||||
|
@ -82,7 +118,7 @@ class MediaController
|
||||||
return MediaData::from($media);
|
return MediaData::from($media);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(Request $request, $parentModel, int $parentId, string $collectionName): MediaData|DataCollection
|
public function index(Request $request, $parentModel, int $parentId, string $collectionName): MediaData|JsonResponse
|
||||||
{
|
{
|
||||||
$model = app('media-library-helpers')->get($parentModel);
|
$model = app('media-library-helpers')->get($parentModel);
|
||||||
$model = $model::find($parentId);
|
$model = $model::find($parentId);
|
||||||
|
@ -98,7 +134,7 @@ class MediaController
|
||||||
|
|
||||||
return $isSingle
|
return $isSingle
|
||||||
? MediaData::from($model->getFirstMedia($collectionName))
|
? MediaData::from($model->getFirstMedia($collectionName))
|
||||||
: MediaData::collection($model->getMedia($collectionName));
|
: response()->json(MediaData::collect($model->getMedia($collectionName))->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Media $media, Request $request): JsonResponse
|
public function destroy(Media $media, Request $request): JsonResponse
|
||||||
|
@ -118,27 +154,7 @@ class MediaController
|
||||||
return property_exists($collection, $callback);
|
return property_exists($collection, $callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function validateModel(Request $request): HasMedia
|
private function storeTemporaryFile(string $data, MediaCollection $collection): string
|
||||||
{
|
|
||||||
$model = app('media-library-helpers')->get($request->input('model'));
|
|
||||||
|
|
||||||
$request->validate([
|
|
||||||
'collection' => [
|
|
||||||
'required',
|
|
||||||
'string',
|
|
||||||
Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name')),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$model = $model::find($request->input('id'));
|
|
||||||
if (!$model) {
|
|
||||||
throw ValidationException::withMessages(['model' => 'nicht gefunden']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $model;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fileAdderFromData($model, $data, $collection): FileAdder
|
|
||||||
{
|
{
|
||||||
$maxWidth = $collection->runCallback('maxWidth', 9);
|
$maxWidth = $collection->runCallback('maxWidth', 9);
|
||||||
if (str_contains($data, ';base64')) {
|
if (str_contains($data, ';base64')) {
|
||||||
|
@ -158,21 +174,24 @@ class MediaController
|
||||||
throw InvalidBase64Data::create();
|
throw InvalidBase64Data::create();
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmpFile = tempnam(sys_get_temp_dir(), 'media-library');
|
$tmpFile = 'media-library/' . str()->uuid()->toString() . '.' . $collection->runCallback('convert', MediaFile::extensionFromData($data));
|
||||||
file_put_contents($tmpFile, $binaryData);
|
Storage::disk(config('media-library.temp_disk'))->put($tmpFile, $binaryData);
|
||||||
|
|
||||||
$i = (new Imagick());
|
$imagePath = Storage::disk(config('media-library.temp_disk'))->path($tmpFile);
|
||||||
$i->readImage($tmpFile);
|
$image = Image::load($imagePath);
|
||||||
|
if (null !== $maxWidth && 'image/jpeg' === Storage::disk(config('media-library.temp_disk'))->mimeType($tmpFile)) {
|
||||||
if ($i->getImageFormat() === 'HEIC') {
|
$image->width($maxWidth);
|
||||||
$i->setFormat('jpg');
|
|
||||||
$i->writeImage($tmpFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $maxWidth && 'image/jpeg' === mime_content_type($tmpFile)) {
|
$image->save();
|
||||||
Image::load($tmpFile)->width($maxWidth)->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $model->addMedia($tmpFile);
|
return $tmpFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function fileAdderFromData($model, $data, $collection): FileAdder
|
||||||
|
{
|
||||||
|
$tmpFile = $this->storeTemporaryFile($data, $collection);
|
||||||
|
|
||||||
|
return $model->addMediaFromDisk($tmpFile, config('media-library.temp_disk'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,17 @@ class MediaData extends Data
|
||||||
|
|
||||||
public bool $fallback = false;
|
public bool $fallback = false;
|
||||||
|
|
||||||
|
public bool $isDeferred = false;
|
||||||
|
|
||||||
public static function fromMedia(Media $media): self
|
public static function fromMedia(Media $media): self
|
||||||
{
|
{
|
||||||
$conversions = collect($media->getMediaConversionNames())->flip()->map(fn ($integer, $conversion) => $media->hasGeneratedConversion($conversion)
|
$conversions = collect($media->getMediaConversionNames())->flip()->map(
|
||||||
? ['original_url' => $media->getFullUrl($conversion)]
|
fn ($integer, $conversion) => $media->hasGeneratedConversion($conversion)
|
||||||
: null,
|
? ['original_url' => $media->getFullUrl($conversion)]
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
return self::withoutMagicalCreationFrom([
|
return self::factory()->withoutMagicalCreation()->from([
|
||||||
...$media->toArray(),
|
...$media->toArray(),
|
||||||
'conversions' => $conversions->toArray(),
|
'conversions' => $conversions->toArray(),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
|
|
||||||
|
class MediaFile
|
||||||
|
{
|
||||||
|
|
||||||
|
public File $file;
|
||||||
|
|
||||||
|
public function __construct(string $path)
|
||||||
|
{
|
||||||
|
$this->file = new File($path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBasename(): string
|
||||||
|
{
|
||||||
|
return $this->file->getBasename('.' . $this->file->getExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBasename(string $basename): void
|
||||||
|
{
|
||||||
|
$newInstance = new self(($this->getPath() ? $this->getPath() . '/' : '') . $basename . '.' . $this->getExtension());
|
||||||
|
$this->file = $newInstance->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExtension(string $extension): void
|
||||||
|
{
|
||||||
|
$newInstance = new self(($this->getPath() ? $this->getPath() . '/' : '') . $this->getBasename() . '.' . $extension);
|
||||||
|
$this->file = $newInstance->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($method, $arguments)
|
||||||
|
{
|
||||||
|
return $this->file->{$method}(...$arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function extensionFromData(string $data): string
|
||||||
|
{
|
||||||
|
$tempFile = sys_get_temp_dir() . '/' . str()->uuid()->toString();
|
||||||
|
|
||||||
|
file_put_contents($tempFile, base64_decode($data));
|
||||||
|
|
||||||
|
$extension = (new self($tempFile))->guessExtension();
|
||||||
|
|
||||||
|
unlink($tempFile);
|
||||||
|
|
||||||
|
return $extension;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ class OrderController
|
||||||
$mediaCount = collect($request->order)->map(function ($media) {
|
$mediaCount = collect($request->order)->map(function ($media) {
|
||||||
$media = Media::findOrFail($media);
|
$media = Media::findOrFail($media);
|
||||||
|
|
||||||
return $media->model_id.'_'.$media->model_type;
|
return $media->model_id . '_' . $media->model_type;
|
||||||
})->unique()->count();
|
})->unique()->count();
|
||||||
|
|
||||||
if (1 !== $mediaCount) {
|
if (1 !== $mediaCount) {
|
||||||
|
@ -31,6 +31,6 @@ class OrderController
|
||||||
|
|
||||||
$model->getMediaCollection($collectionName)->runCallback('after', $model->fresh());
|
$model->getMediaCollection($collectionName)->runCallback('after', $model->fresh());
|
||||||
|
|
||||||
return MediaData::collection($model->getMedia($collectionName));
|
return MediaData::collect($model->getMedia($collectionName))->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper\Rules;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Validation\InvokableRule;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Illuminate\Validation\Factory;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||||
|
|
||||||
|
class ModelRule implements InvokableRule
|
||||||
|
{
|
||||||
|
|
||||||
|
public ?string $collection;
|
||||||
|
public ?int $id;
|
||||||
|
public ?string $model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{?id: int, ?collection: string, ?model: string} $attribute
|
||||||
|
*/
|
||||||
|
public function __invoke($attribute, $value, $fail)
|
||||||
|
{
|
||||||
|
app(Factory::class)->make([$attribute => $value], [
|
||||||
|
"{$attribute}.id" => 'nullable|integer|gt:0',
|
||||||
|
"{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
|
||||||
|
"{$attribute}.collection_name" => 'required|string',
|
||||||
|
])->validate();
|
||||||
|
|
||||||
|
|
||||||
|
$this->model = data_get($value, 'model');
|
||||||
|
$this->id = data_get($value, 'id');
|
||||||
|
$this->collection = data_get($value, 'collection_name');
|
||||||
|
|
||||||
|
if (is_null($this->id)) {
|
||||||
|
$this->validateDeferred($attribute, $value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model = app('media-library-helpers')->get($this->model);
|
||||||
|
app(Factory::class)->make([$attribute => $value], [
|
||||||
|
"{$attribute}.collection_name" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))],
|
||||||
|
"{$attribute}.id" => ['required', 'exists:' . (new $model)->getTable() . ',id'],
|
||||||
|
])->validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateDeferred($attribute, $value): void
|
||||||
|
{
|
||||||
|
app(Factory::class)->make([$attribute => $value], [
|
||||||
|
"{$attribute}.model" => ['required', 'string', Rule::in(app('media-library-helpers')->keys())],
|
||||||
|
"{$attribute}.collection_name" => 'required|string',
|
||||||
|
])->validate();
|
||||||
|
|
||||||
|
$model = app('media-library-helpers')->get($this->model);
|
||||||
|
|
||||||
|
app(Factory::class)->make([$attribute => $value], [
|
||||||
|
"{$attribute}.collection_name" => ['required', Rule::in((new $model())->getRegisteredMediaCollections()->pluck('name'))],
|
||||||
|
])->validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{?id: int, ?collection_name: string, ?model: string} $modelParam
|
||||||
|
*/
|
||||||
|
public static function getModel($modelParam): HasMedia
|
||||||
|
{
|
||||||
|
$model = static::getModelClassName($modelParam);
|
||||||
|
|
||||||
|
return $model::find($modelParam['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{?id: int, ?collection_name: string, ?model: string} $modelParam
|
||||||
|
* @return class-string<HasMedia>
|
||||||
|
*/
|
||||||
|
public static function getModelClassName($modelParam): string
|
||||||
|
{
|
||||||
|
return app('media-library-helpers')->get($modelParam['model']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array{?id: int, ?collection_name: string, ?model: string} $modelParam
|
||||||
|
*/
|
||||||
|
public static function getCollection($modelParam): MediaCollection
|
||||||
|
{
|
||||||
|
$className = static::getModelClassName($modelParam);
|
||||||
|
|
||||||
|
return (new $className)->getMediaCollection($modelParam['collection_name']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ namespace Zoomyboy\MedialibraryHelper;
|
||||||
|
|
||||||
use Illuminate\Routing\Router;
|
use Illuminate\Routing\Router;
|
||||||
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
|
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
|
||||||
|
use Spatie\MediaLibrary\MediaCollections\MediaCollection;
|
||||||
|
|
||||||
class ServiceProvider extends BaseServiceProvider
|
class ServiceProvider extends BaseServiceProvider
|
||||||
{
|
{
|
||||||
|
@ -11,6 +12,8 @@ class ServiceProvider extends BaseServiceProvider
|
||||||
{
|
{
|
||||||
app()->bind('media-library-helpers', fn () => collect([]));
|
app()->bind('media-library-helpers', fn () => collect([]));
|
||||||
app()->singleton(CollectionExtension::class, fn () => new CollectionExtension());
|
app()->singleton(CollectionExtension::class, fn () => new CollectionExtension());
|
||||||
|
|
||||||
|
$this->mergeConfigFrom(__DIR__ . '/../config/media-library.php', 'media-library');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
|
@ -23,7 +26,8 @@ class ServiceProvider extends BaseServiceProvider
|
||||||
$router->patch('mediaupload/{media}', [MediaController::class, 'update'])->name('media.update');
|
$router->patch('mediaupload/{media}', [MediaController::class, 'update'])->name('media.update');
|
||||||
});
|
});
|
||||||
|
|
||||||
app(CollectionExtension::class)->boot();
|
|
||||||
|
MediaCollection::mixin(app(CollectionExtension::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
providers:
|
||||||
|
- Spatie\MediaLibrary\MediaLibraryServiceProvider
|
||||||
|
- Spatie\LaravelData\LaravelDataServiceProvider
|
||||||
|
- Zoomyboy\MedialibraryHelper\ServiceProvider
|
||||||
|
|
||||||
|
migrations:
|
||||||
|
- tests/workbench/database/migrations
|
||||||
|
|
||||||
|
workbench:
|
||||||
|
start: '/'
|
||||||
|
install: false
|
||||||
|
discovers:
|
||||||
|
web: false
|
||||||
|
api: false
|
||||||
|
commands: false
|
||||||
|
components: false
|
||||||
|
views: false
|
||||||
|
build: []
|
||||||
|
assets: []
|
||||||
|
sync: []
|
|
@ -0,0 +1,196 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
test('it uploads a deferred file to a collection', function () {
|
||||||
|
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
|
||||||
|
'payload' => ['content' => base64_encode(test()->pdfFile()->getContent()), 'name' => 'beispiel.pdf'],
|
||||||
|
])
|
||||||
|
->assertStatus(201)
|
||||||
|
->assertExactJson([
|
||||||
|
'is_deferred' => true,
|
||||||
|
'original_url' => Storage::disk('temp')->url('media-library/beispiel.pdf'),
|
||||||
|
'name' => 'beispiel',
|
||||||
|
'collection_name' => 'defaultSingleFile',
|
||||||
|
'size' => 64576,
|
||||||
|
'file_name' => 'beispiel.pdf',
|
||||||
|
'mime_type' => 'application/pdf',
|
||||||
|
'icon' => url('storage/filetypes/applicationpdf.svg'),
|
||||||
|
]);
|
||||||
|
Storage::disk('temp')->assertExists('media-library/beispiel.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it forces filename when uploading', function () {
|
||||||
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'singleJpegFile', 'id' => null],
|
||||||
|
'payload' => ['content' => base64_encode(test()->pngFile()->getContent()), 'name' => 'beispiel.png'],
|
||||||
|
])
|
||||||
|
->assertStatus(201)
|
||||||
|
->assertExactJson([
|
||||||
|
'is_deferred' => true,
|
||||||
|
'original_url' => Storage::disk('temp')->url('media-library/beispiel.jpg'),
|
||||||
|
'name' => 'beispiel',
|
||||||
|
'collection_name' => 'singleJpegFile',
|
||||||
|
'size' => 490278,
|
||||||
|
'file_name' => 'beispiel.jpg',
|
||||||
|
'mime_type' => 'image/jpeg',
|
||||||
|
'icon' => url('storage/filetypes/imagejpeg.svg'),
|
||||||
|
]);
|
||||||
|
Storage::disk('temp')->assertExists('media-library/beispiel.jpg');
|
||||||
|
Storage::disk('temp')->assertMissing('media-library/beispiel.png');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it stores a file to media library after deferred upload', function () {
|
||||||
|
Carbon::setTestNow(Carbon::parse('2023-05-06 06:00:00'));
|
||||||
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
Storage::disk('temp')->put('media-library/beispiel.pdf', test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
$post = test()->newPost();
|
||||||
|
|
||||||
|
$post->setDeferredUploads([
|
||||||
|
'file_name' => 'beispiel.pdf',
|
||||||
|
'collection_name' => 'singleForced',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$media = $post->getMedia('singleForced')->first();
|
||||||
|
test()->assertNotNull($media);
|
||||||
|
test()->assertEquals('beispiel 2023-05-06', $media->name);
|
||||||
|
Storage::disk('temp')->assertMissing('media-library/beispiel.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it stores multiple files to media library after deferred upload', function () {
|
||||||
|
Carbon::setTestNow(Carbon::parse('2023-05-06 06:00:00'));
|
||||||
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
Storage::disk('temp')->put('media-library/beispiel.pdf', test()->pdfFile()->getContent());
|
||||||
|
Storage::disk('temp')->put('media-library/beispiel2.pdf', test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
$post = test()->newPost();
|
||||||
|
|
||||||
|
$post->setDeferredUploads([
|
||||||
|
[
|
||||||
|
'file_name' => 'beispiel.pdf',
|
||||||
|
'collection_name' => 'multipleForced',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'file_name' => 'beispiel2.pdf',
|
||||||
|
'collection_name' => 'multipleForced',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$medias = $post->getMedia('multipleForced');
|
||||||
|
test()->assertCount(2, $medias);
|
||||||
|
test()->assertEquals('beispiel 2023-05-06', $medias->get(0)->name);
|
||||||
|
test()->assertEquals('beispiel2 2023-05-06', $medias->get(1)->name);
|
||||||
|
Storage::disk('temp')->assertMissing('media-library/beispiel.pdf');
|
||||||
|
Storage::disk('temp')->assertMissing('media-library/beispiel2.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it uploads multiple files', function () {
|
||||||
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => null],
|
||||||
|
'payload' => [
|
||||||
|
['content' => $content, 'name' => 'beispiel.pdf'],
|
||||||
|
['content' => $content, 'name' => 'beispiel2.pdf'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', $payload)
|
||||||
|
->assertStatus(201)
|
||||||
|
->assertJson([
|
||||||
|
[
|
||||||
|
'is_deferred' => true,
|
||||||
|
'original_url' => Storage::disk('temp')->url('media-library/beispiel.pdf'),
|
||||||
|
'name' => 'beispiel',
|
||||||
|
'collection_name' => 'multipleForced',
|
||||||
|
'size' => 64576,
|
||||||
|
'file_name' => 'beispiel.pdf',
|
||||||
|
'mime_type' => 'application/pdf',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'is_deferred' => true,
|
||||||
|
'original_url' => Storage::disk('temp')->url('media-library/beispiel2.pdf'),
|
||||||
|
'name' => 'beispiel2',
|
||||||
|
'collection_name' => 'multipleForced',
|
||||||
|
'size' => 64576,
|
||||||
|
'file_name' => 'beispiel2.pdf',
|
||||||
|
'mime_type' => 'application/pdf',
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
Storage::disk('temp')->assertExists('media-library/beispiel.pdf');
|
||||||
|
Storage::disk('temp')->assertExists('media-library/beispiel2.pdf');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it reduces file size', function () {
|
||||||
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
|
||||||
|
'payload' => [
|
||||||
|
'content' => base64_encode(test()->jpgFile()->getContent()),
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$size = getimagesize(Storage::disk('temp')->path('media-library/beispiel bild.jpg'));
|
||||||
|
test()->assertEquals(250, $size[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it handles authorization with collection', function () {
|
||||||
|
test()->auth(['storeMedia' => ['collection' => 'rtrt']])->registerModel();
|
||||||
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
|
||||||
|
'payload' => [
|
||||||
|
'content' => $content,
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
])->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it handles authorization with collection correctly', function () {
|
||||||
|
test()->auth(['storeMedia' => ['collection' => 'defaultSingleFile']])->registerModel();
|
||||||
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => null],
|
||||||
|
'payload' => [
|
||||||
|
'content' => $content,
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
])->assertStatus(201);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it needs a collection', function ($key, $value) {
|
||||||
|
test()->auth()->registerModel();
|
||||||
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'parent' => ['model' => 'post', 'collection' => '', 'id' => null],
|
||||||
|
'payload' => [
|
||||||
|
'content' => $content,
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
data_set($payload, $key, $value);
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', $payload)->assertJsonValidationErrors($key);
|
||||||
|
})->with(function () {
|
||||||
|
yield ['parent.collection_name', ''];
|
||||||
|
yield ['parent.collection_name', -1];
|
||||||
|
yield ['parent.collection_name', 'missingcollection'];
|
||||||
|
yield ['parent.model', 'lalala'];
|
||||||
|
yield ['parent.model', -1];
|
||||||
|
yield ['parent.model', ''];
|
||||||
|
});
|
|
@ -3,8 +3,8 @@
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
use Workbench\App\Events\MediaChange;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaDestroyed;
|
use Workbench\App\Events\MediaDestroyed;
|
||||||
|
|
||||||
test('it deletes multiple media', function () {
|
test('it deletes multiple media', function () {
|
||||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
$this->auth()->registerModel()->withoutExceptionHandling();
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
|
|
||||||
test('it gets all medias', function () {
|
test('it gets all medias', function () {
|
||||||
$this->auth()->registerModel();
|
$this->auth()->registerModel();
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
$post = $this->newPost();
|
$post = $this->newPost();
|
||||||
$firstMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
$firstMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||||
$secondMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
$secondMedia = $post->addMedia($this->pdfFile()->getPathname())->withCustomProperties(['test' => 'old'])->preservingOriginal()->toMediaCollection('images');
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Console\VendorPublishCommand;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Spatie\MediaLibrary\MediaLibraryServiceProvider;
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
@unlink(config_path('media-library.php'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('modifies config file', function () {
|
||||||
|
Artisan::call(VendorPublishCommand::class, ['--provider' => MediaLibraryServiceProvider::class, '--tag' => 'config']);
|
||||||
|
$configContents = file_get_contents(config_path('media-library.php'));
|
||||||
|
$configContents = preg_replace('/\'image_driver\' => env.*/', '\'image_driver\' => "lala",', $configContents);
|
||||||
|
file_put_contents(config_path('media-library.php'), $configContents);
|
||||||
|
|
||||||
|
$this->tearDownTheTestEnvironment();
|
||||||
|
$this->setUpTheTestEnvironment();
|
||||||
|
|
||||||
|
$this->assertEquals('lala', config('media-library.image_driver'));
|
||||||
|
$this->assertEquals('temp', config('media-library.temp_disk'));
|
||||||
|
});
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
use Workbench\App\Events\MediaChange;
|
||||||
|
|
||||||
test('it can reorder media', function () {
|
test('it can reorder media', function () {
|
||||||
Event::fake();
|
Event::fake();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
use Workbench\App\Events\MediaChange;
|
||||||
|
|
||||||
test('it updates a single files properties', function () {
|
test('it updates a single files properties', function () {
|
||||||
Event::fake();
|
Event::fake();
|
||||||
|
|
|
@ -4,62 +4,65 @@ namespace Zoomyboy\MedialibraryHelper\Tests\Feature;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaStored;
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
|
use Workbench\App\Events\MediaChange;
|
||||||
|
use Workbench\App\Events\MediaStored;
|
||||||
|
|
||||||
test('it uploads a single file to a single file collection', function () {
|
test('it uploads a single file to a single file collection', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'defaultSingleFile',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.pdf',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$this->assertCount(1, $post->getMedia('defaultSingleFile'));
|
test()->assertCount(1, $post->getMedia('defaultSingleFile'));
|
||||||
$media = $post->getFirstMedia('defaultSingleFile');
|
$media = $post->getFirstMedia('defaultSingleFile');
|
||||||
$response->assertJsonPath('id', $media->id);
|
$response->assertJsonPath('id', $media->id);
|
||||||
$response->assertJsonPath('original_url', $media->getFullUrl());
|
$response->assertJsonPath('original_url', $media->getFullUrl());
|
||||||
$response->assertJsonPath('size', 3028);
|
$response->assertJsonPath('size', 3028);
|
||||||
$response->assertJsonPath('name', 'beispiel bild');
|
$response->assertJsonPath('name', 'beispiel bild');
|
||||||
$response->assertJsonPath('collection_name', 'defaultSingleFile');
|
$response->assertJsonPath('collection_name', 'defaultSingleFile');
|
||||||
$response->assertJsonPath('file_name', 'beispiel-bild.jpg');
|
$response->assertJsonPath('file_name', 'beispiel-bild.pdf');
|
||||||
|
$response->assertJsonPath('is_deferred', false);
|
||||||
$response->assertJsonMissingPath('model_type');
|
$response->assertJsonMissingPath('model_type');
|
||||||
$response->assertJsonMissingPath('model_id');
|
$response->assertJsonMissingPath('model_id');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it uploads heig image', function () {
|
test('it changes format', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->getFile('heic.jpg', 'heic.jpg')->getContent());
|
$content = base64_encode(test()->pngFile()->getContent());
|
||||||
|
|
||||||
$this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'singleJpegFile', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'conversionsWithDefault',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.png',
|
||||||
],
|
],
|
||||||
])->assertStatus(201);
|
]);
|
||||||
|
|
||||||
|
$response->assertStatus(201)
|
||||||
|
->assertJsonPath('size', 490278)
|
||||||
|
->assertJsonPath('file_name', 'beispiel-bild.jpg')
|
||||||
|
->assertJsonPath('mime_type', 'image/jpeg');
|
||||||
|
$this->assertEquals('image/jpeg', MimeTypes::getDefault()->guessMimeType($post->getFirstMedia('singleJpegFile')->getPath()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it uploads a single image to a single file collection', function () {
|
test('it uploads a single image to a single file collection', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->jpgFile()->getContent());
|
$content = base64_encode(test()->jpgFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'defaultSingleFile',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
|
@ -67,19 +70,17 @@ test('it uploads a single image to a single file collection', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$this->assertCount(1, $post->getMedia('defaultSingleFile'));
|
test()->assertCount(1, $post->getMedia('defaultSingleFile'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it forces a filename for a single collection', function () {
|
test('it forces a filename for a single collection', function () {
|
||||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->jpgFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'singleForced', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'singleForced',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
|
@ -87,20 +88,18 @@ test('it forces a filename for a single collection', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$this->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('singleForced')->file_name);
|
test()->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('singleForced')->file_name);
|
||||||
$response->assertJsonPath('name', 'beispiel bild 2023-04-04');
|
$response->assertJsonPath('name', 'beispiel bild 2023-04-04');
|
||||||
$response->assertJsonPath('file_name', 'beispiel-bild-2023-04-04.jpg');
|
$response->assertJsonPath('file_name', 'beispiel-bild-2023-04-04.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it sets custom title when storing', function () {
|
test('it sets custom title when storing', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'singleStoringHook', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'singleStoringHook',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
|
@ -110,41 +109,37 @@ test('it sets custom title when storing', function () {
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$media = $post->getFirstMedia('singleStoringHook');
|
$media = $post->getFirstMedia('singleStoringHook');
|
||||||
|
|
||||||
$this->assertEquals('AAA', $media->getCustomProperty('use'));
|
test()->assertEquals('AAA', $media->getCustomProperty('use'));
|
||||||
$this->assertEquals('beispiel bild', $media->getCustomProperty('ttt'));
|
test()->assertEquals('beispiel bild', $media->getCustomProperty('ttt'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it sets custom properties from properties method', function () {
|
test('it sets custom properties from properties method', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'multipleProperties', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'multipleProperties',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.pdf',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$media = $post->getFirstMedia('multipleProperties');
|
$media = $post->getFirstMedia('multipleProperties');
|
||||||
|
|
||||||
$this->assertEquals('beispielBild.jpg', $media->getCustomProperty('test'));
|
test()->assertEquals('beispielBild.pdf', $media->getCustomProperty('test'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it forces a filename for multiple collections', function () {
|
test('it forces a filename for multiple collections', function () {
|
||||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->jpgFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'multipleForced', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'multipleForced',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
[
|
[
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
|
@ -154,20 +149,18 @@ test('it forces a filename for multiple collections', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$this->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('multipleForced')->file_name);
|
test()->assertEquals('beispiel-bild-2023-04-04.jpg', $post->getFirstMedia('multipleForced')->file_name);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it throws event when file has been uploaded', function () {
|
test('it throws event when file has been uploaded', function () {
|
||||||
Event::fake();
|
Event::fake();
|
||||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'singleWithEvent',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
|
@ -182,14 +175,12 @@ test('it throws event when file has been uploaded', function () {
|
||||||
test('it throws event when multiple files uploaded', function () {
|
test('it throws event when multiple files uploaded', function () {
|
||||||
Event::fake();
|
Event::fake();
|
||||||
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
Carbon::setTestNow(Carbon::parse('2023-04-04 00:00:00'));
|
||||||
$this->auth()->registerModel()->withoutExceptionHandling();
|
test()->auth()->registerModel()->withoutExceptionHandling();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$content = base64_encode($this->pdfFile()->getContent());
|
$content = base64_encode(test()->pdfFile()->getContent());
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'multipleFilesWithEvent', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'multipleFilesWithEvent',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
[
|
[
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
|
@ -208,120 +199,164 @@ test('it throws event when multiple files uploaded', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it uploads multiple files', function () {
|
test('it uploads multiple files', function () {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
$file = $this->pdfFile();
|
$file = test()->pdfFile();
|
||||||
$post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images');
|
$post->addMedia($file->getPathname())->preservingOriginal()->toMediaCollection('images');
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$response = test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'images',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
[
|
[
|
||||||
'content' => base64_encode($file->getContent()),
|
'content' => base64_encode($file->getContent()),
|
||||||
'name' => 'aaaa.jpg',
|
'name' => 'aaaa.pdf',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'content' => base64_encode($file->getContent()),
|
'content' => base64_encode($file->getContent()),
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.pdf',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response->assertStatus(201);
|
$response->assertStatus(201);
|
||||||
$this->assertCount(3, $post->getMedia('images'));
|
test()->assertCount(3, $post->getMedia('images'));
|
||||||
|
$this->assertEquals('application/pdf', MimeTypes::getDefault()->guessMimeType($post->getFirstMedia('images')->getPath()));
|
||||||
$media = $post->getMedia('images')->skip(1)->values();
|
$media = $post->getMedia('images')->skip(1)->values();
|
||||||
$this->assertCount(2, $response->json());
|
test()->assertCount(2, $response->json());
|
||||||
$response->assertJsonPath('0.id', $media->get(0)->id);
|
$response->assertJsonPath('0.id', $media->get(0)->id);
|
||||||
$response->assertJsonPath('1.id', $media->get(1)->id);
|
$response->assertJsonPath('1.id', $media->get(1)->id);
|
||||||
$response->assertJsonPath('0.original_url', $media->first()->getFullUrl());
|
$response->assertJsonPath('0.original_url', $media->first()->getFullUrl());
|
||||||
$response->assertJsonPath('0.size', 3028);
|
$response->assertJsonPath('0.size', 64576);
|
||||||
$response->assertJsonPath('0.name', 'aaaa');
|
$response->assertJsonPath('0.name', 'aaaa');
|
||||||
$response->assertJsonPath('0.collection_name', 'images');
|
$response->assertJsonPath('0.collection_name', 'images');
|
||||||
$response->assertJsonPath('0.file_name', 'aaaa.jpg');
|
$response->assertJsonPath('0.file_name', 'aaaa.pdf');
|
||||||
$response->assertJsonMissingPath('0.model_type');
|
$response->assertJsonMissingPath('0.model_type');
|
||||||
$response->assertJsonMissingPath('0.model_id');
|
$response->assertJsonMissingPath('0.model_id');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it returns 403 when not authorized', function () {
|
test('it returns 403 when not authorized', function () {
|
||||||
$this->auth(['storeMedia' => false])->registerModel();
|
test()->auth(['storeMedia' => false])->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'defaultSingleFile',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
],
|
],
|
||||||
]);
|
])->assertStatus(403);
|
||||||
|
|
||||||
$response->assertStatus(403);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it needs validation for single files', function (array $payload, string $invalidFieldName) {
|
test('it checks for model when running authorization', function () {
|
||||||
$this->auth()->registerModel();
|
$otherPost = test()->newPost();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
|
test()->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel();
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
test()->postJson('/mediaupload', [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'defaultSingleFile',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
],
|
],
|
||||||
...$payload,
|
])->assertStatus(201);
|
||||||
]);
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $otherPost->id],
|
||||||
|
'payload' => [
|
||||||
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
])->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it checks for collection when running authorization', function () {
|
||||||
|
$post = test()->newPost();
|
||||||
|
test()->auth(['storeMedia' => ['id' => $post->id, 'collection' => 'defaultSingleFile']])->registerModel();
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
|
'payload' => [
|
||||||
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
])->assertStatus(201);
|
||||||
|
|
||||||
|
test()->postJson('/mediaupload', [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'singleWithEvent', 'id' => $post->id],
|
||||||
|
'payload' => [
|
||||||
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
])->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it needs validation for single files', function (array $payloadOverwrites, string $invalidFieldName) {
|
||||||
|
test()->auth()->registerModel();
|
||||||
|
$post = test()->newPost();
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'parent' => ['model' => 'post', 'collection_name' => 'defaultSingleFile', 'id' => $post->id],
|
||||||
|
'payload' => [
|
||||||
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
|
'name' => 'beispiel bild.jpg',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($payloadOverwrites as $key => $value) {
|
||||||
|
data_set($payload, $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = test()->postJson('/mediaupload', $payload);
|
||||||
|
|
||||||
$response->assertStatus(422);
|
$response->assertStatus(422);
|
||||||
$response->assertJsonValidationErrors($invalidFieldName);
|
$response->assertJsonValidationErrors($invalidFieldName);
|
||||||
})->with(function () {
|
})->with(function () {
|
||||||
yield [['model' => 'missingmodel'], 'model'];
|
yield [['parent.model' => 'missingmodel'], 'parent.model'];
|
||||||
yield [['id' => -1], 'model'];
|
yield [['parent.id' => -1], 'parent.id'];
|
||||||
yield [['collection' => 'missingcollection'], 'collection'];
|
yield [['parent.collection_name' => 'missingcollection'], 'parent.collection_name'];
|
||||||
yield [['payload' => ['name' => 'AAA', 'content' => []]], 'payload.content'];
|
yield [['payload.content' => []], 'payload.content'];
|
||||||
yield [['payload' => ['name' => 'AAA', 'content' => ['UU']]], 'payload.content'];
|
yield [['payload.content' => ['UU']], 'payload.content'];
|
||||||
yield [['payload' => ['name' => 'AAA', 'content' => null]], 'payload.content'];
|
yield [['payload.content' => null], 'payload.content'];
|
||||||
yield [['payload' => ['name' => 'AAA', 'content' => '']], 'payload.content'];
|
yield [['payload.content' => ''], 'payload.content'];
|
||||||
yield [['payload' => ['name' => 'AAA', 'content' => 1]], 'payload.content'];
|
yield [['payload.content' => 1], 'payload.content'];
|
||||||
yield [['payload' => ['name' => '', 'content' => 'aaadfdf']], 'payload.name'];
|
yield [['payload.name' => ''], 'payload.name'];
|
||||||
yield [['payload' => ['name' => ['U'], 'content' => 'aaadfdf']], 'payload.name'];
|
yield [['payload.name' => ['U']], 'payload.name'];
|
||||||
yield [['payload' => ['name' => 1, 'content' => 'aaadfdf']], 'payload.name'];
|
yield [['payload.name' => 1], 'payload.name'];
|
||||||
yield [['payload' => ['name' => null, 'content' => 'aaadfdf']], 'payload.name'];
|
yield [['payload.name' => null], 'payload.name'];
|
||||||
yield [['payload' => 'lalal'], 'payload'];
|
yield [['payload' => 'lalal'], 'payload'];
|
||||||
yield [['payload' => 55], 'payload'];
|
yield [['payload' => 55], 'payload'];
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it needs validation for multiple files', function (array $payload, string $invalidFieldName) {
|
test('it needs validation for multiple files', function (array $payloadOverwrites, string $invalidFieldName) {
|
||||||
$this->auth()->registerModel();
|
test()->auth()->registerModel();
|
||||||
$post = $this->newPost();
|
$post = test()->newPost();
|
||||||
|
|
||||||
$response = $this->postJson('/mediaupload', [
|
$payload = [
|
||||||
'model' => 'post',
|
'parent' => ['model' => 'post', 'collection_name' => 'images', 'id' => $post->id],
|
||||||
'id' => $post->id,
|
|
||||||
'collection' => 'images',
|
|
||||||
'payload' => [
|
'payload' => [
|
||||||
[
|
[
|
||||||
'content' => base64_encode($this->pdfFile()->getContent()),
|
'content' => base64_encode(test()->pdfFile()->getContent()),
|
||||||
'name' => 'beispiel bild.jpg',
|
'name' => 'beispiel bild.jpg',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
...$payload,
|
];
|
||||||
]);
|
|
||||||
|
foreach ($payloadOverwrites as $key => $value) {
|
||||||
|
data_set($payload, $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = test()->postJson('/mediaupload', $payload);
|
||||||
|
|
||||||
|
|
||||||
$response->assertStatus(422);
|
$response->assertStatus(422);
|
||||||
$response->assertJsonValidationErrors($invalidFieldName);
|
$response->assertJsonValidationErrors($invalidFieldName);
|
||||||
})->with(function () {
|
})->with(function () {
|
||||||
yield [['model' => 'missingmodel'], 'model'];
|
yield [['parent.model' => 'missingmodel'], 'parent.model'];
|
||||||
yield [['id' => -1], 'model'];
|
yield [['parent.model' => 'post.missingcollection'], 'parent.model'];
|
||||||
yield [['collection' => 'missingcollection'], 'collection'];
|
yield [['parent.id' => -1], 'parent.id'];
|
||||||
yield [['payload' => 'lalal'], 'payload'];
|
yield [['payload' => 'lalal'], 'payload'];
|
||||||
yield [['payload' => []], 'payload'];
|
yield [['payload' => []], 'payload'];
|
||||||
yield [['payload' => 1], 'payload'];
|
yield [['payload' => 1], 'payload'];
|
||||||
|
|
||||||
yield [['payload' => [['name' => 'AAA', 'content' => []]]], 'payload.0.content'];
|
yield [['payload' => [['name' => 'AAA', 'content' => []]]], 'payload.0.content'];
|
||||||
yield [['payload' => [['name' => 'AAA', 'content' => ['UU']]]], 'payload.0.content'];
|
yield [['payload' => [['name' => 'AAA', 'content' => ['UU']]]], 'payload.0.content'];
|
||||||
yield [['payload' => [['name' => 'AAA', 'content' => null]]], 'payload.0.content'];
|
yield [['payload' => [['name' => 'AAA', 'content' => null]]], 'payload.0.content'];
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Workbench\App\Models\Post;
|
||||||
|
use Workbench\App\Policies\PostPolicy;
|
||||||
|
|
||||||
uses(Zoomyboy\MedialibraryHelper\Tests\TestCase::class)->in('Feature');
|
uses(Zoomyboy\MedialibraryHelper\Tests\TestCase::class)->in('Feature');
|
||||||
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
|
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature');
|
||||||
uses()->beforeEach(fn () => Storage::fake('media'))->in('Feature');
|
uses()->beforeEach(fn () => Storage::fake('media') && Storage::fake('temp'))->in('Feature');
|
||||||
|
uses()->beforeEach(fn () => Gate::policy(Post::class, PostPolicy::class))->in('Feature');
|
||||||
|
|
|
@ -4,30 +4,15 @@ namespace Zoomyboy\MedialibraryHelper\Tests;
|
||||||
|
|
||||||
use Illuminate\Http\File;
|
use Illuminate\Http\File;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Orchestra\Testbench\Concerns\WithWorkbench;
|
||||||
use Orchestra\Testbench\TestCase as BaseTestCase;
|
use Orchestra\Testbench\TestCase as BaseTestCase;
|
||||||
use Spatie\LaravelData\LaravelDataServiceProvider;
|
use Workbench\App\Models\Post;
|
||||||
use Spatie\MediaLibrary\MediaLibraryServiceProvider;
|
use Workbench\App\Models\User;
|
||||||
use Zoomyboy\MedialibraryHelper\ServiceProvider;
|
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Models\Post;
|
|
||||||
|
|
||||||
class TestCase extends BaseTestCase
|
class TestCase extends BaseTestCase
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Define database migrations.
|
|
||||||
*/
|
|
||||||
protected function defineDatabaseMigrations(): void
|
|
||||||
{
|
|
||||||
$this->loadMigrationsFrom(__DIR__ . '/migrations');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getPackageProviders($app): array
|
use WithWorkbench;
|
||||||
{
|
|
||||||
return [
|
|
||||||
ServiceProvider::class,
|
|
||||||
MediaLibraryServiceProvider::class,
|
|
||||||
LaravelDataServiceProvider::class,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a pdf file with a filename and get path.
|
* Generate a pdf file with a filename and get path.
|
||||||
|
@ -42,6 +27,11 @@ class TestCase extends BaseTestCase
|
||||||
return $this->getFile('jpg.jpg', $filename ?: 'jpg.jpg');
|
return $this->getFile('jpg.jpg', $filename ?: 'jpg.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pngFile(?string $filename = null): File
|
||||||
|
{
|
||||||
|
return $this->getFile('png.png', $filename ?: 'png.png');
|
||||||
|
}
|
||||||
|
|
||||||
protected function getFile(string $location, string $as): File
|
protected function getFile(string $location, string $as): File
|
||||||
{
|
{
|
||||||
$path = __DIR__ . '/stubs/' . $location;
|
$path = __DIR__ . '/stubs/' . $location;
|
||||||
|
@ -65,23 +55,8 @@ class TestCase extends BaseTestCase
|
||||||
|
|
||||||
protected function auth(array $policies = []): self
|
protected function auth(array $policies = []): self
|
||||||
{
|
{
|
||||||
$policies = [
|
$this->be(User::factory()->policies($policies)->create());
|
||||||
'storeMedia' => true,
|
|
||||||
'updateMedia' => true,
|
|
||||||
'destroyMedia' => true,
|
|
||||||
'listMedia' => true,
|
|
||||||
...$policies,
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($policies as $ability => $result) {
|
|
||||||
Gate::define($ability, fn (?string $user, string $collectionName) => $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function defineEnvironment($app)
|
|
||||||
{
|
|
||||||
$app['config']->set('media-library.middleware', ['web']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.4 MiB |
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
namespace Workbench\App\Events;
|
||||||
|
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
namespace Workbench\App\Events;
|
||||||
|
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Events;
|
namespace Workbench\App\Events;
|
||||||
|
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Zoomyboy\MedialibraryHelper\Tests\Models;
|
namespace Workbench\App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
@ -8,19 +8,22 @@ use Illuminate\Support\Str;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaChange;
|
use Workbench\App\Events\MediaChange;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaDestroyed;
|
use Workbench\App\Events\MediaDestroyed;
|
||||||
use Zoomyboy\MedialibraryHelper\Tests\Events\MediaStored;
|
use Workbench\App\Events\MediaStored;
|
||||||
|
use Zoomyboy\MedialibraryHelper\DefersUploads;
|
||||||
|
|
||||||
class Post extends Model implements HasMedia
|
class Post extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
|
use DefersUploads;
|
||||||
|
|
||||||
public $guarded = [];
|
public $guarded = [];
|
||||||
|
|
||||||
public function registerMediaCollections(): void
|
public function registerMediaCollections(): void
|
||||||
{
|
{
|
||||||
$this->addMediaCollection('defaultSingleFile')->maxWidth(fn () => 250)->singleFile();
|
$this->addMediaCollection('defaultSingleFile')->maxWidth(fn () => 250)->singleFile();
|
||||||
|
$this->addMediaCollection('singleJpegFile')->convert(fn ($extension) => 'jpg')->singleFile();
|
||||||
|
|
||||||
$this->addMediaCollection('conversionsWithDefault')
|
$this->addMediaCollection('conversionsWithDefault')
|
||||||
->singleFile()
|
->singleFile()
|
||||||
|
@ -56,7 +59,7 @@ class Post extends Model implements HasMedia
|
||||||
Event::dispatch(new MediaStored($media));
|
Event::dispatch(new MediaStored($media));
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->addMediaCollection('multipleProperties')->singleFile()->withDefaultProperties(fn ($path, $pathinfo) => [
|
$this->addMediaCollection('multipleProperties')->singleFile()->withDefaultProperties(fn ($path) => [
|
||||||
'test' => Str::camel($path),
|
'test' => Str::camel($path),
|
||||||
])->withPropertyValidation(fn ($path) => [
|
])->withPropertyValidation(fn ($path) => [
|
||||||
'test' => 'string|max:10',
|
'test' => 'string|max:10',
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Workbench\App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
|
|
||||||
|
class User extends Authenticatable
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
public $guarded = [];
|
||||||
|
|
||||||
|
public $casts = [
|
||||||
|
'policies' => 'json',
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Workbench\App\Policies;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
use Workbench\App\Models\User;
|
||||||
|
|
||||||
|
class PostPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
public function listMedia(User $user, HasMedia $model): bool
|
||||||
|
{
|
||||||
|
if (is_bool($user->policies['listMedia'])) {
|
||||||
|
return $user->policies['listMedia'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_get($user->policies, 'listMedia.id') === $model->id
|
||||||
|
&& data_get($user->policies, 'listMedia.collection') === $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeMedia(User $user, ?HasMedia $model, ?string $collection = null): bool
|
||||||
|
{
|
||||||
|
if (is_bool($user->policies['storeMedia'])) {
|
||||||
|
return $user->policies['storeMedia'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($model)) {
|
||||||
|
return data_get($user->policies, 'storeMedia.collection') === $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_get($user->policies, 'storeMedia.id') === $model->id
|
||||||
|
&& data_get($user->policies, 'storeMedia.collection') === $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMedia(User $user, HasMedia $model, string $collection): bool
|
||||||
|
{
|
||||||
|
if (is_bool($user->policies['updateMedia'])) {
|
||||||
|
return $user->policies['updateMedia'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_get($user->policies, 'updateMedia.id') === $model->id
|
||||||
|
&& data_get($user->policies, 'updateMedia.collection') === $collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroyMedia(User $user, HasMedia $model, string $collection): bool
|
||||||
|
{
|
||||||
|
if (is_bool($user->policies['destroyMedia'])) {
|
||||||
|
return $user->policies['destroyMedia'] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_get($user->policies, 'destroyMedia.id') === $model->id
|
||||||
|
&& data_get($user->policies, 'destroyMedia.collection') === $collection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories\Models;
|
||||||
|
|
||||||
|
use Workbench\App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<User>
|
||||||
|
*/
|
||||||
|
class UserFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = User::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => $this->faker->safeEmail,
|
||||||
|
'password' => Hash::make('password'),
|
||||||
|
'name' => $this->faker->firstName,
|
||||||
|
'policies' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function policies(array $policies): self
|
||||||
|
{
|
||||||
|
return $this->state(['policies' => [
|
||||||
|
'storeMedia' => true,
|
||||||
|
'updateMedia' => true,
|
||||||
|
'destroyMedia' => true,
|
||||||
|
'listMedia' => true,
|
||||||
|
...$policies,
|
||||||
|
]]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('users', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('email');
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('password');
|
||||||
|
$table->json('policies');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue