Skip to content
4 changes: 4 additions & 0 deletions resources/js/bootstrap/cp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import AssetIndexes from '@/components/utilities/AssetIndexes/AssetIndexes.vue';
import SystemMessages from '@/components/utilities/SystemMessages/SystemMessages.vue';
import DeprecationErrorsToolbar from '@/components/utilities/DeprecationErrors/DeprecationErrorsToolbar.vue';
import {setTranslations} from '@craftcms/cp/utilities/translate.ts.mjs';
import LocalFsSettings from '@/components/Filesystems/LocalFsSettings.vue';

let bootedCallbacks: Array<(instance: any) => void> = [];
let bootingCallbacks: Array<(instance: any) => void> = [];
Expand Down Expand Up @@ -98,6 +99,9 @@ const Cp = {
app.component('ProjectConfig', ProjectConfig);
app.component('AssetIndexes', AssetIndexes);
app.component('SystemMessages', SystemMessages);
app.component('LocalFsSettings', LocalFsSettings);

// @TODO Register plugin components
},
});

Expand Down
73 changes: 73 additions & 0 deletions resources/js/components/Filesystems/FilesystemSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script setup lang="ts">
import {t} from '@craftcms/cp';
import {computed} from 'vue';
import {usePage} from '@inertiajs/vue3';
import type {Filesystem} from '@/pages/SettingsFilesystemsEditPage.vue';
import VarDump from '@/components/VarDump.vue';
import CraftSwitch from '@craftcms/cp/vue/CraftSwitch.vue';
import CraftCombobox from '@/components/form/CraftCombobox.vue';
import DynamicHtmlRenderer from '@/components/DynamicHtmlRenderer.vue';

const hasUrls = defineModel<boolean>('hasUrls');
const url = defineModel<string>('url');
withDefaults(
defineProps<{
filesystem?: Filesystem;
}>(),
{
filesystem: {
errors: () => [],
name: null,
handle: null,
oldHandle: null,
hasUrls: false,
url: null,
uid: null,
rootUrl: null,
id: null,
dateCreated: null,
dateUpdated: null,
settingsHtml: null,
rootPath: null,
path: null,
},
}
);

const page = usePage<{
readOnly: boolean;
baseUrlSuggestions?: Array<any>;
}>();
</script>

<template>
<div v-if="filesystem" :id="filesystem.type">
<template v-if="filesystem.showHasUrlSetting">
<CraftSwitch
:label="t('Files in this filesystem have public URLs')"
name="hasUrls"
id="has-urls"
v-model="hasUrls"
:disabled="page.props.readOnly"
/>

<template v-if="hasUrls && filesystem.showUrlSetting">
<CraftCombobox
:label="t('Base URL')"
:help-text="t('The base URL to the files in this filesystem.')"
v-model="url"
:options="page.props.baseUrlSuggestions"
name="url"
:required="true"
placeholder="//example.com/path/to/folder"
data-error-key="url"
Comment thread
riasvdv marked this conversation as resolved.
Outdated
:disabled="page.props.readOnly"
></CraftCombobox>
</template>
</template>
<DynamicHtmlRenderer :html="filesystem.settingsHtml"/>
<!--<div v-html="filesystem.settingsHtml" />-->
</div>
</template>

<style scoped lang="scss"></style>
18 changes: 18 additions & 0 deletions resources/js/components/Filesystems/LocalFsSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script setup lang="ts">
import {computed} from 'vue';
import VarDump from '@/components/VarDump.vue';

const props = defineProps<{
filesystem?: string;
}>();

const fs = computed(() => props.filesystem ? JSON.parse(props.filesystem) : null);
</script>

<template>
<h2>Roundabout LocalFsSettings</h2>

<VarDump :data="{props}" />
</template>

<style scoped lang="scss"></style>
130 changes: 130 additions & 0 deletions resources/js/pages/SettingsFilesystemsEditPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script setup lang="ts">
Comment thread
riasvdv marked this conversation as resolved.
Outdated
import {t, toHandle} from '@craftcms/cp';
import AppLayout from '@/layout/AppLayout.vue';
import VarDump from '@/components/VarDump.vue';
import type {SelectItem, SelectOption} from '@/types';
import Pane from '@/components/Pane.vue';
import CraftInput from '@craftcms/cp/vue/CraftInput.vue';
import CraftInputHandle from '@craftcms/cp/vue/CraftInputHandle.vue';
import Select from '@/components/form/Select.vue';
import {useForm} from '@inertiajs/vue3';
import {useInputGenerator} from '@/composables/useInputGenerator';
import DynamicHtmlRenderer from '@/components/DynamicHtmlRenderer.vue';
import FilesystemSettings from '@/components/Filesystems/FilesystemSettings.vue';

export type Filesystem = {
errors: any[];
name: string | null;
handle: string | null;
oldHandle: any;
hasUrls: boolean;
url: any;
uid: any;
rootUrl: any;
id: any;
dateCreated: any;
dateUpdated: any;
settingsHtml: string;
rootPath: string;
path: any;
type: string;
showHasUrlSetting: boolean;
showUrlSetting: boolean;
};

const props = defineProps<{
oldHandle: string | null;
filesystem: Filesystem;
fsOptions: Array<SelectOption>;
fsInstances: Record<string, Filesystem>;
fsTypes: Array<string>;
readOnly: boolean;
}>();

const form = useForm({
name: props.filesystem.name ?? '',
handle: props.filesystem.handle ?? '',
type: props.filesystem.type ?? '',
hasUrls: props.filesystem.hasUrls ?? false,
url: props.filesystem.url ?? '',
});

useInputGenerator(
() => form.name,
(v) => (form.handle = toHandle(v))
);

function handleSubmit() {
const form = event.target as HTMLFormElement;

form.transform(() => {
return {
...(new FormData(form)),
...form
}
}).submit();
}

function getFs(fsType: string): Filesystem | null {
if (fsType === form.type) {
return props.filesystem;
}

return props.fsInstances[fsType] ?? null;
}
</script>

<template>
<AppLayout>
<Pane appearance="raised">
<div class="grid gap-3">
Comment thread
riasvdv marked this conversation as resolved.
Outdated
<CraftInput
v-model="form.name"
:label="t('Name')"
id="name"
name="name"
:autofocus="true"
:required="true"
:error="form.errors?.name"
data-error-key="name"
:disabled="readOnly"
/>

<CraftInputHandle
:label="t('Handle')"
id="handle"
name="handle"
v-model="form.handle"
:required="true"
:error="form.errors?.handle"
data-error-key="handle"
:disabled="readOnly"
/>

<hr />

<template v-if="fsOptions.length">
<Select
id="type"
name="type"
:label="t('Filesystem Type')"
:help-text="t('What type of filesystem is this?')"
:options="fsOptions"
v-model="form.type"
:disabled="readOnly"
/>
</template>

<template v-for="fsType in fsTypes">
<FilesystemSettings
v-model:has-urls="form.hasUrls"
v-model:url="form.url"
:filesystem="fsInstances[fsType]"
/>
</template>
</div>
</Pane>
</AppLayout>
</template>

<style scoped lang="scss"></style>
32 changes: 19 additions & 13 deletions resources/templates/_components/fs/Local/settings.twig
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
{% from "_includes/forms" import autosuggestField %}

<craft-input
label="{{ "Base Path"|t('app') }}"
:help-text="{{ "The base folder path that should be used as the root of the filesystem."|t('app') }}"
name="path"
></craft-input>

{{ autosuggestField({
label: "Base Path"|t('app'),
instructions: "The base folder path that should be used as the root of the filesystem."|t('app'),
id: 'path',
class: 'ltr',
name: 'path',
suggestEnvVars: true,
suggestAliases: true,
value: filesystem.path,
required: true,
placeholder: "/path/to/folder"|t('app'),
errors: filesystem.errors.get('path'),
data: {'error-key': 'path'},
disabled: readOnly,
label: "Base Path"|t('app'),
instructions: "The base folder path that should be used as the root of the filesystem."|t('app'),
id: 'path',
class: 'ltr',
name: 'path',
suggestEnvVars: true,
suggestAliases: true,
value: filesystem.path,
required: true,
placeholder: "/path/to/folder"|t('app'),
errors: filesystem.errors.get('path'),
data: {'error-key': 'path'},
disabled: readOnly,
}) }}
14 changes: 11 additions & 3 deletions src/Filesystem/Filesystems/Local.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use CraftCms\Cms\Support\Env;
use CraftCms\Cms\Support\Facades\Security;
use CraftCms\Cms\Support\File;
use CraftCms\Cms\Support\Html;
use CraftCms\Cms\View\TemplateMode;
use Override;

Expand Down Expand Up @@ -124,10 +125,17 @@ public function getReadOnlySettingsHtml(): ?string

private function settingsHtml(bool $readOnly): string
{
return template('_components/fs/Local/settings', [
'filesystem' => $this,
return Html::tag('LocalFsSettings', attributes: [
// ':filesystem' => json_encode([
// 'name' => $this->name,
// 'handle' => $this->handle,
// ]),
'readOnly' => $readOnly,
], TemplateMode::Cp);
]);
// return template('_components/fs/Local/settings', [
// 'filesystem' => $this,
// 'readOnly' => $readOnly,
// ], TemplateMode::Cp);
}

#[Override]
Expand Down
26 changes: 23 additions & 3 deletions src/Http/Controllers/Settings/FilesystemsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

use CraftCms\Cms\Config\GeneralConfig;
use CraftCms\Cms\Cp\Html\ContentHtml;
use CraftCms\Cms\Cp\SelectOptions;
use CraftCms\Cms\Filesystem\Contracts\FsInterface;
use CraftCms\Cms\Filesystem\Filesystems;
use CraftCms\Cms\Filesystem\Resources\FsResource;
use CraftCms\Cms\Http\RespondsWithFlash;
use CraftCms\Cms\Http\Responses\CpScreenResponse;
use CraftCms\Cms\Support\Arr;
use CraftCms\Cms\Support\Html;
use CraftCms\Cms\Support\Str;
use CraftCms\Cms\Support\Url;
use Illuminate\Http\Request;
use Inertia\Inertia;
Expand Down Expand Up @@ -93,18 +95,36 @@
$title = t('Create a new filesystem');
}

$isValidUrl = fn ($value) => Str::isUrl($value);

return new CpScreenResponse()
->title($title)
->addCrumb(t('Settings'), 'settings')
->addCrumb(t('Filesystems'), 'settings/filesystems')
->contentTemplate('settings/filesystems/_edit.twig', [
->inertiaPage('SettingsFilesystemsEditPage', [
'oldHandle' => $handle,
'filesystem' => $filesystem,
'filesystem' => [
...$filesystem,

Check failure on line 107 in src/Http/Controllers/Settings/FilesystemsController.php

View workflow job for this annotation

GitHub Actions / Code Quality / Phpstan

Only iterables can be unpacked, CraftCms\Cms\Filesystem\Contracts\FsInterface given.
'type' => $filesystem::class,
'settingsHtml' => $this->readOnly ? $filesystem->getReadOnlySettingsHtml() : $filesystem->getSettingsHtml(),
'showHasUrlSetting' => $filesystem->getShowHasUrlSetting(),
'showUrlSetting' => $filesystem->getShowUrlSetting(),
],
'fsOptions' => $fsOptions,
'fsInstances' => $fsInstances,
'fsTypes' => $allFsTypes,
'readOnly' => $this->readOnly,

// @TODO this should probably be its own item on SelectOptions
'baseUrlSuggestions' => SelectOptions::getEnvSuggestions(true, $isValidUrl),
])
// ->contentTemplate('settings/filesystems/_edit.twig', [
// 'oldHandle' => $handle,
// 'filesystem' => $filesystem,
// 'fsOptions' => $fsOptions,
// 'fsInstances' => $fsInstances,
// 'fsTypes' => $allFsTypes,
// 'readOnly' => $this->readOnly,
// ])
->unless(
$this->readOnly,
function (CpScreenResponse $response) {
Expand Down
Loading