media security_type updates
This commit is contained in:
@@ -69,10 +69,12 @@ class MediaConductor extends Conductor
|
|||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
if ($user === null) {
|
if ($user === null) {
|
||||||
$builder->where('security_type', '');
|
$builder->where('security_type', '')
|
||||||
|
->orWhere('security_type', 'password');
|
||||||
} else {
|
} else {
|
||||||
$builder->where(function ($query) use ($user) {
|
$builder->where(function ($query) use ($user) {
|
||||||
$query->where('security_type', '')
|
$query->where('security_type', '')
|
||||||
|
->orWhere('security_type', 'password')
|
||||||
->orWhere(function ($subquery) use ($user) {
|
->orWhere(function ($subquery) use ($user) {
|
||||||
$subquery->where('security_type', 'permission')
|
$subquery->where('security_type', 'permission')
|
||||||
->whereIn('security_data', $user->permissions);
|
->whereIn('security_data', $user->permissions);
|
||||||
@@ -89,12 +91,14 @@ class MediaConductor extends Conductor
|
|||||||
*/
|
*/
|
||||||
public static function viewable(Model $model): bool
|
public static function viewable(Model $model): bool
|
||||||
{
|
{
|
||||||
if ($model->permission !== '') {
|
if (strcasecmp('permission', $model->security_type) === 0) {
|
||||||
/** @var \App\Models\User */
|
/** @var \App\Models\User */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
if ($user === null || $user->hasPermission($model->permission) === false) {
|
if ($user === null || $user->hasPermission($model->security_data) === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if($model->security_type !== '' && strcasecmp('password', $model->security_type) !== 0) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -170,6 +170,13 @@ class MediaController extends ApiController
|
|||||||
if($data['security']['type'] === '') {
|
if($data['security']['type'] === '') {
|
||||||
$data['security']['data'] = '';
|
$data['security']['data'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(strcasecmp($data['security']['type'], $medium->security_type) !== 0) {
|
||||||
|
if($request->has('storage') === false) {
|
||||||
|
$mime_type = $request->get('mime_type', $medium->mime_type);
|
||||||
|
$data['storage'] = Media::recommendedStorage($mime_type, $data['security']['type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(array_key_exists('storage', $data) === true &&
|
if(array_key_exists('storage', $data) === true &&
|
||||||
@@ -288,16 +295,16 @@ class MediaController extends ApiController
|
|||||||
* @param \App\Models\Media $medium Specified media.
|
* @param \App\Models\Media $medium Specified media.
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function download(Request $request, Media $medium)
|
public function download(Request $request, Media $media)
|
||||||
{
|
{
|
||||||
$headers = [];
|
$headers = [];
|
||||||
|
|
||||||
/* Check file exists */
|
/* Check file exists */
|
||||||
if(Storage::disk($medium->storage)->exists($medium->name) === true) {
|
if(Storage::disk($media->storage)->exists($media->name) === false) {
|
||||||
return $this->respondNotFound();
|
return $this->respondNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
$updated_at = Carbon::parse(Storage::disk($medium->storage)->lastModified($medium->name));
|
$updated_at = Carbon::parse(Storage::disk($media->storage)->lastModified($media->name));
|
||||||
|
|
||||||
$headerPragma = 'no-cache';
|
$headerPragma = 'no-cache';
|
||||||
$headerCacheControl = 'max-age=0, must-revalidate';
|
$headerCacheControl = 'max-age=0, must-revalidate';
|
||||||
@@ -316,21 +323,21 @@ class MediaController extends ApiController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($medium->security_type === '') {
|
if ($media->security_type === '') {
|
||||||
/* no security */
|
/* no security */
|
||||||
$headerPragma = 'public';
|
$headerPragma = 'public';
|
||||||
$headerExpires = $updated_at->addMonth()->toRfc2822String();
|
$headerExpires = $updated_at->addMonth()->toRfc2822String();
|
||||||
} else if (strcasecmp('password', $medium->security_type) === 0) {
|
} else if (strcasecmp('password', $media->security_type) === 0) {
|
||||||
/* password */
|
/* password */
|
||||||
if(
|
if(
|
||||||
($user === null || $user->hasPermission('admin/media') === false) &&
|
($user === null || $user->hasPermission('admin/media') === false) &&
|
||||||
($request->has('password') === false || $request->get('password') !== $medium->security_data)) {
|
($request->has('password') === false || $request->get('password') !== $media->security_data)) {
|
||||||
return $this->respondForbidden();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
} else if (strcasecmp('permission', $medium->security_type) === 0) {
|
} else if (strcasecmp('permission', $media->security_type) === 0) {
|
||||||
/* permission */
|
/* permission */
|
||||||
if(
|
if(
|
||||||
$user === null || ($user->hasPermission('admin/media') === false && $user->hasPermission($medium->security_data) === false)) {
|
$user === null || ($user->hasPermission('admin/media') === false && $user->hasPermission($media->security_data) === false)) {
|
||||||
return $this->respondForbidden();
|
return $this->respondForbidden();
|
||||||
}
|
}
|
||||||
}//end if
|
}//end if
|
||||||
@@ -341,7 +348,7 @@ class MediaController extends ApiController
|
|||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
'Cache-Control' => $headerCacheControl,
|
'Cache-Control' => $headerCacheControl,
|
||||||
'Content-Disposition' => sprintf('inline; filename="%s"', basename($medium->name)),
|
'Content-Disposition' => sprintf('inline; filename="%s"', basename($media->name)),
|
||||||
'Etag' => $headerEtag,
|
'Etag' => $headerEtag,
|
||||||
'Expires' => $headerExpires,
|
'Expires' => $headerExpires,
|
||||||
'Last-Modified' => $headerLastModified,
|
'Last-Modified' => $headerLastModified,
|
||||||
@@ -360,14 +367,15 @@ class MediaController extends ApiController
|
|||||||
return response()->make('', 304, $headers);
|
return response()->make('', 304, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
$headers['Content-Type'] = Storage::disk($medium->storage)->mimeType($medium->name);
|
$headers['Content-Type'] = Storage::disk($media->storage)->mimeType($media->name);
|
||||||
$headers['Content-Length'] = Storage::disk($medium->storage)->size($medium->name);
|
$headers['Content-Length'] = Storage::disk($media->storage)->size($media->name);
|
||||||
$headers['Content-Disposition'] = 'inline; filename="' . basename($medium->name) . '"';
|
$headers['Content-Disposition'] = 'attachment; filename="' . basename($media->name) . '"';
|
||||||
|
|
||||||
$stream = Storage::disk($medium->storage)->readStream($medium->name);
|
$stream = Storage::disk($media->storage)->readStream($media->name);
|
||||||
return response()->stream(
|
return response()->stream(
|
||||||
function () use ($stream) {
|
function() use($stream) {
|
||||||
fclose($stream);
|
while(ob_get_level() > 0) ob_end_flush();
|
||||||
|
fpassthru($stream);
|
||||||
},
|
},
|
||||||
200,
|
200,
|
||||||
$headers
|
$headers
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ class Media extends Model
|
|||||||
public function getUrlPath(): string
|
public function getUrlPath(): string
|
||||||
{
|
{
|
||||||
$url = config("filesystems.disks.$this->storage.url");
|
$url = config("filesystems.disks.$this->storage.url");
|
||||||
return "$url/";
|
return "$url";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -985,11 +985,28 @@ class Media extends Model
|
|||||||
return $this->hasMany(MediaJob::class, 'media_id');
|
return $this->hasMany(MediaJob::class, 'media_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function recommendedStorage(string $mime_type, string $security_type): string {
|
||||||
|
if($mime_type === '') {
|
||||||
|
return 'cdn';
|
||||||
|
}
|
||||||
|
|
||||||
|
if($security_type === '') {
|
||||||
|
if (strpos($mime_type, 'image/') === 0) {
|
||||||
|
return('local');
|
||||||
|
} else {
|
||||||
|
return('cdn');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return('private');
|
||||||
|
}
|
||||||
|
|
||||||
public static function verifyStorage($mime_type, $security_type, &$storage): int {
|
public static function verifyStorage($mime_type, $security_type, &$storage): int {
|
||||||
if($mime_type === '') {
|
if($mime_type === '') {
|
||||||
return Media::STORAGE_MIME_MISSING;
|
return Media::STORAGE_MIME_MISSING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::info('verify: ' . $storage);
|
||||||
if($storage === '') {
|
if($storage === '') {
|
||||||
if($security_type === '') {
|
if($security_type === '') {
|
||||||
if (strpos($mime_type, 'image/') === 0) {
|
if (strpos($mime_type, 'image/') === 0) {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ import { Media } from "../helpers/api.types";
|
|||||||
import { onMounted, ref, watch } from "vue";
|
import { onMounted, ref, watch } from "vue";
|
||||||
import { ImportMetaExtras } from "../../../import-meta";
|
import { ImportMetaExtras } from "../../../import-meta";
|
||||||
import { strCaseCmp } from "../helpers/string";
|
import { strCaseCmp } from "../helpers/string";
|
||||||
|
import { mediaGetWebURL } from "../helpers/media";
|
||||||
|
|
||||||
const emits = defineEmits(["update:modelValue"]);
|
const emits = defineEmits(["update:modelValue"]);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -157,24 +158,8 @@ const updateFileList = (newFileList: Array<Media>) => {
|
|||||||
fileList.value = [];
|
fileList.value = [];
|
||||||
|
|
||||||
for (const mediaItem of newFileList) {
|
for (const mediaItem of newFileList) {
|
||||||
const webUrl = (import.meta as ImportMetaExtras).env.APP_URL;
|
mediaItem.url = mediaGetWebURL(mediaItem);
|
||||||
const apiUrl = (import.meta as ImportMetaExtras).env.APP_URL_API;
|
if (mediaItem.url != "") {
|
||||||
|
|
||||||
// Is the URL a API request?
|
|
||||||
if (mediaItem.url.startsWith(apiUrl)) {
|
|
||||||
const fileUrlPath = mediaItem.url.substring(apiUrl.length);
|
|
||||||
const fileUrlParts = fileUrlPath.split("/");
|
|
||||||
|
|
||||||
if (
|
|
||||||
fileUrlParts.length === 4 &&
|
|
||||||
fileUrlParts[0].length === 0 &&
|
|
||||||
strCaseCmp("media", fileUrlParts[1]) === true &&
|
|
||||||
strCaseCmp("download", fileUrlParts[3]) === true
|
|
||||||
) {
|
|
||||||
mediaItem.url = webUrl + "/file/" + fileUrlParts[2];
|
|
||||||
fileList.value.push(mediaItem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileList.value.push(mediaItem);
|
fileList.value.push(mediaItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<SMForm :model-value="form" @submit="handleSubmit">
|
<SMForm :model-value="form" @submit="handleSubmit">
|
||||||
<SMFormCard :loading="dialogLoading">
|
<SMCard :loading="dialogLoading">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3>Change Password</h3>
|
<h3>Change Password</h3>
|
||||||
<p>Enter your new password below</p>
|
<p>Enter your new password below</p>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<button type="button" @click="handleClickCancel">Cancel</button>
|
<button type="button" @click="handleClickCancel">Cancel</button>
|
||||||
<input role="button" type="submit" value="Update" />
|
<input role="button" type="submit" value="Update" />
|
||||||
</template>
|
</template>
|
||||||
</SMFormCard>
|
</SMCard>
|
||||||
</SMForm>
|
</SMForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -31,11 +31,12 @@ import { useToastStore } from "../../store/ToastStore";
|
|||||||
import { useUserStore } from "../../store/UserStore";
|
import { useUserStore } from "../../store/UserStore";
|
||||||
import SMForm from "../SMForm.vue";
|
import SMForm from "../SMForm.vue";
|
||||||
import SMInput from "../SMInput.vue";
|
import SMInput from "../SMInput.vue";
|
||||||
|
import SMCard from "../SMCard.vue";
|
||||||
|
|
||||||
const form: FormObject = reactive(
|
const form: FormObject = reactive(
|
||||||
Form({
|
Form({
|
||||||
password: FormControl("", And([Required(), Password()])),
|
password: FormControl("", And([Required(), Password()])),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const applicationStore = useApplicationStore();
|
const applicationStore = useApplicationStore();
|
||||||
|
|||||||
@@ -145,7 +145,8 @@
|
|||||||
)}')`,
|
)}')`,
|
||||||
}">
|
}">
|
||||||
<div
|
<div
|
||||||
v-if="item.security_type != ''">
|
v-if="item.security_type != ''"
|
||||||
|
class="absolute right--1 top--1 h-4 w-4">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import { ImportMetaExtras } from "../../../import-meta";
|
||||||
import { Media, MediaJob } from "./api.types";
|
import { Media, MediaJob } from "./api.types";
|
||||||
import { toTitleCase } from "./string";
|
import { strCaseCmp, toTitleCase } from "./string";
|
||||||
|
|
||||||
export const mediaGetVariantUrl = (
|
export const mediaGetVariantUrl = (
|
||||||
media: Media,
|
media: Media,
|
||||||
@@ -25,6 +26,30 @@ export const mediaGetVariantUrl = (
|
|||||||
: media.url;
|
: media.url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mediaGetWebURL = (media: Media): string => {
|
||||||
|
const webUrl = (import.meta as ImportMetaExtras).env.APP_URL;
|
||||||
|
const apiUrl = (import.meta as ImportMetaExtras).env.APP_URL_API;
|
||||||
|
|
||||||
|
let url = media.url;
|
||||||
|
|
||||||
|
// Is the URL a API request?
|
||||||
|
if (media.url.startsWith(apiUrl)) {
|
||||||
|
const fileUrlPath = media.url.substring(apiUrl.length);
|
||||||
|
const fileUrlParts = fileUrlPath.split("/");
|
||||||
|
|
||||||
|
if (
|
||||||
|
fileUrlParts.length >= 4 &&
|
||||||
|
fileUrlParts[0].length === 0 &&
|
||||||
|
strCaseCmp("media", fileUrlParts[1]) === true &&
|
||||||
|
strCaseCmp("download", fileUrlParts[3]) === true
|
||||||
|
) {
|
||||||
|
url = webUrl + "/file/" + fileUrlParts[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a mime matches.
|
* Check if a mime matches.
|
||||||
* @param {string} mimeExpected The mime expected.
|
* @param {string} mimeExpected The mime expected.
|
||||||
|
|||||||
@@ -121,5 +121,9 @@ export const toPrice = (numOrString: number | string): string => {
|
|||||||
* @returns {boolean} If the strings match.
|
* @returns {boolean} If the strings match.
|
||||||
*/
|
*/
|
||||||
export const strCaseCmp = (string1: string, string2: string): boolean => {
|
export const strCaseCmp = (string1: string, string2: string): boolean => {
|
||||||
return string1.toLowerCase() === string2.toLowerCase();
|
if (string1 !== undefined && string2 !== undefined) {
|
||||||
|
return string1.toLowerCase() === string2.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,3 +126,22 @@ export const extractFileNameFromUrl = (url: string): string => {
|
|||||||
const fileName = matches[1];
|
const fileName = matches[1];
|
||||||
return fileName;
|
return fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addQueryParam = (
|
||||||
|
url: string,
|
||||||
|
name: string,
|
||||||
|
value: string,
|
||||||
|
): string => {
|
||||||
|
const urlObject = new URL(url);
|
||||||
|
const queryParams = new URLSearchParams(urlObject.search);
|
||||||
|
|
||||||
|
if (queryParams.has(name)) {
|
||||||
|
queryParams.set(name, value);
|
||||||
|
} else {
|
||||||
|
// Add the new query parameter
|
||||||
|
queryParams.append(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
urlObject.search = queryParams.toString();
|
||||||
|
return urlObject.toString();
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,25 +4,49 @@
|
|||||||
:status="pageStatus" />
|
:status="pageStatus" />
|
||||||
<SMLoading v-else-if="pageLoading == true"></SMLoading>
|
<SMLoading v-else-if="pageLoading == true"></SMLoading>
|
||||||
<SMForm
|
<SMForm
|
||||||
v-else-if="showPasswordForm == true"
|
v-else-if="showForm == 'password'"
|
||||||
:model-value="form"
|
:model-value="form"
|
||||||
@submit="handleSubmit">
|
@submit="handleSubmit">
|
||||||
<SMFormCard>
|
<div
|
||||||
<template #header>
|
class="max-w-2xl mx-auto border-1 bg-white rounded-xl mt-7xl text-gray-5 px-12 py-8">
|
||||||
<h3>Password Required</h3>
|
<h3 class="mb-4">Password Required</h3>
|
||||||
<p>This file requires a password before it can be viewed</p>
|
<p class="mb-2">
|
||||||
</template>
|
This file requires a password before it can be viewed
|
||||||
<template #body>
|
</p>
|
||||||
<SMInput
|
<SMInput
|
||||||
control="password"
|
class="mb-4"
|
||||||
type="password"
|
control="password"
|
||||||
label="File Password"
|
type="password"
|
||||||
autofocus />
|
label="File Password"
|
||||||
</template>
|
autofocus />
|
||||||
<template #footer-space-between>
|
<div class="flex flex-justify-end">
|
||||||
<input role="button" type="submit" value="OK" />
|
<input
|
||||||
</template>
|
type="submit"
|
||||||
</SMFormCard>
|
class="font-medium px-6 py-3.1 rounded-2 hover:shadow-md text-lg transition bg-sky-600 hover:bg-sky-500 text-white cursor-pointer"
|
||||||
|
value="Submit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SMForm>
|
||||||
|
<SMForm
|
||||||
|
v-else-if="showForm == 'complete'"
|
||||||
|
:model-value="form"
|
||||||
|
@submit="handleSubmit">
|
||||||
|
<div
|
||||||
|
class="max-w-2xl mx-auto border-1 bg-white rounded-xl mt-7xl text-gray-5 px-12 py-8">
|
||||||
|
<h3 class="mb-4">Download Complete</h3>
|
||||||
|
<p class="mb-2">
|
||||||
|
If you have permission to view this document, your download
|
||||||
|
should now begin.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-justify-end">
|
||||||
|
<button
|
||||||
|
role="button"
|
||||||
|
class="font-medium px-6 py-3.1 rounded-2 hover:shadow-md text-lg transition bg-sky-600 hover:bg-sky-500 text-white cursor-pointer"
|
||||||
|
@click="handleClose()">
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</SMForm>
|
</SMForm>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,8 +54,11 @@
|
|||||||
import { reactive, ref } from "vue";
|
import { reactive, ref } from "vue";
|
||||||
import { api } from "../helpers/api";
|
import { api } from "../helpers/api";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { Media } from "../helpers/api.types";
|
import { Media, MediaResponse } from "../helpers/api.types";
|
||||||
|
import SMForm from "../components/SMForm.vue";
|
||||||
|
import SMInput from "../components/SMInput.vue";
|
||||||
import SMLoading from "../components/SMLoading.vue";
|
import SMLoading from "../components/SMLoading.vue";
|
||||||
|
import SMPageStatus from "../components/SMPageStatus.vue";
|
||||||
import { strCaseCmp } from "../helpers/string";
|
import { strCaseCmp } from "../helpers/string";
|
||||||
import { useUserStore } from "../store/UserStore";
|
import { useUserStore } from "../store/UserStore";
|
||||||
import { Form, FormControl, FormObject } from "../helpers/form";
|
import { Form, FormControl, FormObject } from "../helpers/form";
|
||||||
@@ -39,7 +66,7 @@ import { Required } from "../helpers/validate";
|
|||||||
|
|
||||||
const pageStatus = ref(200);
|
const pageStatus = ref(200);
|
||||||
const pageLoading = ref(true);
|
const pageLoading = ref(true);
|
||||||
const showPasswordForm = ref(false);
|
const showForm = ref("");
|
||||||
const fileUrl = ref("");
|
const fileUrl = ref("");
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
@@ -71,6 +98,9 @@ const downloadFile = (params = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
|
window.setTimeout(() => {
|
||||||
|
showForm.value = "complete";
|
||||||
|
}, 1500);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -84,6 +114,10 @@ const handleSubmit = () => {
|
|||||||
downloadFile(params);
|
downloadFile(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
window.close();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle page loading
|
* Handle page loading
|
||||||
*/
|
*/
|
||||||
@@ -96,34 +130,45 @@ const handleLoad = async () => {
|
|||||||
id: route.params.id,
|
id: route.params.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = await api.get({
|
try {
|
||||||
url: "/media/:id",
|
let result = await api.get({
|
||||||
params: params,
|
url: "/media/{id}",
|
||||||
});
|
params: params,
|
||||||
|
});
|
||||||
|
|
||||||
if (result.status === 200) {
|
if (result.status === 200) {
|
||||||
const medium = result.data as Media;
|
const data = result.data as MediaResponse;
|
||||||
fileUrl.value = medium.url;
|
const medium = data.medium as Media;
|
||||||
|
fileUrl.value = medium.url;
|
||||||
|
|
||||||
if (medium.security_type === "") {
|
if (medium.security_type === "") {
|
||||||
downloadFile();
|
downloadFile();
|
||||||
} else if (
|
} else if (
|
||||||
strCaseCmp("permission", medium.security_type) === true &&
|
strCaseCmp("permission", medium.security_type) === true &&
|
||||||
userStore.id
|
userStore.id
|
||||||
) {
|
) {
|
||||||
const params = {
|
const params = {
|
||||||
token: userStore.token,
|
token: userStore.token,
|
||||||
};
|
};
|
||||||
|
|
||||||
downloadFile(params);
|
downloadFile(params);
|
||||||
} else if (strCaseCmp("password", medium.security_type) === true) {
|
} else if (
|
||||||
showPasswordForm.value = true;
|
strCaseCmp("password", medium.security_type) === true
|
||||||
|
) {
|
||||||
|
showForm.value = "password";
|
||||||
|
} else {
|
||||||
|
/* unknown security type */
|
||||||
|
pageStatus.value = 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageLoading.value = false;
|
||||||
} else {
|
} else {
|
||||||
/* unknown security type */
|
pageStatus.value = result.status;
|
||||||
pageStatus.value = 403;
|
pageLoading.value = false;
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
pageStatus.value = result.status;
|
pageStatus.value = error.status;
|
||||||
|
pageLoading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,12 +74,24 @@
|
|||||||
<template #item-title="item">
|
<template #item-title="item">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<div
|
<div
|
||||||
class="w-100 h-100 max-h-15 max-w-20 mr-2 bg-contain bg-no-repeat bg-center"
|
class="w-100 h-100 max-h-15 max-w-20 mr-2 bg-contain bg-no-repeat bg-center relative"
|
||||||
:style="{
|
:style="{
|
||||||
backgroundImage: `url('${mediaGetThumbnail(
|
backgroundImage: `url('${mediaGetThumbnail(
|
||||||
item,
|
item,
|
||||||
)}')`,
|
)}')`,
|
||||||
}"></div>
|
}">
|
||||||
|
<div
|
||||||
|
v-if="item.security_type != ''"
|
||||||
|
class="absolute right--1 top--1 h-4 w-4">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<title>locked</title>
|
||||||
|
<path
|
||||||
|
d="M12,17A2,2 0 0,0 14,15C14,13.89 13.1,13 12,13A2,2 0 0,0 10,15A2,2 0 0,0 12,17M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6A2,2 0 0,1 4,20V10C4,8.89 4.9,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="flex flex-col flex-justify-center">
|
<div class="flex flex-col flex-justify-center">
|
||||||
<span>{{ item.title }}</span>
|
<span>{{ item.title }}</span>
|
||||||
<span class="small">({{ item.name }})</span>
|
<span class="small">({{ item.name }})</span>
|
||||||
@@ -101,11 +113,11 @@
|
|||||||
fill="currentColor" />
|
fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<a
|
||||||
type="button"
|
:href="mediaGetWebURL(item)"
|
||||||
class="bg-transparent cursor-pointer hover:text-sky-5"
|
class="bg-transparent cursor-pointer hover:text-sky-5"
|
||||||
title="Download"
|
title="Download"
|
||||||
@click="handleDownload(item)">
|
target="_blank">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 -960 960 960"
|
viewBox="0 -960 960 960"
|
||||||
@@ -114,7 +126,7 @@
|
|||||||
d="M220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Zm260-153L287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193Z"
|
d="M220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Zm260-153L287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193Z"
|
||||||
fill="currrentColor" />
|
fill="currrentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</a>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="bg-transparent cursor-pointer hover:text-red-7"
|
class="bg-transparent cursor-pointer hover:text-red-7"
|
||||||
@@ -170,11 +182,11 @@ import SMMastHead from "../../components/SMMastHead.vue";
|
|||||||
import SMTable from "../../components/SMTable.vue";
|
import SMTable from "../../components/SMTable.vue";
|
||||||
import SMPagination from "../../components/SMPagination.vue";
|
import SMPagination from "../../components/SMPagination.vue";
|
||||||
import SMLoading from "../../components/SMLoading.vue";
|
import SMLoading from "../../components/SMLoading.vue";
|
||||||
import { updateRouterParams } from "../../helpers/url";
|
import { addQueryParam, updateRouterParams } from "../../helpers/url";
|
||||||
import { userHasPermission } from "../../helpers/utils";
|
import { userHasPermission } from "../../helpers/utils";
|
||||||
import SMPageStatus from "../../components/SMPageStatus.vue";
|
import SMPageStatus from "../../components/SMPageStatus.vue";
|
||||||
import SMCheckbox from "../../components/SMCheckbox.vue";
|
import SMCheckbox from "../../components/SMCheckbox.vue";
|
||||||
import { mediaGetThumbnail } from "../../helpers/media";
|
import { mediaGetThumbnail, mediaGetWebURL } from "../../helpers/media";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -448,7 +460,9 @@ const handleEditSelected = async () => {
|
|||||||
* @param {Media} item The media item.
|
* @param {Media} item The media item.
|
||||||
*/
|
*/
|
||||||
const handleDownload = (item: Media) => {
|
const handleDownload = (item: Media) => {
|
||||||
window.open(`${item.url}?download=1`, "_blank");
|
// window.open(`${item.url}?download=1`, "_blank");
|
||||||
|
// window.open(addQueryParam(mediaGetWebURL(item), "download", "1"), "_blank");
|
||||||
|
window.open(mediaGetWebURL(item), "_blank");
|
||||||
};
|
};
|
||||||
|
|
||||||
const computedSelectedCount = computed(() => {
|
const computedSelectedCount = computed(() => {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Route::get('/users/{user}/events', [UserController::class, 'eventList']);
|
|||||||
Route::get('media/jobs', [MediaJobController::class, 'index']);
|
Route::get('media/jobs', [MediaJobController::class, 'index']);
|
||||||
Route::get('media/jobs/{mediaJob}', [MediaJobController::class, 'show']);
|
Route::get('media/jobs/{mediaJob}', [MediaJobController::class, 'show']);
|
||||||
Route::apiResource('media', MediaController::class);
|
Route::apiResource('media', MediaController::class);
|
||||||
Route::get('media/{medium}/download', [MediaController::class, 'download']);
|
Route::get('media/{media}/download', [MediaController::class, 'download']);
|
||||||
|
|
||||||
Route::apiResource('articles', ArticleController::class);
|
Route::apiResource('articles', ArticleController::class);
|
||||||
// Route::apiAddendumResource('attachments', 'articles', ArticleController::class);
|
// Route::apiAddendumResource('attachments', 'articles', ArticleController::class);
|
||||||
|
|||||||
Reference in New Issue
Block a user