users API start
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using Raven.Client.Documents.Indexes;
|
||||
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class zero_Api_Users_Listing : ZeroIndex<ZeroUser>
|
||||
{
|
||||
protected override void Create()
|
||||
{
|
||||
Map = items => items.Select(item => new
|
||||
{
|
||||
Name = item.Name,
|
||||
CreatedDate = item.CreatedDate
|
||||
});
|
||||
|
||||
Index(x => x.Name, FieldIndexing.Search);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class UserBasic : ZeroIdEntity
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string AvatarId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class UserMapperProfile : ZeroMapperProfile
|
||||
{
|
||||
public override void Configure(IZeroMapper mapper)
|
||||
{
|
||||
mapper.Define<ZeroUser, UserBasic>((source, ctx) => new(), Map);
|
||||
}
|
||||
|
||||
|
||||
protected virtual void Map(ZeroUser source, UserBasic target, IZeroMapperContext ctx)
|
||||
{
|
||||
target.Id = source.Id;
|
||||
target.Name = source.Name;
|
||||
target.Email = source.Email;
|
||||
target.IsActive = source.IsActive;
|
||||
target.AvatarId = source.AvatarId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class UserModule : ZeroModule
|
||||
{
|
||||
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSingleton<IPermissionProvider, UserPermissions>();
|
||||
services.AddSingleton<IMapperProfile, UserMapperProfile>();
|
||||
|
||||
services.Configure<RavenOptions>(opts =>
|
||||
{
|
||||
opts.Indexes.Add<zero_Api_Users_Listing>();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class UserPermissions : PermissionProvider
|
||||
{
|
||||
public const string Group = "zero.user";
|
||||
|
||||
public const string Create = "zero.user.create";
|
||||
public const string Read = "zero.user.read";
|
||||
public const string Update = "zero.user.update";
|
||||
public const string Delete = "zero.user.delete";
|
||||
|
||||
|
||||
public override Task Configure(IPermissionContext context)
|
||||
{
|
||||
PermissionGroup group = new(Group, "@user.list");
|
||||
group.Permissions.Add(new Permission("zero.user.defaults", "Default permissions")
|
||||
{
|
||||
Children = new()
|
||||
{
|
||||
new(Create, "@permission.states.create"),
|
||||
new(Read, "@permission.states.read"),
|
||||
new(Update, "@permission.states.update"),
|
||||
new(Delete, "@permission.states.delete")
|
||||
}
|
||||
});
|
||||
|
||||
context.AddGroup(group);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//namespace zero.Web.Models
|
||||
//{
|
||||
// public class UserListModel : ListModel
|
||||
// {
|
||||
// public string Name { get; set; }
|
||||
|
||||
// public bool IsActive { get; set; }
|
||||
|
||||
// public string Email { get; set; }
|
||||
|
||||
// public string Roles { get; set; }
|
||||
|
||||
// public string AvatarId { get; set; }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace zero.Api.Endpoints.Users;
|
||||
|
||||
public class UsersController : ZeroApiController
|
||||
{
|
||||
readonly IUserService Users;
|
||||
|
||||
public UsersController(IUserService users)
|
||||
{
|
||||
Users = users;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("empty")]
|
||||
[ZeroAuthorize(UserPermissions.Create)]
|
||||
public virtual async Task<ActionResult<ZeroUser>> Empty(string flavor = null)
|
||||
{
|
||||
return new ZeroUser();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[ZeroAuthorize(UserPermissions.Read)]
|
||||
public virtual async Task<ActionResult<ZeroUser>> Get(string id)
|
||||
{
|
||||
ZeroUser model = await Users.GetUserById(id);
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
//HttpContext.Items[ApiConstants.ChangeToken] = Store.GetChangeToken(model);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("")]
|
||||
[ZeroAuthorize(UserPermissions.Read)]
|
||||
public virtual async Task<ActionResult<Paged>> Get([FromQuery] ListQuery<ZeroUser> query)
|
||||
{
|
||||
query.OrderQuery ??= q => q.OrderByDescending(x => x.CreatedDate);
|
||||
query.SearchSelector ??= x => x.Name;
|
||||
Paged<ZeroUser> result = await Users.GetAll(query.Page, query.PageSize);
|
||||
return Mapper.Map<ZeroUser, UserBasic>(result);
|
||||
}
|
||||
|
||||
|
||||
//[HttpPost("")]
|
||||
//[ZeroAuthorize(UserPermissions.Create)]
|
||||
//public virtual Task<ActionResult<Result>> Create(ZeroUser saveModel) => CreateModel(saveModel);
|
||||
|
||||
|
||||
//[HttpPut("{id}")]
|
||||
//[ZeroAuthorize(UserPermissions.Update)]
|
||||
//public virtual Task<ActionResult<Result>> Update(string id, ZeroUser updateModel, [FromQuery] string changeToken = null) => UpdateModel(id, updateModel, changeToken);
|
||||
|
||||
|
||||
//[HttpDelete("{id}")]
|
||||
//[ZeroAuthorize(UserPermissions.Delete)]
|
||||
//public virtual Task<ActionResult<Result>> Delete(string id) => DeleteModel(id);
|
||||
}
|
||||
@@ -38,6 +38,7 @@ public class ZeroApiPlugin : ZeroPlugin
|
||||
modules.Add<Endpoints.Media.MediaModule>();
|
||||
modules.Add<Endpoints.Spaces.SpaceModule>();
|
||||
modules.Add<Endpoints.Integrations.IntegrationModule>();
|
||||
modules.Add<Endpoints.Users.UserModule>();
|
||||
|
||||
modules.ConfigureServices(services, configuration);
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import AppConfirm from 'zero/components/overlays/confirm.vue'; // TODO importing vue files in js/ts files causes a Rollup production build error
|
||||
import AppMessage from 'zero/components/overlays/message.vue';
|
||||
import Strings from 'zero/helpers/strings.js';
|
||||
import { find as _find, extend as _extend } from 'underscore';
|
||||
|
||||
export default new Vue({
|
||||
|
||||
data: () => ({
|
||||
dropdownInstance: null,
|
||||
instances: []
|
||||
}),
|
||||
|
||||
methods: {
|
||||
|
||||
|
||||
// opens an overlay
|
||||
open(options)
|
||||
{
|
||||
const defaultWidth = options.display === 'editor' ? 560 : 460;
|
||||
|
||||
options = _extend({
|
||||
id: Strings.guid(),
|
||||
display: 'dialog',
|
||||
width: defaultWidth,
|
||||
hide: this.close,
|
||||
autoclose: true,
|
||||
softdismiss: options.display !== 'editor',
|
||||
closeLabel: '@ui.close',
|
||||
confirmLabel: '@ui.confirm',
|
||||
confirmType: 'default',
|
||||
alias: options.alias
|
||||
}, options);
|
||||
|
||||
if (typeof options.theme === 'undefined')
|
||||
{
|
||||
options.theme = 'default';
|
||||
}
|
||||
|
||||
this.instances.push(options);
|
||||
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
options.close = () =>
|
||||
{
|
||||
this.close(options);
|
||||
reject(options);
|
||||
// TODO should we move to resolve here, so we don't trigger errors in case the implementation does not catch them?
|
||||
// this will at least need some tests if the .then callback does not catch null values
|
||||
};
|
||||
options.hide = options.close;
|
||||
|
||||
options.confirm = data =>
|
||||
{
|
||||
if (options.autoclose)
|
||||
{
|
||||
this.close(options);
|
||||
}
|
||||
resolve(data, options);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
// closes an overlay
|
||||
close(instance)
|
||||
{
|
||||
if (this.instances.length < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
this.instances.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof instance === 'string')
|
||||
{
|
||||
instance = _find(this.instances, item => item.id === instance);
|
||||
}
|
||||
|
||||
if (instance)
|
||||
{
|
||||
const index = this.instances.indexOf(instance);
|
||||
this.instances.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
// closes all overlays
|
||||
closeAll()
|
||||
{
|
||||
this.instances.forEach(instance =>
|
||||
{
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -5,7 +5,9 @@
|
||||
<ui-dropdown v-else ref="dropdown" align="right">
|
||||
|
||||
<template v-slot:button>
|
||||
<ui-button :label="label" :type="type" :disabled="disabled" />
|
||||
<slot>
|
||||
<ui-button :label="label" :type="type" :disabled="disabled" />
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<template v-if="flavors.length > 0">
|
||||
|
||||
@@ -19,7 +19,8 @@ import
|
||||
pagePlugin,
|
||||
mailTemplatePlugin,
|
||||
translationPlugin,
|
||||
integrationPlugin
|
||||
integrationPlugin,
|
||||
userPlugin
|
||||
} from '../modules';
|
||||
import editorPlugin from '../editor/plugin';
|
||||
import { ZeroSchema } from 'zero/schemas';
|
||||
@@ -111,6 +112,7 @@ export class ZeroRuntime implements Zero
|
||||
mailTemplatePlugin.install(pluginOptions);
|
||||
translationPlugin.install(pluginOptions);
|
||||
integrationPlugin.install(pluginOptions);
|
||||
userPlugin.install(pluginOptions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ export * from './spaces';
|
||||
export * from './pages';
|
||||
export * from './mails';
|
||||
export * from './translations';
|
||||
export * from './integrations';
|
||||
export * from './integrations';
|
||||
export * from './users';
|
||||
@@ -17,8 +17,11 @@
|
||||
<span class="ui-tag" v-for="tag in model.tags">{{tag}}</span>
|
||||
</div>-->
|
||||
<div class="integrations-item-bottom">
|
||||
<ui-button class="integrations-item-button" type="action small" v-if="!model.isConfigured" v-localize="'Setup'" @click="open" />
|
||||
<ui-button class="integrations-item-button" type="action small" v-else v-localize="'Edit'" @click="open" />
|
||||
<!--<ui-add-button :route="createRoute" alias="countries" v-if="!model.isConfigured">
|
||||
<ui-button class="integrations-item-button" type="action small" v-localize="'Setup'" />
|
||||
</ui-add-button>-->
|
||||
<ui-button v-if="!model.isConfigured" class="integrations-item-button" type="action small" v-localize="'Setup'" @click="open" />
|
||||
<ui-button v-else class="integrations-item-button" type="action small" v-localize="'Edit'" @click="open" />
|
||||
<span class="integrations-item-active" v-if="model.isConfigured && model.isActivated">
|
||||
<ui-icon symbol="fth-check-circle" :size="16" /> <span>Active</span>
|
||||
</span>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
import { ZeroEditor } from "../../editor/editor";
|
||||
import { formatDate } from '../../utils/dates';
|
||||
|
||||
const editor = new ZeroEditor('demo');
|
||||
|
||||
editor.field('name', { label: 'Text' }).text({ maxLength: 120 });
|
||||
editor.field('isActive', { label: 'Toggle', horizontal: true }).toggle();
|
||||
editor.field('number', { label: 'Number' }).number();
|
||||
editor.field('output', { label: 'Number' }).output();
|
||||
editor.field('rte', { label: 'Number' }).rte();
|
||||
editor.field('textarea', { label: 'Number' }).textarea();
|
||||
editor.field('state', { label: 'Number' }).state({ items: [{ label: 'Green', value: 'green' }, { label: 'Red', value: 'red' }] });
|
||||
|
||||
export default editor;
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-header-bar title="Demo" />
|
||||
<ui-editor config="demo" v-model="model" :meta="meta" :disabled="disabled" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
//import api from './api';
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
meta: {},
|
||||
model: {
|
||||
name: 'Tobi',
|
||||
isActive: true,
|
||||
number: 17,
|
||||
output: 'This is an output',
|
||||
rte: '',
|
||||
textarea: 'A textarea\nfor you',
|
||||
state: 'red'
|
||||
},
|
||||
route: 'demo',
|
||||
disabled: false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -6,5 +6,8 @@ export default {
|
||||
install(app: ZeroPluginOptions)
|
||||
{
|
||||
app.route({ name: 'settings', path: '/settings', component: () => import('./settings.vue') });
|
||||
app.route({ name: 'demo', path: '/settings/demo', component: () => import('./demo.vue') });
|
||||
|
||||
app.schema('demo', () => import('./demo'));
|
||||
}
|
||||
} as ZeroPlugin;
|
||||
@@ -44,6 +44,18 @@
|
||||
{
|
||||
this.groups = useUiStore().settingGroups;
|
||||
this.appCount = useAppStore().applications.length;
|
||||
|
||||
if (!this.groups[1].items.find(x => x.alias === 'demo'))
|
||||
{
|
||||
this.groups[1].items.push({
|
||||
alias: "demo",
|
||||
description: "Demo all editor components",
|
||||
icon: "fth-box",
|
||||
isPlugin: true,
|
||||
name: "Components",
|
||||
url: "/settings/demo"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -11,13 +11,19 @@ export default {
|
||||
getEmpty: (alias: string, config?: ApiRequestConfig) => get("spaces/empty", { ...config, params: { alias } }),
|
||||
|
||||
|
||||
//getById: (id: string, changeVector?: string, config?: ApiRequestConfig) => get('countries/' + id, { ...config, params: { changeVector } }),
|
||||
//getByAlias: async alias => await get(base + 'getByAlias', { params: { alias } }),
|
||||
|
||||
//getByQuery: (query: ApiRequestQuery, config?: ApiRequestConfig) => get('countries', { ...config, params: { ...query } }),
|
||||
//getAll: async () => await get(base + 'getAll'),
|
||||
|
||||
//create: (model: any, config?: ApiRequestConfig) => post('countries', model, config),
|
||||
//getList: async (alias, query) =>
|
||||
//{
|
||||
// query.alias = alias;
|
||||
// return await get(base + 'getList', { params: query })
|
||||
//},
|
||||
|
||||
//update: (model: any, config?: ApiRequestConfig) => put('countries/' + model.id, model, config),
|
||||
//getContent: async (alias, contentId) => await get(base + 'getContent', { params: { alias, contentId } }),
|
||||
|
||||
//delete: (id: string, config?: ApiRequestConfig) => del('countries/' + id, null, config),
|
||||
//save: async model => await post(base + 'save', model),
|
||||
|
||||
//delete: async (alias, id) => await del(base + 'delete', { params: { alias, id } })
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { get, post, put, del, ApiRequestConfig, ApiRequestQuery } from '../../services/request';
|
||||
|
||||
export default {
|
||||
|
||||
getEmpty: (flavor?: string, config?: ApiRequestConfig) => get("users/empty", { ...config, params: { flavor } }),
|
||||
|
||||
getById: (id: string, changeVector?: string, config?: ApiRequestConfig) => get('users/' + id, { ...config, params: { changeVector } }),
|
||||
|
||||
getByQuery: (query: ApiRequestQuery, config?: ApiRequestConfig) => get('users', { ...config, params: { ...query } }),
|
||||
|
||||
//create: (model: any, config?: ApiRequestConfig) => post('countries', model, config),
|
||||
|
||||
//update: (model: any, config?: ApiRequestConfig) => put('countries/' + model.id, model, config),
|
||||
|
||||
//delete: (id: string, config?: ApiRequestConfig) => del('countries/' + id, null, config),
|
||||
|
||||
//updatePassword: async model => await post(base + 'updatePassword', model),
|
||||
|
||||
//hashPassword: async model => await post(base + 'hashPassword', model),
|
||||
|
||||
//disable: async model => await post(base + 'disable', model),
|
||||
|
||||
//enable: async model => await post(base + 'enable', model)
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import api from './api';
|
||||
import plugin from './plugin';
|
||||
|
||||
export {
|
||||
api as userApi,
|
||||
plugin as userPlugin
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ZeroPlugin, ZeroPluginOptions } from '../../core';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
|
||||
export default {
|
||||
name: "zero.users",
|
||||
|
||||
install(app: ZeroPluginOptions)
|
||||
{
|
||||
//app.vue.component('ui-countrypicker', defineAsyncComponent(() => import('./ui-countrypicker.vue')));
|
||||
|
||||
app.route({ name: 'users', path: '/settings/users', component: () => import('./users.vue') });
|
||||
app.route({ name: 'users-edit', path: '/settings/users/edit/:id?', component: () => import('./user.vue'), props: true });
|
||||
|
||||
app.schema('users', () => import('./schemas/list'));
|
||||
app.schema('users:edit', () => import('./schemas/editor'));
|
||||
}
|
||||
} as ZeroPlugin;
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
import { ZeroEditor } from "../../../editor/editor";
|
||||
//import Permissions from 'zero/components/permissions.vue';
|
||||
|
||||
const editor = new ZeroEditor('users');
|
||||
|
||||
editor.resourcePrefix = '@user.fields.';
|
||||
|
||||
//const permissionsCount = x => (x.claims || []).filter(claim =>
|
||||
//{
|
||||
// const value = claim.value.split(':')[1];
|
||||
// return value !== 'none' && value !== 'false' && !!value;
|
||||
//}).length;
|
||||
|
||||
const general = editor.tab('general', '@ui.tab_general');
|
||||
const permissions = editor.tab('permissions', '@user.tab_permissions'); //, permissionsCount, x => !x.id);
|
||||
|
||||
general.field('name', { label: '@ui.name' }).text({ maxLength: 80 });
|
||||
general.field('email').text({ maxLength: 120 });
|
||||
general.field('passwordHash', { hidden: x => !x.id }).passwordHash();
|
||||
general.field('languageId').culturePicker();
|
||||
general.field('avatarId', { optional: true }).image();
|
||||
//permissions.field('claims', { hideLabel: true }).custom(Permissions);
|
||||
|
||||
export default editor;
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
|
||||
import List from 'zero/core/list.ts';
|
||||
import UsersApi from 'zero/api/users.js';
|
||||
import List from '../../../schemas/list/list';
|
||||
import api from '../api';
|
||||
|
||||
const list = new List('users');
|
||||
const prefix = '@user.fields.';
|
||||
@@ -9,13 +9,13 @@ list.templateLabel = x => prefix + x;
|
||||
list.link = x =>
|
||||
{
|
||||
return {
|
||||
name: zero.alias.settings.users + '-edit',
|
||||
name: 'users-edit',
|
||||
params: { id: x.id },
|
||||
query: { scope: 'shared' }
|
||||
};
|
||||
};
|
||||
|
||||
list.onFetch(filter => UsersApi.getAll(filter));
|
||||
list.onFetch(filter => api.getByQuery(filter));
|
||||
|
||||
list.column('avatarId', { width: 70, canSort: false, hideLabel: true }).image();
|
||||
list.column('name').name();
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<ui-form ref="form" class="user" v-slot="form" @submit="onSubmit" @load="onLoad" :route="route">
|
||||
<ui-form-header v-model:value="model" prefix="@user.users" title="@user.name" :disabled="disabled" :is-create="!id" :state="form.state" :can-delete="meta.canDelete" @delete="onDelete" />
|
||||
<ui-editor config="users:edit" v-model="model" :meta="meta" :disabled="disabled">
|
||||
<template v-slot:below>
|
||||
<ui-editor-infos v-model="model" :disabled="disabled" />
|
||||
</template>
|
||||
</ui-editor>
|
||||
</ui-form>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import api from './api';
|
||||
|
||||
export default {
|
||||
props: ['id'],
|
||||
|
||||
data: () => ({
|
||||
meta: {},
|
||||
model: { name: null },
|
||||
route: 'users-edit',
|
||||
disabled: false
|
||||
}),
|
||||
|
||||
methods: {
|
||||
|
||||
async onLoad(form)
|
||||
{
|
||||
const response = await form.load(() => this.id ? api.getById(this.id) : api.getEmpty(this.$route.query['zero.flavor']));
|
||||
this.model = response;
|
||||
},
|
||||
|
||||
|
||||
async onSubmit(form)
|
||||
{
|
||||
const response = this.id ? await api.update(this.model) : await api.create(this.model);
|
||||
await form.handle(response);
|
||||
},
|
||||
|
||||
|
||||
onDelete(item, opts)
|
||||
{
|
||||
opts.hide();
|
||||
this.$refs.form.onDelete(api.delete.bind(this, this.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="users">
|
||||
<ui-header-bar title="@user.users" :count="count" :back-button="true">
|
||||
<ui-table-filter :attach="$refs.table" />
|
||||
<ui-add-button :route="createRoute" alias="countries" />
|
||||
</ui-header-bar>
|
||||
<div class="ui-blank-box">
|
||||
<ui-table ref="table" config="users" @count="count = $event" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
data: () => ({
|
||||
count: 0,
|
||||
createRoute: 'users-edit'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.users .ui-table-field-image
|
||||
{
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.users .ui-table-cell[table-field="avatarId"]
|
||||
{
|
||||
padding: 12px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.users .ui-table-cell[table-field="name"]
|
||||
{
|
||||
border-left-color: transparent;
|
||||
}
|
||||
</style>
|
||||
@@ -200,7 +200,7 @@ class ListColumn
|
||||
{
|
||||
this._type = 'image';
|
||||
this._asHtml = true;
|
||||
this._func = (value, opts) => value ? `<img src="/zero/api/backoffice/ui/mediapreview/${(value)}" class="ui-table-field-image">` : '';
|
||||
this._func = (value, opts) => value ? `<img src="/zero/api/system/media/${(value)}/thumb.tmp" class="ui-table-field-image">` : '';
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { collection, get } from '../helpers/request.ts';
|
||||
|
||||
export default {
|
||||
...collection('applications/'),
|
||||
|
||||
getAllFeatures: async () => await get('applications/getAllFeatures')
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
import { get, post, del } from 'zero/helpers/request.ts';
|
||||
|
||||
const base = 'integrations/';
|
||||
|
||||
export default {
|
||||
getEmpty: async alias => await get(base + 'getEmpty', { params: { alias } }),
|
||||
getTypes: async () => await get(base + 'getTypes'),
|
||||
getByAlias: async (alias, config) => await get(base + 'getByAlias', { ...config, params: { alias } }),
|
||||
getByQuery: async (query, config) => await get(base + 'getByQuery', { ...config, params: { query } }),
|
||||
save: async (model, config) => await post(base + 'save', model, { ...config }),
|
||||
saveActiveState: async (model, config) => await post(base + 'saveActiveState', model, { ...config }),
|
||||
delete: async alias => await del(base + 'delete', { params: { alias } })
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import { get, post, del } from '../helpers/request.ts';
|
||||
|
||||
const base = 'spaces/';
|
||||
|
||||
export default {
|
||||
getByAlias: async alias => await get(base + 'getByAlias', { params: { alias } }),
|
||||
|
||||
getAll: async () => await get(base + 'getAll'),
|
||||
|
||||
getList: async (alias, query) =>
|
||||
{
|
||||
query.alias = alias;
|
||||
return await get(base + 'getList', { params: query })
|
||||
},
|
||||
|
||||
getContent: async (alias, contentId) => await get(base + 'getContent', { params: { alias, contentId } }),
|
||||
|
||||
save: async model => await post(base + 'save', model),
|
||||
|
||||
delete: async(alias, id) => await del(base + 'delete', { params: { alias, id } })
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
import { collection, post } from '../helpers/request.ts';
|
||||
|
||||
const base = 'users/';
|
||||
|
||||
export default {
|
||||
...collection(base),
|
||||
|
||||
updatePassword: async model => await post(base + 'updatePassword', model),
|
||||
|
||||
hashPassword: async model => await post(base + 'hashPassword', model),
|
||||
|
||||
disable: async model => await post(base + 'disable', model),
|
||||
|
||||
enable: async model => await post(base + 'enable', model)
|
||||
};
|
||||
@@ -1,155 +0,0 @@
|
||||
<template>
|
||||
<div class="page page-error">
|
||||
<ui-icon class="page-error-icon" symbol="fth-cloud-snow" :size="82" />
|
||||
<p class="page-error-text">
|
||||
<strong class="page-error-headline">Not found</strong><br>
|
||||
The requested resource could not be found
|
||||
<br>
|
||||
<code>{{path}}</code>
|
||||
</p>
|
||||
<ui-button class="page-error-button" type="light onbg" :label="detailsText" @click="details = !details" />
|
||||
<template v-if="details">
|
||||
<br><br>
|
||||
<div class="page-error-routes">
|
||||
<span>#</span>
|
||||
<span>Name</span>
|
||||
<span>Path</span>
|
||||
<template v-for="(route, index) in routes">
|
||||
<span>{{index + 1}}.</span>
|
||||
<b>{{route.name}}</b>
|
||||
<span>{{route.path}}</span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
data: () => ({
|
||||
path: null,
|
||||
routes: [],
|
||||
details: false
|
||||
}),
|
||||
|
||||
watch: {
|
||||
'$route': function (val)
|
||||
{
|
||||
this.rebuild();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
detailsText()
|
||||
{
|
||||
return this.details ? 'Hide' : 'Defined routes';
|
||||
}
|
||||
},
|
||||
|
||||
mounted()
|
||||
{
|
||||
this.rebuild();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
rebuild()
|
||||
{
|
||||
this.path = this.$router.options.base + this.$route.path.substring(1);
|
||||
this.routes = [];
|
||||
|
||||
this.$router.getRoutes().forEach(route =>
|
||||
{
|
||||
this.routes.push({
|
||||
path: route.path,
|
||||
name: route.name
|
||||
});
|
||||
|
||||
if (route.children)
|
||||
{
|
||||
route.children.forEach(child =>
|
||||
{
|
||||
this.routes.push({
|
||||
path: route.path + '/' + child.path,
|
||||
name: child.name
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.page-error
|
||||
{
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
padding: var(--padding);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.page-error-icon
|
||||
{
|
||||
color: var(--color-text);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-error-text
|
||||
{
|
||||
font-size: var(--font-size);
|
||||
color: var(--color-text-dim);
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
.page-error-headline
|
||||
{
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
font-size: var(--font-size-l);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.page-error-button
|
||||
{
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.page-error-routes
|
||||
{
|
||||
display: grid;
|
||||
grid-template-columns: auto auto 1fr;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
text-align: left;
|
||||
border-top: 1px solid var(--color-line-onbg);
|
||||
border-left: 1px solid var(--color-line-onbg);
|
||||
margin-top: 30px;
|
||||
|
||||
span, b
|
||||
{
|
||||
border: 1px solid var(--color-line-onbg);
|
||||
border-left: none;
|
||||
border-top: none;
|
||||
padding: 8px 10px 6px;
|
||||
font-family: Consolas;
|
||||
font-size: 12px;
|
||||
|
||||
&:nth-child(3n+1)
|
||||
{
|
||||
color: var(--color-text-dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
import application from './application.js';
|
||||
import country from './country.js';
|
||||
import language from './language.js';
|
||||
import media from './media.js';
|
||||
import translation from './translation.js';
|
||||
import user from './user.js';
|
||||
import userRole from './userRole.js';
|
||||
import mailTemplate from './mailTemplate.js';
|
||||
import pageFolder from './page-folder.js';
|
||||
|
||||
export default {
|
||||
application,
|
||||
country,
|
||||
language,
|
||||
media,
|
||||
translation,
|
||||
user,
|
||||
userRole,
|
||||
mailTemplate,
|
||||
pageFolder
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
import Editor from 'zero/core/editor.ts';
|
||||
import Permissions from 'zero/components/permissions.vue';
|
||||
|
||||
const editor = new Editor('user', '@user.fields.');
|
||||
editor.options.coreDatabase = true;
|
||||
|
||||
const permissionsCount = x => (x.claims || []).filter(claim =>
|
||||
{
|
||||
const value = claim.value.split(':')[1];
|
||||
return value !== 'none' && value !== 'false' && !!value;
|
||||
}).length;
|
||||
|
||||
const general = editor.tab('general', '@ui.tab_general');
|
||||
const permissions = editor.tab('permissions', '@user.tab_permissions', permissionsCount, x => !x.id);
|
||||
|
||||
general.field('name', { label: '@ui.name' }).text(80).required();
|
||||
general.fieldset(set =>
|
||||
{
|
||||
set.field('email').text(120).required();
|
||||
set.field('passwordHash').when(x => x.id).passwordHash().required();
|
||||
});
|
||||
general.field('languageId').culturePicker().required();
|
||||
general.field('avatarId').image();
|
||||
permissions.field('claims', { hideLabel: true }).custom(Permissions);
|
||||
|
||||
export default editor;
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
import countries from './countries.js';
|
||||
import languages from './languages.js';
|
||||
import translations from './translations.js';
|
||||
import users from './users.js';
|
||||
import mails from './mails.js';
|
||||
import recyclebin from './recyclebin.js';
|
||||
|
||||
export default {
|
||||
countries,
|
||||
languages,
|
||||
translations,
|
||||
users,
|
||||
mails,
|
||||
recyclebin
|
||||
};
|
||||
@@ -6,24 +6,23 @@ using System.Security.Claims;
|
||||
|
||||
namespace zero.Identity;
|
||||
|
||||
public class RavenRoleStore<TRole> : IRoleStore<TRole>, IRoleClaimStore<TRole>
|
||||
where TRole : ZeroIdentityRole
|
||||
public class RavenRoleStore<TRole> :
|
||||
EntityStore<TRole>,
|
||||
IRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : ZeroIdentityRole, new()
|
||||
{
|
||||
protected IZeroStore Store { get; private set; }
|
||||
|
||||
protected IZeroOptions Options { get; private set; }
|
||||
|
||||
protected IdentityErrorDescriber ErrorDescriber { get; private set; }
|
||||
|
||||
protected bool Global { get; private set; }
|
||||
|
||||
|
||||
public RavenRoleStore(IZeroStore store, IZeroOptions options, IdentityErrorDescriber describer = null, bool global = false)
|
||||
public RavenRoleStore(IStoreContext storeContext, IdentityErrorDescriber describer = null, bool global = false) : base(storeContext)
|
||||
{
|
||||
Store = store;
|
||||
Options = options;
|
||||
ErrorDescriber = describer ?? new IdentityErrorDescriber();
|
||||
Global = global;
|
||||
Config.Database = global ? Options.For<RavenOptions>().Database : null;
|
||||
Config.IncludeInactive = true;
|
||||
ErrorDescriber = describer ?? new IdentityErrorDescriber();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +35,7 @@ public class RavenRoleStore<TRole> : IRoleStore<TRole>, IRoleClaimStore<TRole>
|
||||
/// <inheritdoc/>
|
||||
public async Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
await session.StoreAsync(role, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Create(role);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
@@ -48,9 +45,7 @@ public class RavenRoleStore<TRole> : IRoleStore<TRole>, IRoleClaimStore<TRole>
|
||||
{
|
||||
try
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
await session.StoreAsync(role, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Update(role);
|
||||
}
|
||||
catch (ConcurrencyException)
|
||||
{
|
||||
@@ -65,9 +60,7 @@ public class RavenRoleStore<TRole> : IRoleStore<TRole>, IRoleClaimStore<TRole>
|
||||
{
|
||||
try
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
session.Delete(role);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Delete(role);
|
||||
}
|
||||
catch (ConcurrencyException)
|
||||
{
|
||||
@@ -107,14 +100,14 @@ public class RavenRoleStore<TRole> : IRoleStore<TRole>, IRoleClaimStore<TRole>
|
||||
/// <inheritdoc/>
|
||||
public async Task<TRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
return await ScopeQuery(Store.Session(Global).Query<TRole>()).FirstOrDefaultAsync(x => x.Id == roleId, cancellationToken);
|
||||
return await ScopeQuery(Session.Query<TRole>()).FirstOrDefaultAsync(x => x.Id == roleId, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
{
|
||||
return await ScopeQuery(Store.Session(Global).Query<TRole>()).FirstOrDefaultAsync(x => x.Name == normalizedRoleName, cancellationToken);
|
||||
return await ScopeQuery(Session.Query<TRole>()).FirstOrDefaultAsync(x => x.Name == normalizedRoleName, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
namespace zero.Identity;
|
||||
|
||||
public class RavenCoreRoleStore<TRole> : RavenRoleStore<TRole>
|
||||
where TRole : ZeroIdentityRole
|
||||
where TRole : ZeroIdentityRole, new()
|
||||
{
|
||||
public RavenCoreRoleStore(IZeroStore store, IZeroOptions options, IdentityErrorDescriber describer = null) : base(store, options, describer, true) { }
|
||||
public RavenCoreRoleStore(IStoreContext context, IdentityErrorDescriber describer = null) : base(context, describer, true) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class RavenCoreUserStore<TUser> : RavenUserStore<TUser>
|
||||
where TUser : ZeroIdentityUser
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
{
|
||||
public RavenCoreUserStore(IZeroStore store, IZeroOptions options) : base(store, options, true) { }
|
||||
public RavenCoreUserStore(IStoreContext context) : base(context, true) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class RavenCoreUserStore<TUser, TRole> : RavenUserStore<TUser, TRole>
|
||||
where TUser : ZeroIdentityUser
|
||||
where TRole : ZeroIdentityRole
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
where TRole : ZeroIdentityRole, new()
|
||||
{
|
||||
public RavenCoreUserStore(IZeroStore store, IZeroOptions options) : base(store, options, true) { }
|
||||
public RavenCoreUserStore(IStoreContext context) : base(context, true) { }
|
||||
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Linq;
|
||||
using Raven.Client.Documents.Session;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace zero.Identity;
|
||||
|
||||
public partial class RavenUserStore<TUser> :
|
||||
|
||||
public partial class RavenUserStore<TUser> :
|
||||
EntityStore<TUser>,
|
||||
IUserStore<TUser>,
|
||||
IUserEmailStore<TUser>,
|
||||
IUserLockoutStore<TUser>,
|
||||
@@ -14,20 +15,15 @@ public partial class RavenUserStore<TUser> :
|
||||
IUserClaimStore<TUser>,
|
||||
IUserSecurityStampStore<TUser>,
|
||||
IProtectedUserStore<TUser>
|
||||
where TUser : ZeroIdentityUser
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
{
|
||||
protected IZeroStore Store { get; private set; }
|
||||
|
||||
protected IZeroOptions Options { get; private set; }
|
||||
|
||||
protected bool Global { get; private set; }
|
||||
|
||||
|
||||
public RavenUserStore(IZeroStore store, IZeroOptions options, bool global = false)
|
||||
public RavenUserStore(IStoreContext storeContext, bool global = false) : base(storeContext)
|
||||
{
|
||||
Store = store;
|
||||
Options = options;
|
||||
Global = global;
|
||||
Global = global;
|
||||
Config.Database = global ? Options.For<RavenOptions>().Database : null;
|
||||
Config.IncludeInactive = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +41,9 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <summary>
|
||||
/// Whether an email is already reserved
|
||||
/// </summary>
|
||||
protected virtual async Task<bool> IsEmailReserved(IAsyncDocumentSession session, TUser user, CancellationToken cancellationToken = default)
|
||||
protected virtual async Task<bool> IsEmailReserved(TUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
TUser existingUser = await ScopeQuery(session.Query<TUser>()).FirstOrDefaultAsync(x => x.Email == user.Email, cancellationToken);
|
||||
TUser existingUser = await ScopeQuery(Session.Query<TUser>()).FirstOrDefaultAsync(x => x.Email == user.Email, cancellationToken);
|
||||
return existingUser != null && existingUser.Id != user.Id;
|
||||
}
|
||||
|
||||
@@ -55,9 +51,7 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
|
||||
if (await IsEmailReserved(session, user, cancellationToken))
|
||||
if (await IsEmailReserved(user, cancellationToken))
|
||||
{
|
||||
return IdentityResult.Failed(new IdentityError
|
||||
{
|
||||
@@ -66,9 +60,7 @@ public partial class RavenUserStore<TUser> :
|
||||
});
|
||||
}
|
||||
|
||||
await session.StoreAsync(user, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
|
||||
Result<TUser> result = await Create(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
@@ -76,13 +68,7 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
|
||||
TUser source = await session.LoadAsync<TUser>(user.Id, cancellationToken);
|
||||
|
||||
session.Delete(source);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
|
||||
Result result = await Delete(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
@@ -90,9 +76,7 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
using IZeroDocumentSession session = Store.Session(Global);
|
||||
using IAsyncDocumentSession newSession = Store.Session(Global, null, ZeroSessionResolution.Create, new SessionOptions() { NoCaching = true });
|
||||
TUser source = await newSession.LoadAsync<TUser>(user.Id, cancellationToken);
|
||||
TUser source = await Load(user.Id);
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
@@ -103,7 +87,7 @@ public partial class RavenUserStore<TUser> :
|
||||
});
|
||||
}
|
||||
|
||||
if (source.Email != user.Email && await IsEmailReserved(Store.Session(Global), user, cancellationToken))
|
||||
if (source.Email != user.Email && await IsEmailReserved(user, cancellationToken))
|
||||
{
|
||||
return IdentityResult.Failed(new IdentityError
|
||||
{
|
||||
@@ -112,8 +96,7 @@ public partial class RavenUserStore<TUser> :
|
||||
});
|
||||
}
|
||||
|
||||
await session.StoreAsync(user, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Update(user);
|
||||
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
@@ -122,14 +105,14 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
return await ScopeQuery(Store.Session(Global).Query<TUser>()).FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
return await ScopeQuery(Session.Query<TUser>()).FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
|
||||
{
|
||||
return await ScopeQuery(Store.Session(Global).Query<TUser>()).FirstOrDefaultAsync(x => x.Username == normalizedUserName, cancellationToken);
|
||||
return await ScopeQuery(Session.Query<TUser>()).FirstOrDefaultAsync(x => x.Username == normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +179,7 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
|
||||
{
|
||||
return await Store.Session(Global).Query<TUser>().FirstOrDefaultAsync(x => x.Email == normalizedEmail, cancellationToken);
|
||||
return await Session.Query<TUser>().FirstOrDefaultAsync(x => x.Email == normalizedEmail, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -320,10 +303,8 @@ public partial class RavenUserStore<TUser> :
|
||||
/// <inheritdoc />
|
||||
public async Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
user.Claims.AddRange(claims.Select(claim => new UserClaim(claim)));
|
||||
await session.StoreAsync(user, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Update(user);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,35 +319,30 @@ public partial class RavenUserStore<TUser> :
|
||||
public async Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
|
||||
{
|
||||
UserClaim userClaim = new(claim);
|
||||
return await ScopeQuery(Store.Session(Global).Query<TUser>()).Where(x => x.Claims.Any(c => c.Type == userClaim.Type && c.Value == userClaim.Value)).ToListAsync(token: cancellationToken);
|
||||
return await ScopeQuery(Session.Query<TUser>()).Where(x => x.Claims.Any(c => c.Type == userClaim.Type && c.Value == userClaim.Value)).ToListAsync(token: cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
IEnumerable<UserClaim> userClaims = claims.Select(c => new UserClaim(c)).ToList();
|
||||
|
||||
user.Claims = user.Claims.Except(userClaims, new UserClaimComparer()).ToList();
|
||||
|
||||
await session.StoreAsync(user, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
|
||||
await Update(user);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
|
||||
{
|
||||
IZeroDocumentSession session = Store.Session(Global);
|
||||
UserClaim userClaim = new(claim);
|
||||
UserClaim newUserClaim = new(newClaim);
|
||||
|
||||
user.Claims.Remove(userClaim);
|
||||
user.Claims.Add(newUserClaim);
|
||||
|
||||
await session.StoreAsync(user, cancellationToken);
|
||||
await session.SaveChangesAsync(cancellationToken);
|
||||
await Update(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace zero.Identity;
|
||||
|
||||
public partial class RavenUserStore<TUser, TRole> : RavenUserStore<TUser>,
|
||||
IUserRoleStore<TUser>
|
||||
where TUser : ZeroIdentityUser
|
||||
where TRole : ZeroIdentityRole
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
where TRole : ZeroIdentityRole, new()
|
||||
{
|
||||
public RavenUserStore(IZeroStore store, IZeroOptions options, bool global = false) : base(store, options, global) { }
|
||||
public RavenUserStore(IStoreContext context, bool global = false) : base(context, global) { }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -33,7 +33,7 @@ public partial class RavenUserStore<TUser, TRole> : RavenUserStore<TUser>,
|
||||
/// <inheritdoc />
|
||||
public async Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken)
|
||||
{
|
||||
return await ScopeQuery(Store.Session(Global).Query<TUser>()).Where(x => roleName.In(x.RoleIds)).ToListAsync();
|
||||
return await ScopeQuery(Session.Query<TUser>()).Where(x => roleName.In(x.RoleIds)).ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class UserService : IUserService
|
||||
UserManager = userManager;
|
||||
Operations = new StoreOperations(store.Context, store.Interceptors, store.Options, new()
|
||||
{
|
||||
IncludeInactive = false,
|
||||
IncludeInactive = true,
|
||||
Database = store.Options.For<RavenOptions>().Database
|
||||
});
|
||||
Context = context;
|
||||
|
||||
@@ -7,8 +7,8 @@ namespace zero.Identity;
|
||||
public static class ZeroIdentityExtensions
|
||||
{
|
||||
public static IdentityBuilder AddZeroIdentity<TUser, TRole>(this IServiceCollection services)
|
||||
where TUser : ZeroIdentityUser
|
||||
where TRole : ZeroIdentityRole
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
where TRole : ZeroIdentityRole, new()
|
||||
{
|
||||
services.AddZeroIdentityCore<TUser>();
|
||||
|
||||
@@ -29,7 +29,7 @@ public static class ZeroIdentityExtensions
|
||||
|
||||
|
||||
public static IdentityBuilder AddZeroIdentity<TUser>(this IServiceCollection services)
|
||||
where TUser : ZeroIdentityUser
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
{
|
||||
services.AddZeroIdentityCore<TUser>();
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class ZeroIdentityExtensions
|
||||
|
||||
|
||||
static IServiceCollection AddZeroIdentityCore<TUser>(this IServiceCollection services)
|
||||
where TUser : ZeroIdentityUser
|
||||
where TUser : ZeroIdentityUser, new()
|
||||
{
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddOptions();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace zero.Stores;
|
||||
public partial class StoreOperations : IStoreOperations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IZeroDocumentSession Session => Context.Store.Session(_overrideDatabase);
|
||||
public IZeroDocumentSession Session => Context.Store.Session(_overrideDatabase ?? Config.Database);
|
||||
|
||||
protected record OperationOptions(bool IncludeInactive);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using zero.Routing;
|
||||
|
||||
namespace zero.Web;
|
||||
@@ -8,6 +7,6 @@ public class ZeroFallbackController : ZeroController<PageRoute>
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return Json(new { Application, Route }, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None });
|
||||
return Json(new { Application, Route });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user