mirror of
https://github.com/directus/directus.git
synced 2026-04-03 03:00:39 -04:00
Add ability to share items with people outside the platform (#10663)
* Add directus_shares * Don't check for usage limit on refresh * Add all endpoints to the shares controller * Move route `/auth/shared` to `/shared/auth` * Add password protection * Add `share` action in permissions * Add `shares/:pk/info` * Start on shared-view * Add basic styling for full shared view * Fixed migrations * Add inline style for shared view * Allow title override * Finish /info endpoint for shares * Add basic UUID validation to share/info endpont * Add UUID validation to other routes * Add not found state * Cleanup /extract/finish share login endpoint * Cleanup auth * Added `share_start` and `share_end` * Add share sidebar details. * Allow share permissions configuration * Hide the `new_share` button for unauthorized users * Fix uses_left displayed value * Show expired / upcoming shares * Improved expired/upcoming styling * Fixed share login query * Fix check-ip and get-permissions middlewares behaviour when role is null * Simplify cache key * Fix typescript linting issues * Handle app auth flow for shared page * Fixed /users/me response * Show when user is authenticated * Try showing item drawer in shared page * Improved shared card styling * Add shares permissions and change share card styling * Pull in schema/permissions on share * Create getPermissionForShare file * Change getPermissionsForShare signature * Render form + item on share after auth * Finalize public front end * Handle fake o2m field in applyQuery * [WIP] * New translations en-US.yaml (Bulgarian) (#10585) * smaller label height (#10587) * Update to the latest Material Icons (#10573) The icons are based on https://fonts.google.com/icons * New translations en-US.yaml (Arabic) (#10593) * New translations en-US.yaml (Arabic) (#10594) * New translations en-US.yaml (Portuguese, Brazilian) (#10604) * New translations en-US.yaml (French) (#10605) * New translations en-US.yaml (Italian) (#10613) * fix M2A list not updating (#10617) * Fix filters * Add admin filter on m2o role selection * Add admin filter on m2o role selection * Add o2m permissions traversing * Finish relational tree permissions generation * Handle implicit a2o relation * Update implicit relation regex * Fix regex * Fix implicitRelation unnesting for new regex * Fix implicitRelation length check * Rename m2a to a2o internally * Add auto-gen permissions for a2o * [WIP] Improve share UX * Add ctx menu options * Add share dialog * Add email notifications * Tweak endpoint * Tweak file interface disabled state * Add nicer invalid state to password input * Dont return info for expired/upcoming shares * Tweak disabled state for relational interfaces * Fix share button for non admin roles * Show/hide edit/delete based on permissions to shares * Fix imports of mutationtype * Resolve (my own) suggestions * Fix migration for ms sql * Resolve last suggestion Co-authored-by: Oreilles <oreilles.github@nitoref.io> Co-authored-by: Oreilles <33065839+oreilles@users.noreply.github.com> Co-authored-by: Ben Haynes <ben@rngr.org> Co-authored-by: Thien Nguyen <72242664+tatthien@users.noreply.github.com> Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>
This commit is contained in:
38
api/src/database/migrations/20211211A-add-shares.ts
Normal file
38
api/src/database/migrations/20211211A-add-shares.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.createTable('directus_shares', (table) => {
|
||||
table.uuid('id').primary();
|
||||
table.string('name');
|
||||
table.string('collection', 64).references('collection').inTable('directus_collections').onDelete('CASCADE');
|
||||
table.string('item');
|
||||
table.uuid('role').references('id').inTable('directus_roles').onDelete('CASCADE');
|
||||
table.string('password');
|
||||
table.uuid('user_created').references('id').inTable('directus_users').onDelete('SET NULL');
|
||||
table.timestamp('date_created').defaultTo(knex.fn.now());
|
||||
table.timestamp('date_start');
|
||||
table.timestamp('date_end');
|
||||
table.integer('times_used').defaultTo(0);
|
||||
table.integer('max_uses');
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('directus_sessions', (table) => {
|
||||
table.dropColumn('data');
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('directus_sessions', (table) => {
|
||||
table.uuid('user').nullable().alter();
|
||||
table.uuid('share').references('id').inTable('directus_shares').onDelete('CASCADE');
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable('directus_sessions', (table) => {
|
||||
table.uuid('user').notNullable().alter();
|
||||
table.json('data');
|
||||
table.dropForeign('share');
|
||||
table.dropColumn('share');
|
||||
});
|
||||
|
||||
await knex.schema.dropTable('directus_shares');
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export default async function runAST(
|
||||
|
||||
const knex = options?.knex || getDatabase();
|
||||
|
||||
if (ast.type === 'm2a') {
|
||||
if (ast.type === 'a2o') {
|
||||
const results: { [collection: string]: null | Item | Item[] } = {};
|
||||
|
||||
for (const collection of ast.names) {
|
||||
@@ -141,7 +141,7 @@ async function parseCurrentLevel(
|
||||
columnsToSelectInternal.push(child.fieldKey);
|
||||
}
|
||||
|
||||
if (child.type === 'm2a') {
|
||||
if (child.type === 'a2o') {
|
||||
columnsToSelectInternal.push(child.relation.field);
|
||||
columnsToSelectInternal.push(child.relation.meta!.one_collection_field!);
|
||||
}
|
||||
@@ -263,7 +263,7 @@ function applyParentFilters(
|
||||
} else {
|
||||
nestedNode.query.union = [foreignField, foreignIds];
|
||||
}
|
||||
} else if (nestedNode.type === 'm2a') {
|
||||
} else if (nestedNode.type === 'a2o') {
|
||||
const keysPerCollection: { [collection: string]: (string | number)[] } = {};
|
||||
|
||||
for (const parentItem of parentItems) {
|
||||
@@ -346,7 +346,7 @@ function mergeWithParentItems(
|
||||
|
||||
parentItem[nestedNode.fieldKey] = itemChildren.length > 0 ? itemChildren : [];
|
||||
}
|
||||
} else if (nestedNode.type === 'm2a') {
|
||||
} else if (nestedNode.type === 'a2o') {
|
||||
for (const parentItem of parentItems) {
|
||||
if (!nestedNode.relation.meta?.one_collection_field) {
|
||||
parentItem[nestedNode.fieldKey] = null;
|
||||
@@ -381,7 +381,7 @@ function removeTemporaryFields(
|
||||
const rawItems = cloneDeep(toArray(rawItem));
|
||||
const items: Item[] = [];
|
||||
|
||||
if (ast.type === 'm2a') {
|
||||
if (ast.type === 'a2o') {
|
||||
const fields: Record<string, string[]> = {};
|
||||
const nestedCollectionNodes: Record<string, NestedCollectionNode[]> = {};
|
||||
|
||||
|
||||
@@ -13,18 +13,6 @@
|
||||
comment:
|
||||
_nnull: true
|
||||
|
||||
- collection: directus_collections
|
||||
action: read
|
||||
|
||||
- collection: directus_fields
|
||||
action: read
|
||||
|
||||
- collection: directus_permissions
|
||||
action: read
|
||||
permissions:
|
||||
role:
|
||||
_eq: $CURRENT_ROLE
|
||||
|
||||
- collection: directus_presets
|
||||
action: read
|
||||
permissions:
|
||||
@@ -60,9 +48,6 @@
|
||||
user:
|
||||
_eq: $CURRENT_USER
|
||||
|
||||
- collection: directus_relations
|
||||
action: read
|
||||
|
||||
- collection: directus_roles
|
||||
action: read
|
||||
permissions:
|
||||
|
||||
@@ -11,6 +11,10 @@ const defaults: Partial<Permission> = {
|
||||
system: true,
|
||||
};
|
||||
|
||||
const schemaPermissionsRaw = requireYAML(require.resolve('./schema-access-permissions.yaml')) as Permission[];
|
||||
const permissions = requireYAML(require.resolve('./app-access-permissions.yaml')) as Permission[];
|
||||
|
||||
export const appAccessMinimalPermissions: Permission[] = permissions.map((row) => merge({}, defaults, row));
|
||||
export const schemaPermissions: Permission[] = schemaPermissionsRaw.map((row) => merge({}, defaults, row));
|
||||
export const appAccessMinimalPermissions: Permission[] = [...schemaPermissions, ...permissions].map((row) =>
|
||||
merge({}, defaults, row)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# NOTE: Activity/collections/fields/presets/relations/revisions will have an extra hardcoded filter
|
||||
# to filter out collections you don't have read access
|
||||
|
||||
- collection: directus_collections
|
||||
action: read
|
||||
|
||||
- collection: directus_fields
|
||||
action: read
|
||||
|
||||
- collection: directus_permissions
|
||||
action: read
|
||||
permissions:
|
||||
role:
|
||||
_eq: $CURRENT_ROLE
|
||||
|
||||
- collection: directus_relations
|
||||
action: read
|
||||
@@ -65,3 +65,6 @@ data:
|
||||
note: $t:directus_collection.directus_panels
|
||||
- collection: directus_notifications
|
||||
note: $t:directus_collection.directus_notifications
|
||||
- collection: directus_shares
|
||||
icon: share
|
||||
note: $t:directus_collection.directus_shares
|
||||
|
||||
@@ -11,4 +11,4 @@ fields:
|
||||
width: half
|
||||
- field: user_agent
|
||||
width: half
|
||||
- field: data
|
||||
- field: share
|
||||
|
||||
73
api/src/database/system-data/fields/shares.yaml
Normal file
73
api/src/database/system-data/fields/shares.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
table: directus_shares
|
||||
|
||||
fields:
|
||||
- field: id
|
||||
special: uuid
|
||||
readonly: true
|
||||
hidden: true
|
||||
|
||||
- field: name
|
||||
|
||||
- field: collection
|
||||
width: half
|
||||
hidden: true
|
||||
|
||||
- field: item
|
||||
width: half
|
||||
hidden: true
|
||||
|
||||
- field: role
|
||||
interface: select-dropdown-m2o
|
||||
width: half
|
||||
options:
|
||||
template: '{{name}}'
|
||||
filter:
|
||||
admin_access:
|
||||
_eq: false
|
||||
|
||||
- field: password
|
||||
special: hash,conceal
|
||||
interface: input-hash
|
||||
options:
|
||||
iconRight: lock
|
||||
masked: true
|
||||
width: half
|
||||
|
||||
- field: date_start
|
||||
width: half
|
||||
|
||||
- field: date_end
|
||||
width: half
|
||||
|
||||
- field: max_uses
|
||||
width: half
|
||||
|
||||
- field: times_used
|
||||
width: half
|
||||
readonly: true
|
||||
|
||||
- field: date_created
|
||||
special: date-created
|
||||
width: half
|
||||
readonly: true
|
||||
conditions:
|
||||
- name: notCreatedYet
|
||||
rule:
|
||||
id:
|
||||
_null: true
|
||||
hidden: true
|
||||
|
||||
- field: user_created
|
||||
special: user-created
|
||||
interface: select-dropdown-m2o
|
||||
width: half
|
||||
display: user
|
||||
options:
|
||||
template: '{{avatar.$thumbnail}} {{first_name}} {{last_name}}'
|
||||
readonly: true
|
||||
conditions:
|
||||
- name: notCreatedYet
|
||||
rule:
|
||||
id:
|
||||
_null: true
|
||||
hidden: true
|
||||
@@ -12,6 +12,9 @@ defaults:
|
||||
sort_field: null
|
||||
|
||||
data:
|
||||
- many_collection: directus_collections
|
||||
many_field: group
|
||||
one_collection: directus_collections
|
||||
- many_collection: directus_users
|
||||
many_field: role
|
||||
one_collection: directus_roles
|
||||
@@ -73,6 +76,9 @@ data:
|
||||
- many_collection: directus_sessions
|
||||
many_field: user
|
||||
one_collection: directus_users
|
||||
- many_collection: directus_sessions
|
||||
many_field: share
|
||||
one_collection: directus_shares
|
||||
- many_collection: directus_settings
|
||||
many_field: storage_default_folder
|
||||
one_collection: directus_folders
|
||||
@@ -88,3 +94,12 @@ data:
|
||||
- many_collection: directus_notifications
|
||||
many_field: sender
|
||||
one_collection: directus_users
|
||||
- many_collection: directus_shares
|
||||
many_field: role
|
||||
one_collection: directus_roles
|
||||
- many_collection: directus_shares
|
||||
many_field: collection
|
||||
one_collection: directus_collections
|
||||
- many_collection: directus_shares
|
||||
many_field: user_created
|
||||
one_collection: directus_users
|
||||
|
||||
Reference in New Issue
Block a user