diff --git a/zero.Core/Api/MediaApi.cs b/zero.Core/Api/MediaApi.cs
index de128717..12d3a70c 100644
--- a/zero.Core/Api/MediaApi.cs
+++ b/zero.Core/Api/MediaApi.cs
@@ -46,13 +46,13 @@ namespace zero.Core.Api
///
- public async Task GetSourceById(string id, bool thumb = false, bool enforceAppId = true)
+ public async Task GetSourceById(string id, bool thumb = false, bool isCoreDatabase = false)
{
- using IAsyncDocumentSession session = Store.OpenAsyncSession();
+ using IAsyncDocumentSession session = isCoreDatabase ? Store.OpenCoreSession() : Store.OpenAsyncSession();
IMedia media = await session.LoadAsync(id);
- if (media == null) //|| (enforceAppId && media.AppId != Constants.Database.SharedAppId && media.AppId != Scope.AppId)) // TODO appx fix
+ if (media == null)
{
return null;
}
@@ -280,7 +280,7 @@ namespace zero.Core.Api
///
/// Get media source by Id
///
- Task GetSourceById(string id, bool thumb = false, bool enforceAppId = true);
+ Task GetSourceById(string id, bool thumb = false, bool isCoreDatabase = false);
///
/// Get media by ids
diff --git a/zero.Web.UI/App/components/tables/table-filter.vue b/zero.Web.UI/App/components/tables/table-filter.vue
index 0b589aef..763af953 100644
--- a/zero.Web.UI/App/components/tables/table-filter.vue
+++ b/zero.Web.UI/App/components/tables/table-filter.vue
@@ -134,6 +134,7 @@
else
{
this.currentFilter = JSON.parse(JSON.stringify(value));
+ this.currentFilter.filter.id = this.currentFilter.id;
this.$emit('filter', this.currentFilter.filter);
this.attach.setFilter(this.currentFilter.filter);
}
@@ -157,7 +158,7 @@
// persist a filter in local storage
saveFilter(name, filter, id)
{
- id = id || Strings.guid();
+ id = id || Strings.guid(4);
let savedFilter = this.storedFilters.find(x => x.id === id);
let index = this.storedFilters.indexOf(savedFilter);
diff --git a/zero.Web.UI/App/components/tables/table.vue b/zero.Web.UI/App/components/tables/table.vue
index 4f1129bb..d0639623 100644
--- a/zero.Web.UI/App/components/tables/table.vue
+++ b/zero.Web.UI/App/components/tables/table.vue
@@ -4,7 +4,7 @@
-
@@ -42,7 +42,8 @@
import './table.scss'
import UiPagination from 'zero/components/pagination.vue';
import TableValue from './table-value.js';
- import { debounce as _debounce } from 'underscore';
+ import { debounce as _debounce, isEqual as _isEqual } from 'underscore';
+ import Qs from 'qs';
export default {
name: 'uiTable',
@@ -88,12 +89,9 @@
},
watch: {
- query: {
- deep: true,
- handler: function (val)
- {
- if (this.loaded) this.debouncedUpdate();
- }
+ 'query.search': function (val)
+ {
+ if (this.loaded) this.onChange();
},
$route(to, from)
{
@@ -116,7 +114,7 @@
flex: column.options.width ? { 'flex': '0 1 ' + column.options.width + 'px' } : {}
};
});
- this.query = { ...this.listConfig.query };
+ this.query = { ...this.listConfig.query, ...this.listConfig.queryToParams(this.$route.query) };
this.component = typeof !!this.listConfig.link ? 'router-link' : 'div';
this.filter = { ...this.listConfig.filterOptions };
this.$nextTick(() =>
@@ -167,6 +165,17 @@
},
+ // a property has changed and therefore we update the URL and table content
+ onChange()
+ {
+ const query = Qs.stringify(this.listConfig.paramsToQuery(this.query));
+ const path = this.$route.path + (query ? '?' + query : '');
+
+ history.replaceState(null, null, path);
+ this.debouncedUpdate();
+ },
+
+
getLink(item)
{
if (!this.listConfig.link)
@@ -190,14 +199,14 @@
setPage(index)
{
this.query.page = index;
- this.debouncedUpdate();
+ this.onChange();
},
// set a new filter
setFilter(filter)
{
this.query.filter = filter;
- this.debouncedUpdate();
+ this.onChange();
},
// sort by a column
@@ -217,7 +226,10 @@
this.query.orderIsDescending = true;
}
- this.debouncedUpdate();
+ // reset the page on sorting change
+ this.query.page = 1;
+
+ this.onChange();
},
// toggle selection of an item
diff --git a/zero.Web.UI/App/editor/editor.vue b/zero.Web.UI/App/editor/editor.vue
index 755eed9a..50611785 100644
--- a/zero.Web.UI/App/editor/editor.vue
+++ b/zero.Web.UI/App/editor/editor.vue
@@ -70,6 +70,7 @@
loaded: false,
hasTabs: false,
asBoxes: false,
+ coreDatabase: false,
tabs: []
}),
@@ -101,6 +102,7 @@
}
this.asBoxes = this.editorConfig.options.boxes;
+ this.coreDatabase = this.editorConfig.options.coreDatabase;
this.onConfigure(this);
this.loaded = true;
diff --git a/zero.Web.UI/App/navigation.vue b/zero.Web.UI/App/navigation.vue
index 4f9c0f4e..e92dd5ec 100644
--- a/zero.Web.UI/App/navigation.vue
+++ b/zero.Web.UI/App/navigation.vue
@@ -105,7 +105,7 @@
if (user && user.avatarId)
{
- this.userAvatar = MediaApi.getImageSource(user.avatarId, false, false);
+ this.userAvatar = MediaApi.getImageSource(user.avatarId, false, true);
}
else
{
diff --git a/zero.Web.UI/App/renderers/editors/application.js b/zero.Web.UI/App/renderers/editors/application.js
index fa7069d2..5996bd36 100644
--- a/zero.Web.UI/App/renderers/editors/application.js
+++ b/zero.Web.UI/App/renderers/editors/application.js
@@ -3,6 +3,7 @@ import Editor from 'zero/core/editor.ts';
import ApplicationFeatures from 'zero/pages/settings/application-features.vue';
const editor = new Editor('application', '@application.fields.');
+editor.options.coreDatabase = true;
const general = editor.tab('general', '@ui.tab_general');
const domains = editor.tab('domains', '@application.tab_domains', x => x.domains.length);
diff --git a/zero.Web.UI/App/renderers/editors/user.js b/zero.Web.UI/App/renderers/editors/user.js
index faa108e7..cadba146 100644
--- a/zero.Web.UI/App/renderers/editors/user.js
+++ b/zero.Web.UI/App/renderers/editors/user.js
@@ -3,6 +3,7 @@ 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 =>
{
diff --git a/zero.Web.UI/App/renderers/editors/userRole.js b/zero.Web.UI/App/renderers/editors/userRole.js
index 5beec5fd..e65dd9e9 100644
--- a/zero.Web.UI/App/renderers/editors/userRole.js
+++ b/zero.Web.UI/App/renderers/editors/userRole.js
@@ -3,6 +3,7 @@ import Editor from 'zero/core/editor.ts';
import Permissions from 'zero/components/permissions.vue';
const editor = new Editor('userRole', '@role.fields.');
+editor.options.coreDatabase = true;
const permissionsCount = x => (x.claims || []).filter(claim =>
{
diff --git a/zero.Web.UI/App/resources/media.js b/zero.Web.UI/App/resources/media.js
index 3df0ae0f..6f76eab0 100644
--- a/zero.Web.UI/App/resources/media.js
+++ b/zero.Web.UI/App/resources/media.js
@@ -35,7 +35,7 @@ var api = base =>
},
// get path to thumbnail source
- getImageSource(id, thumb, enforceAppId)
+ getImageSource(id, thumb, core)
{
if (!id || id.indexOf('http') === 0)
{
@@ -45,7 +45,7 @@ var api = base =>
{
return id.substring(6) + "?preset=productListing";
}
- return zero.apiPath + base + 'streamThumbnail/?id=' + id + (typeof thumb === 'boolean' ? '&thumb=' + (thumb ? 'true' : 'false') : '') + (enforceAppId === false ? '&enforceAppId=false' : '');
+ return zero.apiPath + base + 'streamThumbnail/?id=' + id + (typeof thumb === 'boolean' ? '&thumb=' + (thumb ? 'true' : 'false') : '') + (core === true ? '&core=true' : '');
},
// save a media
diff --git a/zero.Web.UI/App/services/strings.js b/zero.Web.UI/App/services/strings.js
index 30c550f0..b72e34b0 100644
--- a/zero.Web.UI/App/services/strings.js
+++ b/zero.Web.UI/App/services/strings.js
@@ -10,11 +10,18 @@ export default {
///
/// Generate a GUID
///
- guid()
+ guid(length)
{
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
+ var guid = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
+
+ if (length > 0)
+ {
+ return guid.replace(/-/g, '').substring(0, length);
+ }
+
+ return guid;
},
///
diff --git a/zero.Web.UI/app/core/editor-field.ts b/zero.Web.UI/app/core/editor-field.ts
index dd3f8c82..90e22200 100644
--- a/zero.Web.UI/app/core/editor-field.ts
+++ b/zero.Web.UI/app/core/editor-field.ts
@@ -38,6 +38,7 @@ class EditorField
condition: null,
disabled: false,
tab: null,
+ coreDatabase: false,
class: ''
};
diff --git a/zero.Web.UI/app/core/editor.ts b/zero.Web.UI/app/core/editor.ts
index ec4b93e6..9b44f520 100644
--- a/zero.Web.UI/app/core/editor.ts
+++ b/zero.Web.UI/app/core/editor.ts
@@ -20,7 +20,8 @@ class Editor
options = {
disabled: false,
- boxes: false
+ boxes: false,
+ coreDatabase: true
};
@@ -90,22 +91,29 @@ class Editor
* @param {string} [options.helpText] - Display a help text below the field
* @param {boolean|function} [options.condition] - Conditionally hide the field
* @param {boolean|function} [options.disabled=false] - Conditionally disable the field
+ * @param {boolean} [options.coreDatabase] - Operate on the core database for this field (default is set by Editor.options.coreDatabase)
* @param {string|object} [options.tab] - Add this field to a tab (by passing the alias or the tab instance)
* @param {string} [options.class] - Append HTML class to the generated property
* @returns {EditorField}
*/
field(path, options)
{
+ options = options || {};
+
let field = this.fields.find(x => x.path === path);
if (!field)
{
+ if (typeof options.coreDatabase === 'undefined')
+ {
+ options.coreDatabase = this.options.coreDatabase;
+ }
field = new EditorField(path, options);
this.fields.push(field);
}
else
{
- field.options = { ...field.options, ...(options || {}) };
+ field.options = { ...field.options, ...options };
}
return field;
diff --git a/zero.Web.UI/app/core/list.ts b/zero.Web.UI/app/core/list.ts
index a5882a96..8907bdc4 100644
--- a/zero.Web.UI/app/core/list.ts
+++ b/zero.Web.UI/app/core/list.ts
@@ -42,6 +42,67 @@ class List
actions = [];
+ /**
+ * Converts the list parameters (like page, search, filter, ...) to a vue router query
+ */
+ paramsToQuery = params =>
+ {
+ let values: any = {};
+
+ if (params.page !== this.query.page)
+ {
+ values.page = params.page;
+ }
+ if (params.isDescending !== this.query.isDescending || params.orderBy !== this.query.orderBy)
+ {
+ values.by = params.orderBy || this.query.orderBy;
+ values.desc = params.isDescending || this.query.isDescending;
+ }
+ if (!!params.search)
+ {
+ values.search = params.search;
+ }
+ if (params.filter && params.filter.id)
+ {
+ values.filter = params.filter.id;
+ }
+ return values;
+ };
+
+ /**
+ * Converts an URL query string to list parameters
+ */
+ queryToParams = query =>
+ {
+ if (!query)
+ {
+ return {};
+ }
+ let values = JSON.parse(JSON.stringify(this.query));
+ if (query.page)
+ {
+ values.page = +query.page || this.query.page;
+ }
+ if (query.by)
+ {
+ values.orderBy = query.by;
+ }
+ if (query.desc)
+ {
+ values.isDescending = query.desc === "true" || query.desc === true;
+ }
+ if (query.search)
+ {
+ values.search = query.search;
+ }
+ if (query.filter)
+ {
+ values.filter = values.filter || {};
+ values.filter.id = query.filter;
+ }
+ return values;
+ };
+
constructor(alias)
{
diff --git a/zero.Web/Controllers/MediaController.cs b/zero.Web/Controllers/MediaController.cs
index 1db57943..d6fe7c29 100644
--- a/zero.Web/Controllers/MediaController.cs
+++ b/zero.Web/Controllers/MediaController.cs
@@ -81,9 +81,9 @@ namespace zero.Web.Controllers
[HttpGet]
- public async Task StreamThumbnail([FromQuery] string id, [FromQuery] bool thumb = true, [FromQuery] bool enforceAppId = true)
+ public async Task StreamThumbnail([FromQuery] string id, [FromQuery] bool thumb = true, [FromQuery] bool core = false)
{
- string path = await Api.GetSourceById(id, thumb, enforceAppId);
+ string path = await Api.GetSourceById(id, thumb, core);
if (path == null)
{