diff --git a/api/src/controllers/assets.ts b/api/src/controllers/assets.ts index bd36b4b650..5aa5bc0865 100644 --- a/api/src/controllers/assets.ts +++ b/api/src/controllers/assets.ts @@ -119,6 +119,10 @@ router.get( res.attachment(file.filename_download); res.setHeader('Content-Type', file.type); + if (req.query.hasOwnProperty('download') === false) { + res.removeHeader('Content-Disposition'); + } + stream.pipe(res); }) ); diff --git a/api/src/controllers/permissions.ts b/api/src/controllers/permissions.ts index ca8a6b91b3..568bae09b9 100644 --- a/api/src/controllers/permissions.ts +++ b/api/src/controllers/permissions.ts @@ -49,7 +49,7 @@ router.get( router.get( '/me', asyncHandler(async (req, res, next) => { - if (!req.accountability?.user || !req.accountability?.role) { + if (!req.accountability?.user) { throw new InvalidCredentialsException(); } diff --git a/api/src/controllers/users.ts b/api/src/controllers/users.ts index 2e0888141e..b3b7ce4eb1 100644 --- a/api/src/controllers/users.ts +++ b/api/src/controllers/users.ts @@ -58,11 +58,21 @@ router.get( if (!req.accountability?.user) { throw new InvalidCredentialsException(); } + const service = new UsersService({ accountability: req.accountability }); - const item = await service.readByKey(req.accountability.user, req.sanitizedQuery); + try { + const item = await service.readByKey(req.accountability.user, req.sanitizedQuery); + res.locals.payload = { data: item || null }; + } catch (error) { + if (error instanceof ForbiddenException) { + res.locals.payload = { data: { id: req.accountability.user } }; + return next(); + } + + throw error; + } - res.locals.payload = { data: item || null }; return next(); }), respond diff --git a/api/src/database/seeds/03-fields/05-files.yaml b/api/src/database/seeds/03-fields/05-files.yaml index 27554000c0..6d3a4aff64 100644 --- a/api/src/database/seeds/03-fields/05-files.yaml +++ b/api/src/database/seeds/03-fields/05-files.yaml @@ -101,7 +101,7 @@ fields: display: user - collection: directus_files field: modified_on - interface: dateTime + interface: datetime locked: true special: date-updated width: half @@ -111,4 +111,4 @@ fields: display: datetime - collection: directus_files field: created_by - display: user \ No newline at end of file + display: user diff --git a/api/src/utils/parse-filter.ts b/api/src/utils/parse-filter.ts index eef2d5aecd..ea4a1e4f64 100644 --- a/api/src/utils/parse-filter.ts +++ b/api/src/utils/parse-filter.ts @@ -7,7 +7,10 @@ export function parseFilter(filter: Filter, accountability: Accountability | nul if (val === 'true') return true; if (val === 'false') return false; - if (key === '_in' || key === '_nin') return toArray(val); + if (key === '_in' || key === '_nin') { + if (typeof val === 'string' && val.includes(',')) return val.split(','); + else return toArray(val); + } if (val === '$NOW') return new Date(); if (val === '$CURRENT_USER') return accountability?.user || null; diff --git a/api/src/utils/sanitize-query.ts b/api/src/utils/sanitize-query.ts index 9597ce5a2a..adae6792a3 100644 --- a/api/src/utils/sanitize-query.ts +++ b/api/src/utils/sanitize-query.ts @@ -1,6 +1,7 @@ import { Accountability, Query, Sort, Filter, Meta } from '../types'; import logger from '../logger'; import { parseFilter } from '../utils/parse-filter'; +import { flatten } from 'lodash'; export function sanitizeQuery( rawQuery: Record, @@ -75,6 +76,9 @@ function sanitizeFields(rawFields: any) { if (typeof rawFields === 'string') fields = rawFields.split(','); else if (Array.isArray(rawFields)) fields = rawFields as string[]; + // Case where array item includes CSV (fe fields[]=id,name): + fields = flatten(fields.map((field) => (field.includes(',') ? field.split(',') : field))); + return fields; } diff --git a/app/package-lock.json b/app/package-lock.json index 7dcd398b6e..cc1b6b1812 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -6601,51 +6601,6 @@ "tslint": "^5.20.1", "webpack": "^4.0.0", "yorkie": "^2.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fork-ts-checker-webpack-plugin-v5": { - "version": "npm:fork-ts-checker-webpack-plugin@5.2.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz", - "integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==", - "dev": true, - "optional": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - } - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "optional": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - } } }, "@vue/cli-plugin-unit-jest": { @@ -6785,17 +6740,6 @@ "unique-filename": "^1.1.1" } }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -6879,18 +6823,6 @@ "graceful-fs": "^4.1.6" } }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -7004,18 +6936,6 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, - "vue-loader-v16": { - "version": "npm:vue-loader@16.0.0-beta.8", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz", - "integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==", - "dev": true, - "optional": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - } - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -11744,6 +11664,51 @@ } } }, + "fork-ts-checker-webpack-plugin-v5": { + "version": "npm:fork-ts-checker-webpack-plugin@5.2.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.0.tgz", + "integrity": "sha512-NEKcI0+osT5bBFZ1SFGzJMQETjQWZrSvMO1g0nAR/w0t328Z41eN8BJEIZyFCl2HsuiJpa9AN474Nh2qLVwGLQ==", + "dev": true, + "optional": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "optional": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -20377,6 +20342,43 @@ } } }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.0.0-beta.8", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz", + "integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, "vue-router": { "version": "3.4.6", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.6.tgz", diff --git a/app/src/app.vue b/app/src/app.vue index aac919bf04..3d63279fbb 100644 --- a/app/src/app.vue +++ b/app/src/app.vue @@ -14,15 +14,7 @@ - - - - {{ $t('no_app_access_copy') }} - - - + @@ -88,7 +80,7 @@ export default defineComponent({ document.body.classList.remove('light'); document.body.classList.remove('auto'); - if (newUser !== undefined && newUser !== null) { + if (newUser !== undefined && newUser !== null && newUser.theme) { document.body.classList.add(newUser.theme); } else { // Default to light mode @@ -108,11 +100,6 @@ export default defineComponent({ return settingsStore.state?.settings?.custom_css || ''; }); - const appAccess = computed(() => { - if (!userStore.state.currentUser) return true; - return userStore.state.currentUser?.role?.app_access || false; - }); - const error = computed(() => appStore.state.error); /** @@ -124,7 +111,7 @@ export default defineComponent({ axios, }); - return { hydrating, brandStyle, appAccess, error, customCSS }; + return { hydrating, brandStyle, error, customCSS }; }, }); diff --git a/app/src/components/v-input/v-input.vue b/app/src/components/v-input/v-input.vue index b1802baa03..1ffa81823c 100644 --- a/app/src/components/v-input/v-input.vue +++ b/app/src/components/v-input/v-input.vue @@ -161,7 +161,7 @@ export default defineComponent({ } } - if (props.slug === true) { + if (props.dbSafe === true) { const dbSafeCharacters = 'abcdefghijklmnopqrstuvwxyz01234567890-_~ '.split(''); const isAllowed = dbSafeCharacters.includes(key) || systemKeys.includes(key); diff --git a/app/src/hydrate.ts b/app/src/hydrate.ts index 76909ba75a..603ed6a16a 100644 --- a/app/src/hydrate.ts +++ b/app/src/hydrate.ts @@ -57,11 +57,11 @@ export async function hydrate(stores = useStores()) { */ await userStore.hydrate(); - setLanguage((userStore.state.currentUser?.language as Language) || 'en-US'); - - await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.())); - - await registerModules(); + if (userStore.state.currentUser?.role) { + await setLanguage((userStore.state.currentUser?.language as Language) || 'en-US'); + await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.())); + await registerModules(); + } } catch (error) { appStore.state.error = error; } finally { diff --git a/app/src/interfaces/icon/icon.vue b/app/src/interfaces/icon/icon.vue index e9791ba2d1..5ad613c35d 100644 --- a/app/src/interfaces/icon/icon.vue +++ b/app/src/interfaces/icon/icon.vue @@ -91,6 +91,8 @@ export default defineComponent({ }; function setIcon(icon: string | null) { + searchQuery.value = ''; + emit('input', icon); } }, diff --git a/app/src/interfaces/wysiwyg/tinymce-overrides.css b/app/src/interfaces/wysiwyg/tinymce-overrides.css index b5029b72fa..716e9a1276 100644 --- a/app/src/interfaces/wysiwyg/tinymce-overrides.css +++ b/app/src/interfaces/wysiwyg/tinymce-overrides.css @@ -1,5 +1,9 @@ /* stylelint-disable font-family-no-missing-generic-family-keyword */ +.tox { + font-family: var(--family-sans-serif); +} + .tox .tox-tbtn { margin: 2px 2px 4px 0; color: var(--foreground-normal); @@ -138,8 +142,9 @@ body.dark .tox .tox-toolbar__overflow { } .tox .tox-dialog__header { - padding: 16px 24px 0 24px; + padding: 20px; color: var(--foreground-normal); + font-size: 16px; background-color: var(--background-page); } @@ -156,18 +161,61 @@ body.dark .tox .tox-toolbar__overflow { } .tox .tox-textfield, +.tox .tox-listboxfield .tox-listbox, .tox .tox-toolbar-textfield, .tox .tox-selectfield select, .tox .tox-textarea { padding: 12px; color: var(--foreground-normal); - font-family: monospace; + font-weight: 500; + font-size: 14px; + font-family: var(--family-sans-serif); background-color: var(--background-page); border: 2px solid var(--border-normal); border-radius: var(--border-radius); transition: var(--fast) var(--transition); } +.tox .tox-textarea { + font-family: var(--family-monospace); +} + +.tox .tox-textfield:focus, +.tox .tox-listboxfield .tox-listbox:focus, +.tox .tox-toolbar-textfield:focus, +.tox .tox-selectfield select:focus, +.tox .tox-textarea:focus { + background-color: var(--background-page); +} + +.tox .tox-menu { + box-sizing: border-box; + padding: 4px !important; + color: var(--foreground-normal); + font-family: var(--family-sans-serif); + background-color: var(--background-subdued); + border: 2px solid var(--border-normal); + border-radius: var(--border-radius); +} + +.tox .tox-collection__item { + border-radius: var(--border-radius); +} + +.tox .tox-collection--list .tox-collection__item { + color: var(--foreground-normal); +} + +.tox .tox-collection--list .tox-collection__item--active { + color: var(--foreground-normal) !important; + background-color: var(--background-page) !important; +} + +.tox .tox-collection--list .tox-collection__item--enabled { + color: var(--foreground-normal); + background-color: var(--background-page); +} + .tox .tox-textfield:focus, .tox .tox-selectfield select:focus, .tox .tox-textarea:focus { @@ -184,6 +232,7 @@ body.dark .tox .tox-toolbar__overflow { color: var(--white); font-weight: 500; font-size: 16px; + font-family: var(--family-sans-serif); line-height: 19px; background-color: var(--primary); border: 2px solid var(--primary); @@ -228,12 +277,45 @@ body.dark .tox .tox-toolbar__overflow { } .tox .tox-form__group { - margin-top: 24px; + margin-bottom: 24px; } .tox .tox-label, .tox .tox-toolbar-label { - margin-bottom: 10px; + margin-bottom: 4px; color: var(--foreground-normal); - font-size: 14px; + font-weight: 500; + font-size: 16px; } + +.tox .tox-dialog__body-nav-item { + box-sizing: border-box; + width: 100%; + margin-bottom: 4px; + padding: 12px 16px; + color: var(--foreground-normal); + font-weight: 500; + font-size: 14px; + border-bottom: none; + border-radius: var(--border-radius); + transition: var(--fast) var(--transition); + transition-property: background-color, color; +} + +.tox .tox-dialog__body-nav-item:hover { + background-color: var(--background-normal-alt); +} + +.tox .tox-dialog__body-nav-item--active { + background-color: var(--background-normal-alt); +} + +.tox .tox-dialog__body-nav-item--active:focus { + background-color: var(--background-normal-alt); +} + +@media screen and (max-width: 767px) { + .tox .tox-dialog__body-nav-item { + text-align: center; + } +} \ No newline at end of file diff --git a/app/src/lang/en-US/index.json b/app/src/lang/en-US/index.json index 3a792830e2..fad743f221 100644 --- a/app/src/lang/en-US/index.json +++ b/app/src/lang/en-US/index.json @@ -162,8 +162,8 @@ "create_field": "Create Field", "update_field": "Update Field", - "creating_new_field": "{collection}: New Field", - "updating_field_field": "{collection}: \"{field}\" Field", + "creating_new_field": "New Field ({collection})", + "updating_field_field": "{field} ({collection})", "within_collection": "Within {collection}", "field_standard": "Standard", diff --git a/app/src/modules/activity/components/navigation.vue b/app/src/modules/activity/components/navigation.vue index 3b4eeb62e7..618fe17928 100644 --- a/app/src/modules/activity/components/navigation.vue +++ b/app/src/modules/activity/components/navigation.vue @@ -49,7 +49,7 @@ - + {{ $t('comment') }} diff --git a/app/src/modules/docs/components/markdown.vue b/app/src/modules/docs/components/markdown.vue index 2437b10e18..6e10a3025e 100644 --- a/app/src/modules/docs/components/markdown.vue +++ b/app/src/modules/docs/components/markdown.vue @@ -338,10 +338,14 @@ export default defineComponent({ table tr { margin: 0; padding: 0; - background-color: white; + background-color: var(--background-normal); border-top: 1px solid var(--background-normal); } + table thead tr { + background-color: var(--background-normal-alt); + } + table tr:nth-child(2n) { background-color: var(--background-page); } diff --git a/app/src/modules/files/components/navigation.vue b/app/src/modules/files/components/navigation.vue index a83a327841..a2af1e58cc 100644 --- a/app/src/modules/files/components/navigation.vue +++ b/app/src/modules/files/components/navigation.vue @@ -34,7 +34,7 @@ - + {{ $t('my_files') }} diff --git a/app/src/modules/files/routes/item.vue b/app/src/modules/files/routes/item.vue index 61288d4a08..35a624ae57 100644 --- a/app/src/modules/files/routes/item.vue +++ b/app/src/modules/files/routes/item.vue @@ -395,7 +395,7 @@ export default defineComponent({ } function downloadFile() { - const filePath = getRootPath() + `assets/${props.primaryKey}`; + const filePath = getRootPath() + `assets/${props.primaryKey}?download`; window.open(filePath, '_blank'); } diff --git a/app/src/modules/settings/routes/data-model/fields/components/field-select.vue b/app/src/modules/settings/routes/data-model/fields/components/field-select.vue index 618753e0b1..8cfdc0fab7 100644 --- a/app/src/modules/settings/routes/data-model/fields/components/field-select.vue +++ b/app/src/modules/settings/routes/data-model/fields/components/field-select.vue @@ -49,7 +49,15 @@