link providers
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace zero.Core.Extensions
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
public static bool TryGetValue<T>(this Dictionary<string, object> model, string key, out T value)
|
||||
{
|
||||
if (!model.TryGetValue(key, out object valueObj) || !(valueObj is T))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = (T)valueObj;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static T GetValueOrDefault<T>(this Dictionary<string, object> model, string key)
|
||||
{
|
||||
object? value = model.GetValueOrDefault(key);
|
||||
return value == default || !(value is T) ? default : (T)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public interface ILinkListProvider : ILinkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Get paged list items.
|
||||
/// <param name="session">Current document session</param>
|
||||
/// <param name="page">Current page number (one-based)</param>
|
||||
/// <param name="search">Search query</param>
|
||||
/// </summary>
|
||||
Task<IList<TreeItem>> GetLinkListItems(IAsyncDocumentSession session, int page = 1, string search = null);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
using System.Threading.Tasks;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public interface ILinkProvider
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
string Alias { get; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool CanProcess(ILink link);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Task<string> ResolveLink(ILink link);
|
||||
Task<string> Resolve(ILink link);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Task<PreviewModel> Preview(IAsyncDocumentSession session, ILink link);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public interface ILinkTreeProvider : ILinkProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Get tree children for the current parent id.
|
||||
/// <param name="session">Current document session</param>
|
||||
/// <param name="parentId">Parent node id</param>
|
||||
/// <param name="activeId">Selected node so parents can be set to open for the tree to load correclty</param>
|
||||
/// </summary>
|
||||
Task<IList<TreeItem>> GetLinkTreeItems(IAsyncDocumentSession session, string parentId = null, string activeId = null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Database;
|
||||
using zero.Core.Entities;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public class Links : ILinks
|
||||
{
|
||||
protected IZeroStore Store { get; set; }
|
||||
protected ILogger<Links> Logger { get; set; }
|
||||
protected IEnumerable<ILinkProvider> Providers { get; set; }
|
||||
|
||||
public Links(IZeroStore store, ILogger<Links> logger, IEnumerable<ILinkProvider> providers)
|
||||
{
|
||||
Store = store;
|
||||
Logger = logger;
|
||||
Providers = providers;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl(ILink link)
|
||||
{
|
||||
ILinkProvider provider = Providers.LastOrDefault(x => x.CanProcess(link));
|
||||
|
||||
if (provider == null)
|
||||
{
|
||||
Logger.LogWarning("Could not find provider for link with area {area}", link.Area);
|
||||
return null;
|
||||
}
|
||||
|
||||
return await provider.Resolve(link);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<ILink, string>> GetUrls(params ILink[] links)
|
||||
{
|
||||
Dictionary<ILink, string> result = new();
|
||||
|
||||
foreach (ILink link in links)
|
||||
{
|
||||
result.Add(link, await GetUrl(link));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public ILinkProvider GetProvider(ILink link)
|
||||
{
|
||||
return Providers.LastOrDefault(x => x.CanProcess(link));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Get URL from a link object by finding a provider which can resolve the link
|
||||
/// </summary>
|
||||
Task<string> GetUrl(ILink link);
|
||||
|
||||
/// <summary>
|
||||
/// Get URLs from link objects by finding matching providers
|
||||
/// </summary>
|
||||
Task<Dictionary<ILink, string>> GetUrls(params ILink[] links);
|
||||
|
||||
/// <summary>
|
||||
/// Get the provider for a specific link
|
||||
/// </summary>
|
||||
ILinkProvider GetProvider(ILink link);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,65 @@
|
||||
using Raven.Client.Documents.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Database;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
using zero.Core.Options;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public class PageLinkProvider : ILinkProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name { get; } = "@links.providers.page";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Alias { get; } = "zero.pages";
|
||||
|
||||
protected IZeroStore Store { get; set; }
|
||||
|
||||
protected IRoutes Routes { get; set; }
|
||||
protected IZeroOptions Options { get; set; }
|
||||
|
||||
|
||||
public PageLinkProvider(IZeroStore store, IRoutes routes)
|
||||
public PageLinkProvider(IRoutes routes, IZeroOptions options)
|
||||
{
|
||||
Store = store;
|
||||
Routes = routes;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> ResolveLink(ILink link)
|
||||
public bool CanProcess(ILink link) => link.Area == "zero.pages";
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> Resolve(ILink link)
|
||||
{
|
||||
if (!link.Values.TryGetValue("pageId", out object pageIdObj))
|
||||
return await Routes.GetUrl<IPage>(link.Values.GetValueOrDefault<string>("id"));
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PreviewModel> Preview(IAsyncDocumentSession session, ILink link)
|
||||
{
|
||||
string id = link.Values.GetValueOrDefault<string>("id");
|
||||
|
||||
if (id.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string pageId = pageIdObj.ToString();
|
||||
IPage page = await session.LoadAsync<IPage>(id);
|
||||
|
||||
using IAsyncDocumentSession session = Store.OpenAsyncSession();
|
||||
IPage page = await session.LoadAsync<IPage>(pageId);
|
||||
IRoute route = await Routes.GetRoute(page);
|
||||
if (page == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return route.Url;
|
||||
PageType pageType = Options.Pages.GetAllItems().FirstOrDefault(x => x.Alias == page.PageTypeAlias);
|
||||
|
||||
string url = await Routes.GetUrl<IPage>(page);
|
||||
|
||||
return new()
|
||||
{
|
||||
Id = page.Id,
|
||||
Icon = pageType?.Icon ?? "fth-folder",
|
||||
Name = page.Name,
|
||||
Text = url
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Api;
|
||||
using zero.Core.Database;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
@@ -39,10 +38,42 @@ namespace zero.Core.Routing
|
||||
public async Task<string> GetUrl<T>(T model) => (await GetRoute(model))?.Url;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl<T>(string id) where T : IZeroIdEntity => (await GetRoute(id))?.Url;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<T, string>> GetUrls<T>(params T[] models) => (await GetRoutes(models)).ToDictionary(x => x.Key, x => x.Value?.Url);
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IRoute> GetRoute<T>(string id) where T : IZeroIdEntity
|
||||
{
|
||||
if (id.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
IRouteProvider routeProvider = Providers.FirstOrDefault(x => x.AffectedTypes.Any(t => t.IsAssignableFrom(type)));
|
||||
|
||||
if (routeProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using IAsyncDocumentSession session = Store.OpenAsyncSession();
|
||||
T model = await session.LoadAsync<T>(id);
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await routeProvider.GetRoute(session, model);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IRoute> GetRoute<T>(T model)
|
||||
{
|
||||
@@ -246,11 +277,21 @@ namespace zero.Core.Routing
|
||||
/// </summary>
|
||||
Task<string> GetUrl<T>(T model);
|
||||
|
||||
/// <summary>
|
||||
/// Get the URL for an entity
|
||||
/// </summary>
|
||||
Task<string> GetUrl<T>(string id) where T : IZeroIdEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get the route object for an entity
|
||||
/// </summary>
|
||||
Task<IRoute> GetRoute<T>(T model);
|
||||
|
||||
/// <summary>
|
||||
/// Get the route object for an entity
|
||||
/// </summary>
|
||||
Task<IRoute> GetRoute<T>(string id) where T : IZeroIdEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get URLs for multiple entities
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { get, post, del } from '../helpers/request.ts';
|
||||
|
||||
const base = 'links/';
|
||||
|
||||
export default {
|
||||
getPreviews: async links => await post(base + 'getPreviews', links)
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
<ui-search v-model="search" />
|
||||
</ui-property>
|
||||
<ui-property :vertical="true">
|
||||
<ui-tree ref="tree" v-bind="treeConfig" :get="getTreeItems" @select="onSelect" class="ui-linkpicker-area-pages-tree" />
|
||||
<ui-tree ref="tree" v-bind="treeConfig" :get="getTreeItems" @select="onSelect" :selection="selection" class="ui-linkpicker-area-pages-tree" />
|
||||
</ui-property>
|
||||
</div>
|
||||
</template>
|
||||
@@ -29,9 +29,11 @@
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
selection: [],
|
||||
treeConfig: {
|
||||
parent: null,
|
||||
active: null
|
||||
active: null,
|
||||
mode: 'select'
|
||||
},
|
||||
search: null,
|
||||
debouncedSearch: null
|
||||
@@ -41,22 +43,40 @@
|
||||
search()
|
||||
{
|
||||
this.debouncedSearch();
|
||||
},
|
||||
value()
|
||||
{
|
||||
this.selection = [this.value.values.id];
|
||||
}
|
||||
},
|
||||
|
||||
mounted()
|
||||
{
|
||||
this.debouncedSearch = _debounce(() => this.$refs.tree.refresh(), 300);
|
||||
this.selection = this.value.values.id ? [this.value.values.id] : [];
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
onSelect(item)
|
||||
isValid()
|
||||
{
|
||||
this.value.values = {
|
||||
id: item.id
|
||||
};
|
||||
return this.selection.length > 0;
|
||||
},
|
||||
|
||||
onSelect(id)
|
||||
{
|
||||
if (id)
|
||||
{
|
||||
this.selection = [id];
|
||||
this.value.values = { id };
|
||||
}
|
||||
else
|
||||
{
|
||||
this.selection = [];
|
||||
this.value.values = { id: null };
|
||||
}
|
||||
this.$emit('change', this.value);
|
||||
this.$emit('input', this.value);
|
||||
},
|
||||
|
||||
getTreeItems(parent)
|
||||
@@ -67,10 +87,6 @@
|
||||
|
||||
res.forEach(item =>
|
||||
{
|
||||
//if (item.id === this.model)
|
||||
//{
|
||||
// item.isSelected = true;
|
||||
//}
|
||||
item.hasActions = false;
|
||||
});
|
||||
|
||||
@@ -92,4 +108,33 @@
|
||||
{
|
||||
background: var(--color-tree-selected);
|
||||
}
|
||||
|
||||
.ui-linkpicker-area-pages-tree
|
||||
{
|
||||
.ui-tree-item.is-disabled
|
||||
{
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected, .ui-tree-item:hover:not(.is-disabled)
|
||||
{
|
||||
background: var(--color-tree-selected);
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected
|
||||
{
|
||||
&:after
|
||||
{
|
||||
font-family: "Feather";
|
||||
content: "\e83e";
|
||||
font-size: 16px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui-tree-item-text
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="ui-linkpicker" :class="{'is-disabled': disabled }">
|
||||
<input ref="input" type="hidden" :value="value" />
|
||||
<!--<div class="ui-pagepicker-previews" v-if="previews.length > 0">
|
||||
<div class="ui-pagepicker-previews" v-if="previews.length > 0">
|
||||
<div v-for="preview in previews" class="ui-pagepicker-preview">
|
||||
<ui-select-button :icon="preview.icon" :label="preview.name" :description="preview.text" :disabled="disabled" @click="pick(preview.id)" :tokens="{ id: preview.id }" />
|
||||
<ui-select-button :icon="preview.icon" :label="preview.name" :description="preview.text" :disabled="disabled" @click="onPick(preview.id)" :tokens="{ id: preview.id }" />
|
||||
<ui-icon-button v-if="!disabled" @click="remove(preview.id)" icon="fth-x" title="@ui.close" />
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
<ui-select-button v-if="canAdd" icon="fth-plus" :label="limit > 1 ? '@ui.add' : '@ui.select'" @click="onPick()" :disabled="disabled" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<script>
|
||||
import LinkpickerOverlay from './overlay.vue';
|
||||
import LinksApi from 'zero/api/links.js';
|
||||
import Overlay from 'zero/helpers/overlay.js';
|
||||
import { extend as _extend, isArray as _isArray, isEmpty as _isEmpty, clone as _clone } from 'underscore';
|
||||
|
||||
@@ -77,7 +78,6 @@
|
||||
},
|
||||
canAdd()
|
||||
{
|
||||
return true; // TODO
|
||||
let count = Array.isArray(this.value) ? this.value.length : (!this.value ? 0 : 1);
|
||||
return !this.disabled && count < this.limit;
|
||||
}
|
||||
@@ -101,17 +101,19 @@
|
||||
updatePreviews()
|
||||
{
|
||||
this.previews = [];
|
||||
//if (!this.value || _isEmpty(this.value))
|
||||
//{
|
||||
// this.previews = [];
|
||||
// return;
|
||||
//}
|
||||
|
||||
//let ids = _isArray(this.value) ? this.value : [this.value];
|
||||
//PagesApi.getPreviews(ids).then(res =>
|
||||
//{
|
||||
// this.previews = res;
|
||||
//});
|
||||
if (!this.value || _isEmpty(this.value))
|
||||
{
|
||||
this.previews = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let links = Array.isArray(this.value) ? this.value : [this.value];
|
||||
|
||||
LinksApi.getPreviews(links).then(res =>
|
||||
{
|
||||
this.previews = res;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -125,7 +127,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
this.onChange(this.limit > 1 ? [] : null);
|
||||
this.onChange(this.multiple ? [] : null);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -134,7 +136,8 @@
|
||||
{
|
||||
this.pick(id).then(res =>
|
||||
{
|
||||
if (this.limit > 1)
|
||||
console.info(JSON.parse(JSON.stringify(res)));
|
||||
if (this.multiple)
|
||||
{
|
||||
this.value.push(res);
|
||||
this.onChange(this.value);
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
|
||||
<div class="ui-box">
|
||||
<component v-if="area && area.component" :is="area.component" :area="area" :value="link" />
|
||||
<component v-if="area && area.component" :is="area.component" :area="area" v-model="link" />
|
||||
</div>
|
||||
</div>
|
||||
</ui-overlay-editor>
|
||||
@@ -90,6 +90,8 @@
|
||||
this.link = JSON.parse(JSON.stringify(this.model || this.template));
|
||||
this.link.area = this.current;
|
||||
|
||||
console.info(JSON.parse(JSON.stringify(this.link)));
|
||||
|
||||
setTimeout(() => this.opened = true, 300);
|
||||
},
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
activeId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -62,7 +66,7 @@
|
||||
'has-children': item.hasChildren,
|
||||
'is-inactive': item.isInactive,
|
||||
'is-open': item.isOpen,
|
||||
'is-selected': item.isSelected,
|
||||
'is-selected': this.selected || item.isSelected,
|
||||
'is-disabled': item.disabled,
|
||||
'is-active': this.isLink && ((!!item.id && item.id === this.activeId) || item.id == this.$route.params.id || (item.url && !item.url.params && item.url.name === this.$route.name))
|
||||
};
|
||||
@@ -128,7 +132,7 @@
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
&.is-active:before
|
||||
&.is-active:before, &.is-selected:before
|
||||
{
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
@@ -139,17 +143,19 @@
|
||||
background: var(--color-tree-selected);
|
||||
}
|
||||
|
||||
/*&.is-active:after
|
||||
&.is-selected:after
|
||||
{
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
display: inline-block;
|
||||
background: var(--color-tree-selected-line);
|
||||
}*/
|
||||
font-family: "Feather";
|
||||
content: "\e83e";
|
||||
font-size: 16px;
|
||||
color: var(--color-primary);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&.is-selected .ui-tree-item-text
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tree-item-link
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<slot></slot>
|
||||
<span v-if="status === 'loading'" class="ui-tree-item-loading"><i></i></span>
|
||||
<template v-for="item in items">
|
||||
<ui-tree-item :value="item" @rightclick="onRightClicked" @click="onSelect(item, $event)" @actions="onActionsClicked" @open="toggle" :active-id="active" />
|
||||
<ui-tree v-if="item.hasChildren && item.isOpen && status != 'loading'" :get="get" :parent="item.id" :depth="depth + 1" :active="active" @select="onSelect">
|
||||
<ui-tree-item :value="item" @rightclick="onRightClicked" @click="onSelect(item, $event)" @actions="onActionsClicked" @open="toggle" :active-id="active" :selected="selection.indexOf(item.id) > -1" />
|
||||
<ui-tree v-if="item.hasChildren && item.isOpen && status != 'loading'" v-bind="{ get, parent: item.id, depth: depth + 1, active, onSelect, mode, selection, selectionLimit }">
|
||||
<template v-slot:actions="props">
|
||||
<slot name="actions" v-bind="props"></slot>
|
||||
</template>
|
||||
@@ -51,6 +51,18 @@
|
||||
hasActions: {
|
||||
type: Function,
|
||||
default: null
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'link'
|
||||
},
|
||||
selection: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectionLimit: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
|
||||
@@ -123,7 +135,29 @@
|
||||
// selected an item
|
||||
onSelect(item, ev)
|
||||
{
|
||||
this.$emit('select', item, ev);
|
||||
if (this.mode === 'select')
|
||||
{
|
||||
let index = this.selection.indexOf(item.id);
|
||||
if (index > -1)
|
||||
{
|
||||
this.selection.splice(index, 1);
|
||||
}
|
||||
else if (this.selectionLimit === 1)
|
||||
{
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.selection.push(item.id);
|
||||
}
|
||||
else if (this.selection.length < this.selectionLimit)
|
||||
{
|
||||
this.selection.push(item.id);
|
||||
}
|
||||
|
||||
this.$emit('select', this.selectionLimit > 1 ? this.selection : (this.selection.length > 0 ? this.selection[0] : null), ev);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.$emit('select', item, ev);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -197,6 +231,7 @@
|
||||
.ui-tree
|
||||
{
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ui-tree-header + .ui-tree-item
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</ui-property>
|
||||
</div>
|
||||
<div class="ui-box pages-copy-items">
|
||||
<ui-tree ref="tree" :get="getItems" @select="onSelect" />
|
||||
<ui-tree ref="tree" :get="getItems" @select="onSelect" mode="select" />
|
||||
</div>
|
||||
</ui-overlay-editor>
|
||||
</template>
|
||||
@@ -39,7 +39,6 @@
|
||||
|
||||
data: () => ({
|
||||
items: [],
|
||||
selected: [],
|
||||
state: 'default',
|
||||
cache: {},
|
||||
prevItem: null,
|
||||
@@ -56,18 +55,9 @@
|
||||
|
||||
methods: {
|
||||
|
||||
onSelect(item)
|
||||
onSelect(id)
|
||||
{
|
||||
item.isSelected = true;
|
||||
|
||||
if (this.prevItem && this.prevItem.id != item.id)
|
||||
{
|
||||
this.prevItem.isSelected = false;
|
||||
}
|
||||
|
||||
this.prevItem = item;
|
||||
this.selected = item;
|
||||
//this.config.confirm(item);
|
||||
this.selected = id;
|
||||
},
|
||||
|
||||
getItems(parent)
|
||||
@@ -100,8 +90,6 @@
|
||||
|
||||
response.forEach(item =>
|
||||
{
|
||||
//item.disabled = true;
|
||||
item.isSelected = false;
|
||||
item.disabled = item.id === 'recyclebin' || item.id == this.model.id;
|
||||
item.hasActions = false;
|
||||
});
|
||||
@@ -115,7 +103,7 @@
|
||||
{
|
||||
this.state = 'loading';
|
||||
|
||||
PagesApi.copy(this.model.id, this.selected.id, this.includeDescendants).then(res =>
|
||||
PagesApi.copy(this.model.id, this.selected, this.includeDescendants).then(res =>
|
||||
{
|
||||
if (res.success)
|
||||
{
|
||||
@@ -154,7 +142,7 @@
|
||||
.pages-copy .ui-box
|
||||
{
|
||||
margin: 0;
|
||||
padding: 20px var(--padding) 18px;
|
||||
padding: 20px var(--padding) 18px;
|
||||
|
||||
& + .ui-box
|
||||
{
|
||||
@@ -166,27 +154,6 @@
|
||||
{
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected, .ui-tree-item:hover:not(.is-disabled)
|
||||
{
|
||||
background: var(--color-tree-selected);
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected
|
||||
{
|
||||
&:after
|
||||
{
|
||||
font-family: "Feather";
|
||||
content: "\e83e";
|
||||
font-size: 16px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui-tree-item-text
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pages-copy content
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<p class="pages-move-text" v-localize:html="{ key: '@ui.move.text', tokens: { name: model.name } }"></p>
|
||||
<div class="ui-box pages-move-items">
|
||||
<ui-tree ref="tree" :get="getItems" @select="onSelect" />
|
||||
<ui-tree ref="tree" :get="getItems" mode="select" @select="onSelect" />
|
||||
</div>
|
||||
</ui-overlay-editor>
|
||||
</template>
|
||||
@@ -50,18 +50,9 @@
|
||||
|
||||
methods: {
|
||||
|
||||
onSelect(item)
|
||||
onSelect(id)
|
||||
{
|
||||
item.isSelected = true;
|
||||
|
||||
if (this.prevItem && this.prevItem.id != item.id)
|
||||
{
|
||||
this.prevItem.isSelected = false;
|
||||
}
|
||||
|
||||
this.prevItem = item;
|
||||
this.selected = item;
|
||||
//this.config.confirm(item);
|
||||
this.selected = id;
|
||||
},
|
||||
|
||||
getItems(parent)
|
||||
@@ -94,13 +85,6 @@
|
||||
|
||||
response.forEach(item =>
|
||||
{
|
||||
//item.disabled = true;
|
||||
item.isSelected = this.model.parentId == item.id;
|
||||
|
||||
if (item.isSelected)
|
||||
{
|
||||
this.prevItem = item;
|
||||
}
|
||||
item.disabled = item.id === 'recyclebin' || item.id == this.model.id;
|
||||
item.hasActions = false;
|
||||
});
|
||||
@@ -112,7 +96,7 @@
|
||||
|
||||
onSave()
|
||||
{
|
||||
if (this.model.parentId == this.selected.id)
|
||||
if (this.model.parentId == this.selected)
|
||||
{
|
||||
this.config.close();
|
||||
return;
|
||||
@@ -120,7 +104,7 @@
|
||||
|
||||
this.state = 'loading';
|
||||
|
||||
PagesApi.move(this.model.id, this.selected.id).then(res =>
|
||||
PagesApi.move(this.model.id, this.selected).then(res =>
|
||||
{
|
||||
if (res.success)
|
||||
{
|
||||
@@ -148,27 +132,6 @@
|
||||
{
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected, .ui-tree-item:hover:not(.is-disabled)
|
||||
{
|
||||
background: var(--color-tree-selected);
|
||||
}
|
||||
|
||||
.ui-tree-item.is-selected
|
||||
{
|
||||
&:after
|
||||
{
|
||||
font-family: "Feather";
|
||||
content: "\e83e";
|
||||
font-size: 16px;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ui-tree-item-text
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pages-move content
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Api;
|
||||
using zero.Core.Entities;
|
||||
|
||||
namespace zero.Web.Controllers
|
||||
{
|
||||
public class LinkPickerController : BackofficeController
|
||||
{
|
||||
IPageTreeApi Api;
|
||||
IPagesApi PagesApi;
|
||||
|
||||
public LinkPickerController(IPageTreeApi api, IPagesApi pagesApi)
|
||||
{
|
||||
Api = api;
|
||||
PagesApi = pagesApi;
|
||||
}
|
||||
|
||||
|
||||
public async Task<IList<TreeItem>> GetChildren([FromQuery] string areaAlias, [FromQuery] string parent = null, [FromQuery] string active = null)
|
||||
{
|
||||
return await Api.GetChildren(parent, active);
|
||||
}
|
||||
|
||||
//public async Task<IList<PreviewModel>> GetPreviews([FromQuery] List<string> ids)
|
||||
//{
|
||||
// return Previews(await PagesApi.GetByIds(ids.ToArray()), PreviewTransform);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Api;
|
||||
using zero.Core.Database;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Routing;
|
||||
using zero.Core.Utils;
|
||||
|
||||
namespace zero.Web.Controllers
|
||||
{
|
||||
public class LinksController : BackofficeController
|
||||
{
|
||||
IZeroStore Store;
|
||||
ILinks Links;
|
||||
|
||||
public LinksController(IZeroStore store, ILinks links)
|
||||
{
|
||||
Store = store;
|
||||
Links = links;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IList<PreviewModel>> GetPreviews([FromBody] List<Link> links)
|
||||
{
|
||||
IList<PreviewModel> previews = new List<PreviewModel>();
|
||||
using IAsyncDocumentSession session = Store.OpenAsyncSession();
|
||||
|
||||
foreach (Link link in links)
|
||||
{
|
||||
ILinkProvider provider = Links.GetProvider(link);
|
||||
PreviewModel model = null;
|
||||
|
||||
if (provider != null)
|
||||
{
|
||||
model = await provider.Preview(session, link);
|
||||
}
|
||||
|
||||
previews.Add(model ?? new PreviewModel()
|
||||
{
|
||||
HasError = true,
|
||||
Icon = "fth-alert-circle color-red",
|
||||
Id = IdGenerator.Create(),
|
||||
Name = "@errors.preview.notfound",
|
||||
Text = "@errors.preview.notfound_text"
|
||||
});
|
||||
}
|
||||
|
||||
return previews;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,8 @@ namespace zero.Web.Defaults
|
||||
//services.AddTransient<IUrlProvider, PageUrlProvider>();
|
||||
services.AddScoped<IRouteProvider, PageRouteProvider>();
|
||||
services.AddScoped<PageRouteProvider>();
|
||||
services.AddScoped<ILinks, Links>();
|
||||
services.AddScoped<ILinkProvider, PageLinkProvider>();
|
||||
|
||||
services.AddScoped<ZeroRoutesTransformer>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user