backend urls

This commit is contained in:
2022-03-04 13:06:50 +01:00
parent f2f1efe314
commit 36f1e0e6e4
13 changed files with 241 additions and 80 deletions
@@ -16,6 +16,9 @@ public abstract class ZeroApiController : ControllerBase
IZeroMapper _mapper;
protected IZeroMapper Mapper => _mapper ?? (_mapper = HttpContext?.RequestServices?.GetService<IZeroMapper>());
IZeroContext _context;
protected IZeroContext ZeroContext => _context ?? (_context = HttpContext?.RequestServices?.GetService<IZeroContext>());
public ApiRequestHints Hints { get; protected set; } = new();
}
@@ -64,6 +64,21 @@ public class PagesController : ZeroApiTreeEntityStoreController<Page, IPagesStor
return GetModelsByIndex<zero_Api_Pages_Listing>(query);
}
[HttpGet("{id}/urls")]
[ZeroAuthorize(PagePermissions.Read)]
public async Task<ActionResult<UrlsResult>> GetUrls(string id)
{
string url = await Routes.GetUrl<Page>(id);
return new UrlsResult(ZeroContext.Application.Domains.FirstOrDefault(), url);
}
[HttpGet("{id}/urls/preview")]
[ZeroAuthorize(PagePermissions.Read)]
public async Task<ActionResult<PreviewUrlResult>> GetPreviewUrl(string id)
{
return new PreviewUrlResult(await Routes.GetUrl<Page>(id));
}
[HttpPost("")]
[ZeroAuthorize(PagePermissions.Create)]
public virtual Task<ActionResult<Result>> Create(Page saveModel) => CreateModel(saveModel);
+3 -3
View File
@@ -240,12 +240,12 @@ export function compileEditor(zero: Zero, editor: ZeroEditor): ZeroCompiledEdito
name: editorTab.name,
sort: editorTab.sort,
alias: editorTab.alias,
class: null,
class: editorTab.class,
count: editorTab.count,
disabled: editorTab.disabled,
hidden: editorTab.hidden,
fieldsets: [],
component: null
component: editorTab.component
} as ZeroCompiledEditorTab;
let fieldsets = [];
@@ -316,7 +316,7 @@ export function compileEditor(zero: Zero, editor: ZeroEditor): ZeroCompiledEdito
});
if (tab.fieldsets.length)
if (tab.fieldsets.length || tab.component != null)
{
model.tabs.push(tab);
}
@@ -1,6 +1,7 @@
import { ZeroEditorField } from "zero/schemas";
import { ZeroEditorFieldImpl, ZeroEditorFieldConfiguration, createFieldProxy } from "./editor-field";
import { arrayRemove } from '../utils';
import { Component } from "vue";
export class ZeroEditorCanvasBase
@@ -86,6 +87,8 @@ export class ZeroEditorTab extends ZeroEditorCanvasBaseWithFieldset
count: (model: any) => number | undefined = () => undefined;
hidden: (model: any) => boolean = () => false;
disabled: (model: any) => boolean = () => false;
class: string | null = null;
component: Component | null = null;
constructor(alias: string, name: string)
{
+1 -1
View File
@@ -5,7 +5,7 @@ export default function (app: App)
{
app.component('ui-editor', defineAsyncComponent(() => import('./ui-editor.vue')));
app.component('ui-editor-component', defineAsyncComponent(() => import('./ui-editor-component.vue')));
app.component('ui-editor-infos', defineAsyncComponent(() => import('./ui-editor-infos.vue')));
app.component('ui-editor-meta', defineAsyncComponent(() => import('./ui-editor-meta.vue')));
app.component('ui-editor-header', defineAsyncComponent(() => import('./ui-editor-header.vue')));
app.component('ui-editor-page', defineAsyncComponent(() => import('./ui-editor-page.vue')));
};
@@ -1,49 +0,0 @@
<template>
<div v-if="visible" class="editor-infos">
<div class="ui-box is-light editor-infos-aside">
<slot name="before"></slot>
<template v-if="modelValue && modelValue.id">
<ui-property v-if="modelValue.lastModifiedDate" field="lastModifiedDate" label="@ui.modifiedDate">
<ui-date v-model="modelValue.lastModifiedDate" />
</ui-property>
<ui-property label="@ui.createdDate" field="createdDate">
<ui-date v-model="modelValue.createdDate" />
</ui-property>
<ui-property label="@ui.entityfields.alias" field="alias">
{{modelValue.alias}}
</ui-property>
<ui-property label="@ui.entityfields.sort" field="sort">
{{modelValue.sort}}
</ui-property>
<ui-property v-if="modelValue.key" label="@ui.entityfields.key" field="key">
{{modelValue.key}}
</ui-property>
</template>
<slot name="after"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'uiEditorInfos',
props: {
modelValue: {
type: [Object, Array]
},
disabled: {
type: Boolean,
default: false
}
},
computed: {
visible()
{
return (this.modelValue && this.modelValue.id) || this.$slots.hasOwnProperty('before') || this.$slots.hasOwnProperty('after');
}
}
}
</script>
@@ -0,0 +1,130 @@
<template>
<div class="editor-meta">
<div class="ui-box">
<ui-property v-if="!isCreate && urls.length" label="@page.infotab.links" :is-text="true" :vertical="false">
<a v-for="url in urls" class="ui-linktext" :href="url" target="_blank"><ui-icon symbol="fth-external-link"></ui-icon> {{url}}</a>
</ui-property>
<!--<ui-property label="@page.schedule.label" :is-text="true" :vertical="false">
<ui-daterangepicker :value="{ from: value.publishDate, to: value.unpublishDate }" @input="onRangeChange" :class="{ 'is-primary': value.publishDate || value.unpublishDate }" :disabled="disabled" />
</ui-property>-->
<ui-property label="@page.type" :is-text="true" v-if="pageType" :vertical="false">
<ui-icon :symbol="pageType.icon" style="margin-right:6px;"></ui-icon>{{pageType.name}}
</ui-property>
<ui-property v-if="!isCreate" label="@ui.id" :is-text="true" :vertical="false">
{{value.id}}
</ui-property>
<ui-property v-if="!isCreate" label="@ui.createdDate" :is-text="true" :vertical="false">
<ui-date v-model="value.createdDate" :split="true" format="long" />
</ui-property>
<ui-property v-if="!isCreate" label="@ui.modifiedDate" :is-text="true" :vertical="false">
<ui-date v-model="value.lastModifiedDate" :split="true" format="long" />
</ui-property>
<ui-property v-if="!isCreate" label="@ui.entityfields.alias" :is-text="true" :vertical="false">
{{value.alias}}
</ui-property>
</div>
</div>
</template>
<script>
import api from '../api';
export default {
props: {
value: {
type: [ Object, Array ]
},
disabled: {
type: Boolean,
default: false
}
},
data: () => ({
pageType: null,
urls: [],
urlDomain: null
}),
computed: {
isCreate()
{
return !this.value.id;
}
},
watch: {
'value.lastModifiedDate'()
{
this.reloadUrls();
}
},
mounted()
{
//api.getPageType(this.value.pageTypeAlias).then(pageType =>
//{
// this.pageType = pageType;
//});
this.reloadUrls();
},
methods: {
async reloadUrls()
{
if (this.value.id)
{
const result = await api.getUrls(this.value.id);
this.urls = result.data.urls;
this.urlDomain = result.data.domain;
}
},
getRevisions(page)
{
//return api.getRevisions(this.value.id, page);
},
onRangeChange(value)
{
this.value.publishDate = value.from;
this.value.unpublishDate = value.to;
},
}
}
</script>
<style lang="scss">
.editor-meta
{
padding: 0 !important;
}
.editor-meta .ui-view-box-aside
{
padding: 0;
}
.editor-meta .ui-box
{
margin: 0;
}
.editor-meta .ui-box + .ui-box
{
margin-top: var(--padding-s);
}
.editor-meta .ui-property + .ui-property
{
/*border-top: none;
margin-top: 0;
padding-top: var(--padding-s);*/
}
.editor-meta .ui-box:last-child
{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
</style>
+2 -1
View File
@@ -102,7 +102,8 @@
border-top-left-radius: 0;
border-top-right-radius: 0;
display: flex;
gap: var(--padding-l);
flex-direction: column;
gap: var(--padding-xs);
}
.editor-infos .ui-property
+2 -2
View File
@@ -34,10 +34,10 @@
</ui-tab>
</ui-tabs>
<aside class="editor-aside" v-if="asideDefined">
<slot name="aside"></slot>
<slot name="aside" v-bind="{ value }"></slot>
</aside>
<aside class="editor-below" v-if="belowDefined">
<slot name="below"></slot>
<slot name="below" v-bind="{ value }"></slot>
</aside>
</div>
</div>
@@ -20,6 +20,9 @@ export default {
getPreviews: (ids: string[], config?: ApiRequestConfig) => get(`backoffice/pages/previews`, { ...config, params: { ids } }),
getUrls: (id: string, config?: ApiRequestConfig) => get(`pages/${id}/urls`, { ...config }),
getPreviewUrl: (id: string, config?: ApiRequestConfig) => get(`pages/${id}/urls/preview`, { ...config }),
create: (model: any, config?: ApiRequestConfig) => post('pages', model, config),
+24 -8
View File
@@ -2,19 +2,18 @@
<ui-form :key="id" ref="form" class="page page-editor" v-slot="form" @submit="onSubmit" @load="onLoad" :route="route">
<ui-form-header v-model:value="model" title="@page.name" :disabled="disabled" :is-create="!id" :state="form.state" :active-toggle="!isFolder" :can-delete="meta.canDelete" @delete="onDelete" :sticky="true">
<template v-slot:actions>
<!--<ui-dropdown-button v-if="!isFolder" label="@page.preview.title" icon="fth-eye" :disabled="disabled" @click="openPreview" />
<ui-dropdown-separator v-if="!isFolder" />-->
<ui-dropdown-button v-if="hasPreview" label="@page.preview.title" icon="fth-eye" :disabled="disabled" :prevent="true" @click="openPreview" />
<ui-dropdown-separator v-if="hasPreview" />
<ui-dropdown-button label="@ui.move.title" icon="fth-corner-down-right" @click="move(model)" />
<ui-dropdown-button label="@ui.copy.title" icon="fth-copy" @click="copy(model)" />
<ui-dropdown-separator />
</template>
<!--<template v-slot:buttons>
<ui-button type="light onbg" icon="fth-info" />
</template>-->
</ui-form-header>
<ui-editor v-if="!loading && editor" :config="editor" v-model="model" :is-page="true" infos="aside" :meta="meta" :disabled="disabled" :scope="true" :on-configure="onEditorConfigure">
<template v-slot:below>
<ui-editor-infos v-model="model" :disabled="disabled" />
</template>
</ui-editor>
<ui-editor v-if="!loading && editor" :config="editor" v-model="model" :is-page="true" infos="aside" :meta="meta" :disabled="disabled" :scope="true" :on-configure="onEditorConfigure" />
<div v-if="isFolder">
<!-- // TODO list children -->
@@ -59,6 +58,10 @@
isCreate()
{
return this.$route.name === 'pages-create' || !this.id;
},
hasPreview()
{
return this.id && !this.isFolder;
}
},
@@ -110,7 +113,7 @@
var config = { system: this.$route.query['zero.scope'] == 'system' };
const response = await form.load(() => !this.isCreate ? api.getById(this.id, undefined, config) : api.getEmpty(this.flavor, this.parent, config));
this.model = response;
this.model = response;
if (this.model)
{
@@ -184,6 +187,19 @@
disabled: _ => false,
component: PageInfoTab
});
},
async openPreview(_, opts)
{
opts.loading(true);
const result = await api.getPreviewUrl(this.id);
opts.loading(false);
if (result.data && result.data.url)
{
var resolved = this.$router.resolve({ name: 'preview', query: { path: result.data.url } });
window.open(window.location.origin + resolved.href, 'blank');
opts.hide();
}
}
}
})
@@ -1,14 +1,9 @@
<template>
<div class="page-editor-info">
<div class="ui-box" v-if="!isCreate && urls.length">
<ui-property label="@page.infotab.links" :is-text="true" :vertical="false">
<a v-for="url in urls" class="ui-link" :href="url" target="_blank"><ui-icon symbol="fth-external-link"></ui-icon> {{url}}</a>
</ui-property>
<!--<ui-property label="@page.infotab.revisions" :is-text="true" :vertical="false">-->
<!--<ui-revisions :get="getRevisions" />-->
<!--</ui-property>-->
</div>
<div class="ui-box">
<ui-property v-if="!isCreate && urls.length" label="@page.infotab.links" :is-text="true" :vertical="false">
<a v-for="url in urls" class="ui-linktext" :href="urlDomain + url" target="_blank"><ui-icon symbol="fth-external-link"></ui-icon> {{url}}</a>
</ui-property>
<!--<ui-property label="@page.schedule.label" :is-text="true" :vertical="false">
<ui-daterangepicker :value="{ from: value.publishDate, to: value.unpublishDate }" @input="onRangeChange" :class="{ 'is-primary': value.publishDate || value.unpublishDate }" :disabled="disabled" />
</ui-property>-->
@@ -48,7 +43,8 @@
data: () => ({
pageType: null,
urls: []
urls: [],
urlDomain: null
}),
computed: {
@@ -59,24 +55,36 @@
},
watch: {
'value.lastModifiedDate'()
{
this.reloadUrls();
}
},
mounted()
{
//api.getPageType(this.value.pageTypeAlias).then(pageType =>
//{
// this.pageType = pageType;
//});
//if (this.value.id)
//{
// api.getUrls(this.value.id).then(urls =>
// {
// this.urls = urls;
// });
//}
this.reloadUrls();
},
methods: {
async reloadUrls()
{
if (this.value.id)
{
const result = await api.getUrls(this.value.id);
this.urls = result.data.urls;
this.urlDomain = result.data.domain;
}
},
getRevisions(page)
{
//return api.getRevisions(this.value.id, page);
+31
View File
@@ -0,0 +1,31 @@
namespace zero.Models;
public class UrlsResult
{
public string[] Urls { get; set; } = Array.Empty<string>();
public string Domain { get; set; }
public UrlsResult(string domain, params string[] urls)
{
Domain = domain;
Urls = urls.Where(x => x.HasValue()).ToArray();
}
public UrlsResult(Uri domain, params string[] urls)
{
Domain = domain.Scheme + "://" + domain.Authority.TrimEnd("/");
Urls = urls.Where(x => x.HasValue()).ToArray();
}
}
public class PreviewUrlResult
{
public string Url { get; set; }
public PreviewUrlResult(string url)
{
Url = url;
}
}