From 4590467260a783e7d83adb20631c3b575d7e4cd3 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Tue, 30 Jun 2020 15:25:08 +0530 Subject: beginning sharing interface ui rework --- src/client/util/SharingManager.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 127ee33ce..903f6e23e 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -296,7 +296,7 @@ export default class SharingManager extends React.Component<{}> { onCloseButtonClick={action(() => this.groupToView = undefined)} /> : null} -

Manage the public link to {this.focusOn("this document...")}

+ {/*

Manage the public link to {this.focusOn("this document...")}

{!this.linkVisible ? (null) :
{this.sharingUrl}
@@ -325,10 +325,10 @@ export default class SharingManager extends React.Component<{}> { {this.sharingOptions}
-
+
*/}
-

Privately share {this.focusOn("this document")} with an individual...

+

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with an individual...

{/*200*/} {!existOtherUsers ? "There are no other users in your database." : this.users.map(({ user, notificationDoc }) => { // can't use async here @@ -366,7 +366,7 @@ export default class SharingManager extends React.Component<{}> {
-

Privately share {this.focusOn("this document")} with a group...

+

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with a group...

{/*200*/} {!existGroups ? "There are no groups in your database." : this.groups.map(group => { -- cgit v1.2.3-70-g09d2 From 467afe6346bf3188461721fa78eed0f9ac214da7 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:55:51 +0530 Subject: ui changes + related functionality changes --- package-lock.json | 44 +++++-- src/client/util/GroupManager.tsx | 91 ++++++------- src/client/util/GroupMemberView.tsx | 4 +- src/client/util/SharingManager.scss | 122 +++++++++++------ src/client/util/SharingManager.tsx | 252 +++++++++++++++++++++++------------- 5 files changed, 321 insertions(+), 192 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/package-lock.json b/package-lock.json index f03a39df0..c4c623994 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2966,7 +2966,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2984,11 +2985,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3001,15 +3004,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3112,7 +3118,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3122,6 +3129,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3134,17 +3142,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3161,6 +3172,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3216,7 +3228,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -3241,7 +3254,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3251,6 +3265,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3319,7 +3334,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3349,6 +3365,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3366,6 +3383,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3404,11 +3422,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 7c68fc2a0..e352d46a8 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -89,7 +89,8 @@ export default class GroupManager extends React.Component<{}> { /** * @returns a list of all group documents. */ - private getAllGroups(): Doc[] { + // private ? + getAllGroups(): Doc[] { const groupDoc = this.GroupManagerDoc; return groupDoc ? DocListCast(groupDoc.data) : []; } @@ -98,7 +99,8 @@ export default class GroupManager extends React.Component<{}> { * @returns a group document based on the group name. * @param groupName */ - private getGroup(groupName: string): Doc | undefined { + // private? + getGroup(groupName: string): Doc | undefined { const groupDoc = this.getAllGroups().find(group => group.groupName === groupName); return groupDoc; } @@ -172,8 +174,9 @@ export default class GroupManager extends React.Component<{}> { deleteGroup(group: Doc): boolean { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { + // TODO look at this later + // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); - SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } @@ -248,48 +251,48 @@ export default class GroupManager extends React.Component<{}> { /** * A getter that @returns the interface rendered to view an individual group. */ - private get editingInterface() { - const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; - const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; - return (!this.currentGroup ? null : -
-
- {this.currentGroup.groupName} -
this.currentGroup = undefined)}> - -
+ // private get editingInterface() { + // const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; + // const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; + // return (!this.currentGroup ? null : + //
+ //
+ // {this.currentGroup.groupName} + //
this.currentGroup = undefined)}> + // + //
- {this.hasEditAccess(this.currentGroup) ? -
-
- this.addMemberToGroup(this.currentGroup!, (selectedOption as UserOptions).value)} + // placeholder={"Add members"} + // value={null} + // closeMenuOnSelect={true} + // /> + //
+ // + //
: + // null} + //
+ //
+ // {members.map(member => ( + //
+ //
+ // {member} + //
+ // {this.hasEditAccess(this.currentGroup!) ? : null} + //
+ // ))} + //
+ //
+ // ); - } + // } /** * A getter that @returns the main interface for the GroupManager. @@ -307,7 +310,7 @@ export default class GroupManager extends React.Component<{}> { {this.currentGroup ? this.currentGroup = undefined} + onCloseButtonClick={action(() => this.currentGroup = undefined)} /> : null}
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index b2d75158e..c844892b1 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -8,7 +8,7 @@ import { action } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import Select from "react-select"; -import { Doc, Opt } from "../../fields/Doc"; +import { Doc } from "../../fields/Doc"; import "./GroupMemberView.scss"; library.add(fa.faWindowClose); @@ -44,7 +44,7 @@ export default class GroupMemberView extends React.Component
- +
: null}
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index fcbc05f8a..209c41651 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,9 +1,9 @@ @import "../views/globalCssVariables"; .sharing-interface { - display: flex; - flex-direction: column; - width: 730px; + // display: flex; + // flex-direction: column; + width: 600px; .dialogue-box { width: 450; @@ -16,6 +16,66 @@ .sharing-contents { display: flex; + flex-direction: column; + + .share-setup { + display: flex; + margin-bottom: 20px; + align-items: center; + height: 36; + + .user-search { + width: 90%; + } + + .permissions-select { + z-index: 1; + margin-left: -100; + border: none; + outline: none; + } + + .share-button { + height: 100%; + margin-left: 3%; + } + } + + .main-container { + display: flex; + + + .individual-container, + .group-container { + width: 50%; + + .share-title { + margin-top: 20px; + margin-bottom: 20px; + } + + .groups-list, + .users-list { + font-style: italic; + background: white; + border: 1px solid black; + padding-left: 10px; + padding-right: 10px; + overflow-y: scroll; + overflow-x: hidden; + text-align: left; + display: flex; + align-content: center; + align-items: center; + text-align: center; + justify-content: center; + // color: red; + color: black; + height: 150px; + margin: 0 2; + } + } + } button { background: $darker-alt-accent; @@ -31,37 +91,6 @@ transition: transform 0.2s; height: 25; } - - .individual-container, - .group-container { - width: 50%; - - .share-groups, - .share-individual { - margin-top: 20px; - margin-bottom: 20px; - } - - .groups-list, - .users-list { - font-style: italic; - background: white; - border: 1px solid black; - padding-left: 10px; - padding-right: 10px; - overflow-y: scroll; - overflow-x: hidden; - text-align: left; - display: flex; - align-content: center; - align-items: center; - text-align: center; - justify-content: center; - color: red; - height: 150px; - margin: 0 2; - } - } } .focus-span { @@ -69,11 +98,12 @@ } p { - font-size: 15px; + font-size: 20px; text-align: left; - font-style: italic; - padding: 0; + // font-style: italic; + // padding: 0; margin: 0 0 20px 0; + color: black; } .hr-substitute { @@ -108,9 +138,9 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - width: 700px; - min-width: 700px; - max-width: 700px; + width: 100%; + // min-width: 700px; + // max-width: 700px; text-align: left; font-style: normal; font-size: 14; @@ -118,20 +148,28 @@ padding: 0; align-items: baseline; + &:hover .padding { + white-space: unset; + } + .padding { padding: 0 10px 0 0; color: black; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 48%; } .permissions-dropdown { - outline: none; + border: none; height: 25; } .edit-actions { display: flex; position: absolute; - right: 51.5%; + right: -10; } } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 903f6e23e..491abe1dc 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -9,7 +9,6 @@ import { Utils } from "../../Utils"; import "./SharingManager.scss"; import { Id } from "../../fields/FieldSymbols"; import { observer } from "mobx-react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { DocumentView } from "../views/nodes/DocumentView"; @@ -17,8 +16,9 @@ import { SelectionManager } from "./SelectionManager"; import { DocumentManager } from "./DocumentManager"; import { CollectionView } from "../views/collections/CollectionView"; import { DictationOverlay } from "../views/DictationOverlay"; -import GroupManager from "./GroupManager"; +import GroupManager, { UserOptions } from "./GroupManager"; import GroupMemberView from "./GroupMemberView"; +import Select from "react-select"; library.add(fa.faCopy); @@ -28,18 +28,18 @@ export interface User { } export enum SharingPermissions { - None = "Not Shared", - View = "Can View", + Edit = "Can Edit", Add = "Can Add", - Edit = "Can Edit" + View = "Can View", + None = "Not Shared" } -const ColorMapping = new Map([ - [SharingPermissions.None, "red"], - [SharingPermissions.View, "maroon"], - [SharingPermissions.Add, "blue"], - [SharingPermissions.Edit, "green"] -]); +// const ColorMapping = new Map([ +// [SharingPermissions.None, "red"], +// [SharingPermissions.View, "maroon"], +// [SharingPermissions.Add, "blue"], +// [SharingPermissions.Edit, "green"] +// ]); const HierarchyMapping = new Map([ [SharingPermissions.None, "0"], @@ -54,10 +54,18 @@ const HierarchyMapping = new Map([ ]); +interface GroupOptions { + label: string; + options: UserOptions[]; +} + const SharingKey = "sharingPermissions"; const PublicKey = "publicLinkPermissions"; const DefaultColor = "black"; +const groupType = "!groupType/"; +const indType = "!indType/"; + interface ValidatedUser { user: User; notificationDoc: Doc; @@ -70,17 +78,18 @@ export default class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @observable private isOpen = false; @observable private users: ValidatedUser[] = []; - @observable private groups: Doc[] = []; + // @observable private groups: Doc[] = []; @observable private targetDoc: Doc | undefined; @observable private targetDocView: DocumentView | undefined; @observable private copied = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; - @observable private groupToView: Opt; + @observable private selectedUsers: UserOptions[] | null = null; + @observable private permissions: SharingPermissions = SharingPermissions.None; - private get linkVisible() { - return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; - } + // private get linkVisible() { + // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; + // } public open = (target: DocumentView) => { SelectionManager.DeselectAll(); @@ -94,7 +103,7 @@ export default class SharingManager extends React.Component<{}> { } })); - runInAction(() => this.groups = GroupManager.Instance.getAllGroupsCopy()); + // runInAction(() => this.groups = GroupManager.Instance.getAllGroups()); } public close = action(() => { @@ -145,6 +154,7 @@ export default class SharingManager extends React.Component<{}> { const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); const sharingDoc = this.sharingDoc!; + console.log(sharingDoc) if (permission === SharingPermissions.None) { const metadata = sharingDoc[StrCast(group.groupName)]; if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; @@ -158,7 +168,7 @@ export default class SharingManager extends React.Component<{}> { }); } - setInternalSharing = async (recipient: ValidatedUser, state: string, group: Opt) => { + setInternalSharing = async (recipient: ValidatedUser, state: string, group?: Doc) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; const manager = this.sharingDoc!; @@ -207,13 +217,13 @@ export default class SharingManager extends React.Component<{}> { if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } - private setExternalSharing = (state: string) => { - const sharingDoc = this.sharingDoc; - if (!sharingDoc) { - return; - } - sharingDoc[PublicKey] = state; - } + // private setExternalSharing = (state: string) => { + // const sharingDoc = this.sharingDoc; + // if (!sharingDoc) { + // return; + // } + // sharingDoc[PublicKey] = state; + // } private get sharingUrl() { if (!this.targetDoc) { @@ -282,18 +292,57 @@ export default class SharingManager extends React.Component<{}> { return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); } + @action + handleUsersChange = (selectedOptions: any) => { + this.selectedUsers = selectedOptions as UserOptions[]; + } + + @action + handlePermissionsChange = (event: React.ChangeEvent) => { + this.permissions = event.currentTarget.value as SharingPermissions; + } + + @action + share = () => { + this.selectedUsers?.forEach(user => { + if (user.value.includes(indType)) { + console.log(user); + console.log(this.users.find(u => u.user.email === user.label)); + this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions); + } + else { + this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); + } + }); + + this.selectedUsers = null; + } + private get sharingInterface() { const existOtherUsers = this.users.length > 0; - const existGroups = this.groups.length > 0; + const existGroups = GroupManager.Instance?.getAllGroups().length > 0; // const manager = this.sharingDoc!; + const options: GroupOptions[] = GroupManager.Instance ? + [ + { + label: 'Individuals', + options: GroupManager.Instance.options.map(({ label, value }) => ({ label, value: "!indType/" + value })) + }, + { + label: 'Groups', + options: GroupManager.Instance.getAllGroups().map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })) + } + ] + : []; + return (
- {this.groupToView ? + {GroupManager.Instance?.currentGroup ? this.groupToView = undefined)} + group={GroupManager.Instance.currentGroup} + onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)} /> : null} {/*

Manage the public link to {this.focusOn("this document...")}

@@ -327,76 +376,95 @@ export default class SharingManager extends React.Component<{}> {
*/}
-
-

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with an individual...

-
{/*200*/} - {!existOtherUsers ? "There are no other users in your database." : - this.users.map(({ user, notificationDoc }) => { // can't use async here - const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); - const color = ColorMapping.get(permissions); - - // console.log(manager); - // const metadata = manager[userKey] as Doc; - // const usersShared = StrCast(metadata?.usersShared, ""); - // console.log(usersShared) - - - return ( -
- {user.email} - {/*
{usersShared}
*/} -
- +

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

+
+ + {this.sharingOptions} + + +
+
+
+
{/*200*/} + {!existOtherUsers ? "There are no other users in your database." : + this.users.map(({ user, notificationDoc }) => { // can't use async here + const userKey = user.userDocumentId; + const permissions = this.computePermissions(userKey); + // const color = ColorMapping.get(permissions); + + // console.log(manager); + // const metadata = manager[userKey] as Doc; + // const usersShared = StrCast(metadata?.usersShared, ""); + // console.log(usersShared) + + + return permissions === SharingPermissions.None ? null : ( +
+ {user.email} + {/*
{usersShared}
*/} +
+ +
-
- ); - }) - } + ); + }) + } +
-
-
-

Privately share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))} with a group...

-
{/*200*/} - {!existGroups ? "There are no groups in your database." : - this.groups.map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); - const color = ColorMapping.get(permissions); - return ( -
- {group.groupName} -
- - +
+
{/*200*/} + {!existGroups ? "There are no groups in your database." : + GroupManager.Instance.getAllGroups().map(group => { + const permissions = this.computePermissions(StrCast(group.groupName)); + // const color = ColorMapping.get(permissions); + return permissions === SharingPermissions.None ? null : ( +
+ {group.groupName} +
+ + +
-
- ); - }) + ); + }) - } + } +
+
Done
-- cgit v1.2.3-70-g09d2 From d7f2f6994ce6f2a450dff67b3595a692be9cb977 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 2 Jul 2020 17:23:31 +0530 Subject: restructuring and simplifying group implementation --- src/client/util/GroupManager.tsx | 34 +++++- src/client/util/SharingManager.tsx | 199 +++++++++++++++++--------------- src/client/views/nodes/DocumentView.tsx | 3 +- src/fields/Doc.ts | 8 +- src/fields/util.ts | 2 +- 5 files changed, 141 insertions(+), 105 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index e352d46a8..c8d3be49b 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -3,11 +3,11 @@ import { observable, action, runInAction, computed } from "mobx"; import { SelectionManager } from "./SelectionManager"; import MainViewModal from "../views/MainViewModal"; import { observer } from "mobx-react"; -import { Doc, DocListCast, Opt } from "../../fields/Doc"; +import { Doc, DocListCast, Opt, DocListCastAsync } from "../../fields/Doc"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; -import SharingManager, { User } from "./SharingManager"; +import { User } from "./SharingManager"; import { Utils } from "../../Utils"; import * as RequestPromise from "request-promise"; import Select from 'react-select'; @@ -33,6 +33,7 @@ export default class GroupManager extends React.Component<{}> { @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown. @observable currentGroup: Opt; // the currently selected group. private inputRef: React.RefObject = React.createRef(); // the ref for the input box. + private currentUserGroups: Doc[] = []; constructor(props: Readonly<{}>) { super(props); @@ -42,6 +43,26 @@ export default class GroupManager extends React.Component<{}> { // sets up the list of users componentDidMount() { this.populateUsers().then(resolved => runInAction(() => this.users = resolved)); + + // this.getAllGroups().forEach(group => { + // const members: string[] = JSON.parse(StrCast(group.members)); + // if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + // }); + DocListCastAsync(this.GroupManagerDoc?.data).then(groups => { + groups?.forEach(group => { + const members: string[] = JSON.parse(StrCast(group.members)); + if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + }); + }) + .finally(() => console.log(this.currentUserGroups)); + + // (this.GroupManagerDoc?.data as List).forEach(group => { + // Promise.resolve(group).then(resolvedGroup => { + // const members: string[] = JSON.parse(StrCast(resolvedGroup.members)); + // if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(resolvedGroup); + // }); + // }); + } /** @@ -49,8 +70,8 @@ export default class GroupManager extends React.Component<{}> { */ populateUsers = async () => { const userList: User[] = JSON.parse(await RequestPromise.get(Utils.prepend("/getUsers"))); - const currentUserIndex = userList.findIndex(user => user.email === Doc.CurrentUserEmail); - currentUserIndex !== -1 && userList.splice(currentUserIndex, 1); + // const currentUserIndex = userList.findIndex(user => user.email === Doc.CurrentUserEmail); + // currentUserIndex !== -1 && userList.splice(currentUserIndex, 1); return userList.map(user => user.email); } @@ -61,6 +82,11 @@ export default class GroupManager extends React.Component<{}> { return this.users.map(user => ({ label: user, value: user })); } + + get groupMemberships() { + return this.currentUserGroups; + } + /** * Makes the GroupManager visible. */ diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 491abe1dc..b4977f8ea 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -85,7 +85,7 @@ export default class SharingManager extends React.Component<{}> { @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; - @observable private permissions: SharingPermissions = SharingPermissions.None; + @observable private permissions: SharingPermissions = SharingPermissions.Edit; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -98,9 +98,9 @@ export default class SharingManager extends React.Component<{}> { this.targetDoc = target.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; - if (!this.sharingDoc) { - this.sharingDoc = new Doc; - } + // if (!this.sharingDoc) { + // this.sharingDoc = new Doc; + // } })); // runInAction(() => this.groups = GroupManager.Instance.getAllGroups()); @@ -116,13 +116,13 @@ export default class SharingManager extends React.Component<{}> { }), 500); }); - private get sharingDoc() { - return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined; - } + // private get sharingDoc() { + // return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined; + // } - private set sharingDoc(value: Doc | undefined) { - this.targetDoc && (this.targetDoc[SharingKey] = value); - } + // private set sharingDoc(value: Doc | undefined) { + // this.targetDoc && (this.targetDoc[SharingKey] = value); + // } constructor(props: {}) { super(props); @@ -153,76 +153,79 @@ export default class SharingManager extends React.Component<{}> { const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); - const sharingDoc = this.sharingDoc!; - console.log(sharingDoc) - if (permission === SharingPermissions.None) { - const metadata = sharingDoc[StrCast(group.groupName)]; - if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; - } - else { - sharingDoc[StrCast(group.groupName)] = permission; - } + const target = this.targetDoc!; + const ACL = `ACL-${StrCast(group.groupName)}`; + + target[ACL] = permission; + // const sharingDoc = this.sharingDoc!; + // if (permission === SharingPermissions.None) { + // const metadata = sharingDoc[StrCast(group.groupName)]; + // if (metadata) sharingDoc[StrCast(group.groupName)] = undefined; + // } + // else { + // sharingDoc[StrCast(group.groupName)] = permission; + // } - users.forEach(user => { - this.setInternalSharing(user, permission, group); + users.forEach(({ notificationDoc }) => { + Doc.AddDocToList(notificationDoc, storage, target); }); } - setInternalSharing = async (recipient: ValidatedUser, state: string, group?: Doc) => { + setInternalSharing = (recipient: ValidatedUser, permission: string, group?: Doc) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; - const manager = this.sharingDoc!; + // const manager = this.sharingDoc!; const key = user.userDocumentId; - let metadata = await DocCastAsync(manager[key]); - const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; - permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(state)!); - const max = Math.max(...Object.values(permissions)); - - // let max = 0; - // const keys: string[] = []; - // for (const [key, value] of Object.entries(permissions)) { - // if (value === max && max !== 0) { - // keys.push(key); - // } - // else if (value > max) { - // keys.splice(0, keys.length); - // keys.push(key); - // max = value; - // } - // } + const ACL = `ACL-${key}`; - switch (max) { - case 0: - if (metadata) { - const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - manager[key] = undefined; - } - break; - - case 1: case 2: case 3: - if (!metadata) { - metadata = new Doc; - const sharedAlias = Doc.MakeAlias(target); - Doc.AddDocToList(notificationDoc, storage, sharedAlias); - metadata.sharedAlias = sharedAlias; - manager[key] = metadata; - } - metadata.permissions = JSON.stringify(permissions); - // metadata.usersShared = JSON.stringify(keys); - break; - } + // const permissions: { [key: string]: number } = target[ACL] ? JSON.parse(StrCast(target[ACL])) : {}; + + target[ACL] = permission; + + Doc.AddDocToList(notificationDoc, storage, target); - if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + + // let metadata = await DocCastAsync(manager[key]); + // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; + // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); + // const max = Math.max(...Object.values(permissions)); + + // switch (max) { + // case 0: + // // if (metadata) { + // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; + // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); + // // manager[key] = undefined; + // // } + // Doc.RemoveDocFromList(notificationDoc, storage, target); + // break; + + // case 1: case 2: case 3: + + // Doc.AddDocToList(notificationDoc, storage, target); + + // if (!metadata) { + // metadata = new Doc; + // const sharedAlias = Doc.MakeAlias(target); + // Doc.AddDocToList(notificationDoc, storage, target); + // metadata.sharedAlias = sharedAlias; + // manager[key] = metadata; + // } + // metadata.permissions = JSON.stringify(permissions); + // // metadata.usersShared = JSON.stringify(keys); + // break; + // } + + // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); } - // private setExternalSharing = (state: string) => { + // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; // if (!sharingDoc) { // return; // } - // sharingDoc[PublicKey] = state; + // sharingDoc[PublicKey] = permission; // } private get sharingUrl() { @@ -281,15 +284,21 @@ export default class SharingManager extends React.Component<{}> { } private computePermissions = (userKey: string) => { - const sharingDoc = this.sharingDoc; - if (!sharingDoc) { - return SharingPermissions.None; - } - const metadata = sharingDoc[userKey] as Doc | string; - if (!metadata) { - return SharingPermissions.None; - } - return StrCast(metadata instanceof Doc ? metadata.maxPermission : metadata, SharingPermissions.None); + // const sharingDoc = this.sharingDoc; + // if (!sharingDoc) { + // return SharingPermissions.None; + // } + // const metadata = sharingDoc[userKey] as Doc | string; + + if (!this.targetDoc) return SharingPermissions.None; + + const ACL = `ACL-${userKey}`; + const permission = StrCast(this.targetDoc[ACL]); + + // if (!metadata) { + // return SharingPermissions.None; + // } + return StrCast(this.targetDoc[ACL], SharingPermissions.None); } @action @@ -314,7 +323,6 @@ export default class SharingManager extends React.Component<{}> { this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions); } }); - this.selectedUsers = null; } @@ -377,27 +385,30 @@ export default class SharingManager extends React.Component<{}> {
*/}

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

-
- - {this.sharingOptions} - - -
+ {this.targetDoc?.author !== Doc.CurrentUserEmail ? null + : +
+ + {this.sharingOptions} + + +
+ }
{/*200*/} - {!existOtherUsers ? "There are no other users in your database." : + { this.users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.userDocumentId; const permissions = this.computePermissions(userKey); @@ -434,8 +445,8 @@ export default class SharingManager extends React.Component<{}> {
{/*200*/} - {!existGroups ? "There are no groups in your database." : - GroupManager.Instance.getAllGroups().map(group => { + { + GroupManager.Instance?.getAllGroups().map(group => { const permissions = this.computePermissions(StrCast(group.groupName)); // const color = ColorMapping.get(permissions); return permissions === SharingPermissions.None ? null : ( diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 09eeaee36..21b6d8310 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1169,9 +1169,8 @@ export class DocumentView extends DocComponent(Docu } render() { - if (!(this.props.Document instanceof Doc)) return (null); if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); - if (this.props.Document.hidden) return (null); + if (!(this.props.Document instanceof Doc)) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e4d11dd4d..7b1db1042 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -96,13 +96,13 @@ export const AclSym = Symbol("Acl"); export const AclPrivate = Symbol("AclOwnerOnly"); export const AclReadonly = Symbol("AclReadOnly"); export const AclAddonly = Symbol("AclAddonly"); -export const AclReadWrite = Symbol("AclReadWrite"); +export const AclEdit = Symbol("AclEdit"); export const UpdatingFromServer = Symbol("UpdatingFromServer"); const CachedUpdates = Symbol("Cached updates"); export function fetchProto(doc: Doc) { - if (doc.author !== Doc.CurrentUserEmail) { + if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions const acl = Doc.Get(doc, "ACL", true); switch (acl) { case "ownerOnly": @@ -114,8 +114,8 @@ export function fetchProto(doc: Doc) { case "addOnly": doc[AclSym] = AclAddonly; break; - case "write": - doc[AclSym] = AclReadWrite; + case "edit": + doc[AclSym] = AclEdit; } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 2dc21c987..7bb090a93 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -111,7 +111,7 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { let prop = in_prop; - if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; + if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { console.log(prop + " is deprecated - switch to _" + prop); -- cgit v1.2.3-70-g09d2 From 293c62ea3eccbeb2565960abb5ab02fabb5e20a0 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 3 Jul 2020 16:16:36 +0530 Subject: many changes related to adding and removing document from the notificationDoc of a user --- src/client/DocServer.ts | 2 +- src/client/util/GroupManager.tsx | 11 +- src/client/util/SharingManager.scss | 62 ++++++--- src/client/util/SharingManager.tsx | 258 +++++++++++++++++++++++------------- src/client/views/MainViewModal.scss | 5 +- src/client/views/MainViewModal.tsx | 1 - 6 files changed, 218 insertions(+), 121 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 2a7a7c59a..eac53bb02 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -31,7 +31,7 @@ export namespace DocServer { export enum WriteMode { Default = 0, //Anything goes - Playground = 1, //Playground (write own/no read) + Playground = 1, //Playground (write own/no read other updates) LiveReadonly = 2,//Live Readonly (no write/read others) LivePlayground = 3,//Live Playground (write own/read others) } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index c8d3be49b..220916ba7 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt, DocListCastAsync } from "../../fields/Doc"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; -import { User } from "./SharingManager"; +import SharingManager, { User } from "./SharingManager"; import { Utils } from "../../Utils"; import * as RequestPromise from "request-promise"; import Select from 'react-select'; @@ -203,6 +203,7 @@ export default class GroupManager extends React.Component<{}> { // TODO look at this later // SharingManager.Instance.setInternalGroupSharing(group, "Not Shared"); Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); + SharingManager.Instance.removeGroup(group); if (group === this.currentGroup) { runInAction(() => this.currentGroup = undefined); } @@ -222,6 +223,7 @@ export default class GroupManager extends React.Component<{}> { const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); !memberList.includes(email) && memberList.push(email); groupDoc.members = JSON.stringify(memberList); + SharingManager.Instance.shareWithAddedMember(groupDoc, email); } } @@ -234,8 +236,11 @@ export default class GroupManager extends React.Component<{}> { if (this.hasEditAccess(groupDoc)) { const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); const index = memberList.indexOf(email); - index !== -1 && memberList.splice(index, 1); - groupDoc.members = JSON.stringify(memberList); + if (index !== -1) { + const user = memberList.splice(index, 1)[0]; + groupDoc.members = JSON.stringify(memberList); + SharingManager.Instance.removeMember(groupDoc, email); + } } } diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 209c41651..2708876a3 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -4,6 +4,7 @@ // display: flex; // flex-direction: column; width: 600px; + height: 360px; .dialogue-box { width: 450; @@ -14,10 +15,23 @@ transform: translate(-20px, -20px); } + select { + text-align: justify; + text-align-last: end + } + .sharing-contents { display: flex; flex-direction: column; + .close-button { + position: absolute; + right: 1em; + top: 1em; + cursor: pointer; + z-index: 999; + } + .share-setup { display: flex; margin-bottom: 20px; @@ -33,11 +47,14 @@ margin-left: -100; border: none; outline: none; + text-align: justify; // for Edge + text-align-last: end; } .share-button { - height: 100%; - margin-left: 3%; + height: 105%; + margin-left: 2%; + background-color: #979797; } } @@ -57,8 +74,8 @@ .groups-list, .users-list { font-style: italic; - background: white; - border: 1px solid black; + background: gainsboro; + // border: 1px solid black; padding-left: 10px; padding-right: 10px; overflow-y: scroll; @@ -71,8 +88,14 @@ justify-content: center; // color: red; color: black; - height: 150px; + height: 255px; margin: 0 2; + + + .none { + font-style: italic; + + } } } } @@ -146,7 +169,7 @@ font-size: 14; font-weight: normal; padding: 0; - align-items: baseline; + align-items: center; &:hover .padding { white-space: unset; @@ -164,6 +187,7 @@ .permissions-dropdown { border: none; height: 25; + background: gainsboro; } .edit-actions { @@ -210,17 +234,17 @@ } } - .close-button { - border-radius: 5px; - margin-top: 20px; - padding: 10px 0; - background: aliceblue; - transition: 0.5s ease all; - border: 1px solid; - border-color: aliceblue; - } - - .close-button:hover { - border-color: black; - } + // .close-button { + // border-radius: 5px; + // margin-top: 20px; + // padding: 10px 0; + // background: aliceblue; + // transition: 0.5s ease all; + // border: 1px solid; + // border-color: aliceblue; + // } + + // .close-button:hover { + // border-color: black; + // } } \ No newline at end of file diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index b4977f8ea..372b6172d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,7 +1,7 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocCastAsync } from "../../fields/Doc"; +import { Doc, Opt, DocCastAsync, DocListCast } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; @@ -19,6 +19,8 @@ import { DictationOverlay } from "../views/DictationOverlay"; import GroupManager, { UserOptions } from "./GroupManager"; import GroupMemberView from "./GroupMemberView"; import Select from "react-select"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { List } from "../../fields/List"; library.add(fa.faCopy); @@ -86,6 +88,8 @@ export default class SharingManager extends React.Component<{}> { @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; @observable private permissions: SharingPermissions = SharingPermissions.Edit; + @observable private sharedUsers: ValidatedUser[] = []; + @observable private sharedGroups: Doc[] = []; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -166,16 +170,52 @@ export default class SharingManager extends React.Component<{}> { // sharingDoc[StrCast(group.groupName)] = permission; // } + group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); + users.forEach(({ notificationDoc }) => { - Doc.AddDocToList(notificationDoc, storage, target); + + if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + }); } - setInternalSharing = (recipient: ValidatedUser, permission: string, group?: Doc) => { + shareWithAddedMember = (group: Doc, email: string) => { + const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); + } + } + + removeMember = (group: Doc, email: string) => { + const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + } + } + + removeGroup = (group: Doc) => { + if (group.docsShared) { + DocListCast(group.docsShared).forEach(doc => { + const ACL = `ACL-${StrCast(group.groupName)}`; + doc[ACL] = "Not Shared"; + + const members: string[] = JSON.parse(StrCast(group.members)); + const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + + users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + }) + } + } + + setInternalSharing = (recipient: ValidatedUser, permission: string) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; // const manager = this.sharingDoc!; - const key = user.userDocumentId; + const key = user.email.replace('.', '_'); + // const key = user.userDocumentId; const ACL = `ACL-${key}`; @@ -183,43 +223,47 @@ export default class SharingManager extends React.Component<{}> { target[ACL] = permission; - Doc.AddDocToList(notificationDoc, storage, target); - - - // let metadata = await DocCastAsync(manager[key]); - // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; - // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); - // const max = Math.max(...Object.values(permissions)); - - // switch (max) { - // case 0: - // // if (metadata) { - // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - // // manager[key] = undefined; - // // } - // Doc.RemoveDocFromList(notificationDoc, storage, target); - // break; - - // case 1: case 2: case 3: - - // Doc.AddDocToList(notificationDoc, storage, target); - - // if (!metadata) { - // metadata = new Doc; - // const sharedAlias = Doc.MakeAlias(target); - // Doc.AddDocToList(notificationDoc, storage, target); - // metadata.sharedAlias = sharedAlias; - // manager[key] = metadata; - // } - // metadata.permissions = JSON.stringify(permissions); - // // metadata.usersShared = JSON.stringify(keys); - // break; - // } - // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target); + else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + } + + // let metadata = await DocCastAsync(manager[key]); + // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; + // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); + // const max = Math.max(...Object.values(permissions)); + + // switch (max) { + // case 0: + // // if (metadata) { + // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; + // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); + // // manager[key] = undefined; + // // } + // Doc.RemoveDocFromList(notificationDoc, storage, target); + // break; + + // case 1: case 2: case 3: + + // Doc.AddDocToList(notificationDoc, storage, target); + + // if (!metadata) { + // metadata = new Doc; + // const sharedAlias = Doc.MakeAlias(target); + // Doc.AddDocToList(notificationDoc, storage, target); + // metadata.sharedAlias = sharedAlias; + // manager[key] = metadata; + // } + // metadata.permissions = JSON.stringify(permissions); + // // metadata.usersShared = JSON.stringify(keys); + // break; + // } + + // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); + + // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; // if (!sharingDoc) { @@ -345,6 +389,67 @@ export default class SharingManager extends React.Component<{}> { ] : []; + const userListContents: (JSX.Element | null)[] = this.users.map(({ user, notificationDoc }) => { // can't use async here + const userKey = user.email.replace('.', '_'); + // const userKey = user.userDocumentId; + const permissions = this.computePermissions(userKey); + // const color = ColorMapping.get(permissions); + + // console.log(manager); + // const metadata = manager[userKey] as Doc; + // const usersShared = StrCast(metadata?.usersShared, ""); + // console.log(usersShared) + + return permissions === SharingPermissions.None ? null : ( +
+ {user.email} + {/*
{usersShared}
*/} +
+ +
+
+ ); + }); + + + const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { + const permissions = this.computePermissions(StrCast(group.groupName)); + // const color = ColorMapping.get(permissions); + + return permissions === SharingPermissions.None ? null : ( +
+ {group.groupName} +
+ + +
+
+ ); + }); + + const displayUserList = userListContents?.every(user => user === null); + const displayGroupList = groupListContents?.every(group => group === null); + return (
{GroupManager.Instance?.currentGroup ? @@ -385,6 +490,9 @@ export default class SharingManager extends React.Component<{}> {
*/}

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

+
+ +
{this.targetDoc?.author !== Doc.CurrentUserEmail ? null :
@@ -407,69 +515,30 @@ export default class SharingManager extends React.Component<{}> { }
-
{/*200*/} +
{/*200*/} { - this.users.map(({ user, notificationDoc }) => { // can't use async here - const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); - // const color = ColorMapping.get(permissions); - - // console.log(manager); - // const metadata = manager[userKey] as Doc; - // const usersShared = StrCast(metadata?.usersShared, ""); - // console.log(usersShared) - - - return permissions === SharingPermissions.None ? null : ( -
- {user.email} - {/*
{usersShared}
*/} -
- -
-
- ); - }) + displayUserList ? +
+ There are no users this document has been shared with. +
+ : + userListContents }
-
{/*200*/} +
{/*200*/} { - GroupManager.Instance?.getAllGroups().map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); - // const color = ColorMapping.get(permissions); - return permissions === SharingPermissions.None ? null : ( -
- {group.groupName} -
- - -
+ displayGroupList ? +
+ There are no groups this document has been shared with.
- ); - }) - + : + groupListContents }
@@ -477,7 +546,6 @@ export default class SharingManager extends React.Component<{}> {
-
Done
); } diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss index f5a9ee76c..812fe540b 100644 --- a/src/client/views/MainViewModal.scss +++ b/src/client/views/MainViewModal.scss @@ -6,9 +6,10 @@ align-self: center; align-content: center; padding: 20px; - background: gainsboro; + // background: gainsboro; + background: white; border-radius: 10px; - border: 3px solid black; + border: 0.5px solid black; box-shadow: #00000044 5px 5px 10px; transform: translate(-50%, -50%); top: 50%; diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index a7bd5882d..c6b3532e8 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -22,7 +22,6 @@ export default class MainViewModal extends React.Component
Date: Fri, 3 Jul 2020 21:50:56 +0530 Subject: can now exit modal by clicking outside it --- src/client/apis/GoogleAuthenticationManager.tsx | 1 + src/client/util/GroupManager.tsx | 1 + src/client/util/GroupMemberView.tsx | 1 + src/client/util/SettingsManager.tsx | 1 + src/client/util/SharingManager.tsx | 1 + src/client/views/DictationOverlay.tsx | 1 + src/client/views/MainViewModal.tsx | 35 +++++++++++++++++++++++++ src/mobile/AudioUpload.tsx | 1 + src/mobile/ImageUpload.tsx | 1 + 9 files changed, 43 insertions(+) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index bf4469aeb..5a2bdb13b 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -157,6 +157,7 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { contents={this.renderPrompt} overlayDisplayedOpacity={0.9} dialogueBoxStyle={this.dialogueBoxStyle} + closeOnExternalClick={() => this.isOpen = false} /> ); } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 220916ba7..0e710457e 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -387,6 +387,7 @@ export default class GroupManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index c844892b1..4377a1428 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -68,6 +68,7 @@ export default class GroupMemberView extends React.Component; } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 9888cce48..fa2b20095 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -134,6 +134,7 @@ export default class SettingsManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 372b6172d..e74824581 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -559,6 +559,7 @@ export default class SharingManager extends React.Component<{}> { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.close} /> ); } diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index 65770c0bb..9ed14509f 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -66,6 +66,7 @@ export class DictationOverlay extends React.Component { interactive={false} dialogueBoxStyle={dialogueBoxStyle} overlayStyle={overlayStyle} + closeOnExternalClick={this.initiateDictationFade} />); } } \ No newline at end of file diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index c6b3532e8..6041593b8 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; import "./MainViewModal.scss"; +import { Opt } from '../../fields/Doc'; +import { Lambda, reaction } from 'mobx'; +import { observer } from 'mobx-react'; export interface MainViewOverlayProps { isDisplayed: boolean; @@ -9,10 +12,41 @@ export interface MainViewOverlayProps { overlayStyle?: React.CSSProperties; dialogueBoxDisplayedOpacity?: number; overlayDisplayedOpacity?: number; + closeOnExternalClick?: () => void; } +@observer export default class MainViewModal extends React.Component { + private ref: React.RefObject = React.createRef(); + private displayedListenerDisposer: Opt; + + componentDidMount() { + + document.removeEventListener("click", this.close); + + this.displayedListenerDisposer = reaction(() => this.props.isDisplayed, (isDisplayed) => { + if (isDisplayed) document.addEventListener("click", this.close); + else document.removeEventListener("click", this.close); + }); + } + + componentWillUnmount() { + this.displayedListenerDisposer?.(); + document.removeEventListener("click", this.close); + } + + close = (e: MouseEvent) => { + + const { left, right, top, bottom } = this.ref.current!.getBoundingClientRect(); + + if (e.clientX === 0 && e.clientY === 0) return; // why does this happen? + if (e.clientX < left || e.clientX > right || e.clientY > bottom || e.clientY < top) { + this.props.closeOnExternalClick?.(); + } + + } + render() { const p = this.props; const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; @@ -26,6 +60,7 @@ export default class MainViewModal extends React.Component ...(p.dialogueBoxStyle || {}), opacity: p.isDisplayed ? dialogueOpacity : 0 }} + ref={this.ref} >{p.contents}
); } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index 5ea626d52..6a5834f52 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -172,6 +172,7 @@ export class Uploader extends React.Component { interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} + closeOnExternalClick={this.closeUpload} /> ); } -- cgit v1.2.3-70-g09d2 From f4830de4f8c4794ec98e54be9ba8730e46155c35 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Mon, 6 Jul 2020 18:18:17 +0530 Subject: trying first implementation of storing acls --- src/client/DocServer.ts | 7 ++- src/client/util/GroupManager.tsx | 60 +++---------------- src/client/util/SharingManager.tsx | 42 +++++++------- src/client/views/DocComponent.tsx | 7 ++- src/client/views/collections/CollectionView.tsx | 8 ++- src/client/views/nodes/DocumentContentsView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 ++- src/fields/Doc.ts | 67 ++++++++++++++-------- src/fields/util.ts | 60 +++++++++++++++++-- 10 files changed, 151 insertions(+), 119 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index eac53bb02..860a8fd92 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -39,9 +39,10 @@ export namespace DocServer { const docsWithUpdates: { [field: string]: Set } = {}; export var PlaygroundFields: string[]; - export function setPlaygroundFields(livePlayougroundFields: string[]) { - DocServer.PlaygroundFields = livePlayougroundFields; - livePlayougroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); + export function setPlaygroundFields(livePlaygroundFields: string[]) { + console.log("here"); + DocServer.PlaygroundFields = livePlaygroundFields; + livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground)); } export function setFieldWriteMode(field: string, writeMode: WriteMode) { diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index b14dcf55b..83b206f94 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -33,7 +33,7 @@ export default class GroupManager extends React.Component<{}> { @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown. @observable currentGroup: Opt; // the currently selected group. private inputRef: React.RefObject = React.createRef(); // the ref for the input box. - private currentUserGroups: Doc[] = []; + currentUserGroups: string[] = []; constructor(props: Readonly<{}>) { super(props); @@ -51,7 +51,7 @@ export default class GroupManager extends React.Component<{}> { DocListCastAsync(this.GroupManagerDoc?.data).then(groups => { groups?.forEach(group => { const members: string[] = JSON.parse(StrCast(group.members)); - if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group); + if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName)); }); }) .finally(() => console.log(this.currentUserGroups)); @@ -82,11 +82,6 @@ export default class GroupManager extends React.Component<{}> { return this.users.map(user => ({ label: user, value: user })); } - - get groupMemberships() { - return this.currentUserGroups; - } - /** * Makes the GroupManager visible. */ @@ -151,6 +146,11 @@ export default class GroupManager extends React.Component<{}> { ); } + getGroupMembers(group: string | Doc): string[] { + if (group instanceof Doc) return JSON.parse(StrCast(group.members)) as string[]; + else return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[]; + } + /** * @returns the members of the admin group. */ @@ -279,52 +279,6 @@ export default class GroupManager extends React.Component<{}> { this.inputRef.current.value = ""; } - /** - * A getter that @returns the interface rendered to view an individual group. - */ - // private get editingInterface() { - // const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : []; - // const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : []; - // return (!this.currentGroup ? null : - //
- //
- // {this.currentGroup.groupName} - //
this.currentGroup = undefined)}> - // - //
- - // {this.hasEditAccess(this.currentGroup) ? - //
- //
- // this.buttonColour = this.inputRef.current?.value ? "black" : "#979797")} /> + - this.props.group.groupName = e.currentTarget.value} + > +
- +
- {GroupManager.Instance.hasEditAccess(this.props.group) ?
@@ -55,13 +60,18 @@ export default class GroupMemberView extends React.Component : null}
+
{members.map(member => (
{member}
- {GroupManager.Instance.hasEditAccess(this.props.group) ? : null} + {GroupManager.Instance.hasEditAccess(this.props.group) ? +
GroupManager.Instance.removeMemberFromGroup(this.props.group, member)}> + +
+ : null}
))}
@@ -75,6 +85,7 @@ export default class GroupMemberView extends React.Component; } diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 2708876a3..ce23ce413 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -6,10 +6,10 @@ width: 600px; height: 360px; - .dialogue-box { - width: 450; - height: 300; - } + // .dialogue-box { + // width: 450; + // height: 300; + // } .overlay { transform: translate(-20px, -20px); @@ -60,11 +60,13 @@ .main-container { display: flex; - + margin-top: -10px; .individual-container, .group-container { width: 50%; + display: flex; + flex-direction: column; .share-title { margin-top: 20px; @@ -74,7 +76,7 @@ .groups-list, .users-list { font-style: italic; - background: gainsboro; + background: #e8e8e8; // border: 1px solid black; padding-left: 10px; padding-right: 10px; @@ -88,7 +90,7 @@ justify-content: center; // color: red; color: black; - height: 255px; + height: 250px; margin: 0 2; @@ -187,7 +189,6 @@ .permissions-dropdown { border: none; height: 25; - background: gainsboro; } .edit-actions { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index af68edab6..6c7c634eb 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -22,7 +22,7 @@ import Select from "react-select"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { List } from "../../fields/List"; -library.add(fa.faCopy); +library.add(fa.faCopy, fa.faTimes); export interface User { email: string; @@ -140,7 +140,7 @@ export default class SharingManager extends React.Component<{}> { setInternalGroupSharing = (group: Doc, permission: string) => { const members: string[] = JSON.parse(StrCast(group.members)); - const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); const target = this.targetDoc!; const ACL = `ACL-${StrCast(group.groupName)}`; @@ -160,8 +160,8 @@ export default class SharingManager extends React.Component<{}> { }); } - shareWithAddedMember = (group: Doc, email: string) => { - const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + shareWithAddedMember = (group: Doc, emailId: string) => { + const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; if (group.docsShared) { DocListCastAsync(group.docsShared).then(docsShared => { @@ -173,8 +173,8 @@ export default class SharingManager extends React.Component<{}> { } } - removeMember = (group: Doc, email: string) => { - const user: ValidatedUser = this.users.find(user => user.user.email === email)!; + removeMember = (group: Doc, emailId: string) => { + const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; if (group.docsShared) { DocListCastAsync(group.docsShared).then(docsShared => { @@ -194,9 +194,9 @@ export default class SharingManager extends React.Component<{}> { doc[ACL] = "Not Shared"; const members: string[] = JSON.parse(StrCast(group.members)); - const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email)); + const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); - users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); + users.forEach(({ notificationDoc }) => Doc.RemoveDocFromList(notificationDoc, storage, doc)); }); }); @@ -330,24 +330,6 @@ export default class SharingManager extends React.Component<{}> { ); } - private computePermissions = (userKey: string) => { - // const sharingDoc = this.sharingDoc; - // if (!sharingDoc) { - // return SharingPermissions.None; - // } - // const metadata = sharingDoc[userKey] as Doc | string; - - if (!this.targetDoc) return SharingPermissions.None; - - const ACL = `ACL-${userKey}`; - const permission = StrCast(this.targetDoc[ACL]); - - // if (!metadata) { - // return SharingPermissions.None; - // } - return StrCast(this.targetDoc[ACL], SharingPermissions.None); - } - @action handleUsersChange = (selectedOptions: any) => { this.selectedUsers = selectedOptions as UserOptions[]; @@ -381,7 +363,7 @@ export default class SharingManager extends React.Component<{}> { [ { label: 'Individuals', - options: GroupManager.Instance.options.map(({ label, value }) => ({ label, value: "!indType/" + value })) + options: this.users.map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })) }, { label: 'Groups', @@ -390,10 +372,12 @@ export default class SharingManager extends React.Component<{}> { ] : []; + console.log(this.users); + const userListContents: (JSX.Element | null)[] = this.users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.email.replace('.', '_'); // const userKey = user.userDocumentId; - const permissions = this.computePermissions(userKey); + const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None); // const color = ColorMapping.get(permissions); // console.log(manager); @@ -401,7 +385,7 @@ export default class SharingManager extends React.Component<{}> { // const usersShared = StrCast(metadata?.usersShared, ""); // console.log(usersShared) - return permissions === SharingPermissions.None ? null : ( + return permissions === SharingPermissions.None || user.email === this.targetDoc?.author ? null : (
{ ); }); + userListContents.unshift( + ( +
+ {this.targetDoc?.author} +
+
+ Owner +
+
+
+ ) + ); const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { - const permissions = this.computePermissions(StrCast(group.groupName)); + const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`], SharingPermissions.None); // const color = ColorMapping.get(permissions); return permissions === SharingPermissions.None ? null : ( @@ -492,7 +491,7 @@ export default class SharingManager extends React.Component<{}> {

Share {this.focusOn(StrCast(this.targetDoc?.title, "this document"))}

- +
{this.targetDoc?.author !== Doc.CurrentUserEmail ? null : @@ -516,6 +515,7 @@ export default class SharingManager extends React.Component<{}> { }
+
Individuals
{/*200*/} { displayUserList ? @@ -530,6 +530,7 @@ export default class SharingManager extends React.Component<{}> {
+
Groups
{/*200*/} { displayGroupList ? diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 0b73a6ad7..249715511 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -21,7 +21,7 @@ export default class MainViewModal extends React.Component const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1; const overlayOpacity = p.overlayDisplayedOpacity || 0.4; return !p.isDisplayed ? (null) : ( -
+
Date: Wed, 15 Jul 2020 16:49:56 +0530 Subject: ui + sorting --- src/client/util/GroupManager.scss | 99 ++++++++++++++++++++---------------- src/client/util/GroupManager.tsx | 74 +++++++++++++++++++-------- src/client/util/GroupMemberView.scss | 27 ++++++++-- src/client/util/GroupMemberView.tsx | 21 ++++++-- src/client/util/SharingManager.scss | 23 +++++++-- src/client/util/SharingManager.tsx | 68 +++++++++++++++++-------- 6 files changed, 215 insertions(+), 97 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss index 2da1f0d95..8a2c616b1 100644 --- a/src/client/util/GroupManager.scss +++ b/src/client/util/GroupManager.scss @@ -20,9 +20,10 @@ // border: none; padding: 8px; min-width: 100%; - margin: 4px 0 4px 0; + // margin: 4px 0 4px 0; border: 1px solid hsl(0, 0%, 80%); outline: none; + height: 30; &:focus { // border: unset; @@ -100,59 +101,69 @@ } } + .main-container { + display: flex; + flex-direction: column; - .group-body { - // display: flex; - justify-content: space-between; - // max-height: 80%; - height: 100%; - background-color: #e8e8e8; - // flex-direction: column; + .sort-groups { + text-align: left; + margin-left: 5; + cursor: pointer; + } - // padding-left: 1em; - padding-right: 1em; - justify-content: space-around; - text-align: left; + .group-body { + // display: flex; + justify-content: space-between; + // max-height: 80%; + height: 220; + background-color: #e8e8e8; + // flex-direction: column; + + // padding-left: 1em; + padding-right: 1em; + justify-content: space-around; + text-align: left; - overflow-y: auto; - width: 100%; + overflow-y: auto; + width: 100%; - .group-row { - display: flex; - // position: relative; - margin-bottom: 5px; - min-height: 30px; - // border: 1px solid; - // border-radius: 10px; - align-items: center; - - .group-name { + .group-row { + display: flex; // position: relative; - max-width: 65%; - // left: 10; - margin: 0 10; - color: black; - } + margin-bottom: 5px; + min-height: 30px; + // border: 1px solid; + // border-radius: 10px; + align-items: center; + + .group-name { + // position: relative; + max-width: 65%; + // left: 10; + margin: 0 10; + color: black; + } - .group-info { - cursor: pointer; + .group-info { + cursor: pointer; + } + + button { + position: absolute; + width: 30%; + right: 2; + margin-top: 0; + } } - button { - position: absolute; - width: 30%; - right: 2; - margin-top: 0; + input { + border-radius: 5px; + border: none; + padding: 4px; + min-width: 100%; + margin: 2px 0; } - } - input { - border-radius: 5px; - border: none; - padding: 4px; - min-width: 100%; - margin: 2px 0; } - } } \ No newline at end of file diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index f2b502ae0..2d8930660 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -38,31 +38,38 @@ export default class GroupManager extends React.Component<{}> { private inputRef: React.RefObject = React.createRef(); // the ref for the input box. private currentUserGroups: string[] = []; @observable private buttonColour: "#979797" | "black" = "#979797"; + @observable private groupSort: "ascending" | "descending" | "none" = "none"; + constructor(props: Readonly<{}>) { super(props); GroupManager.Instance = this; } + componentDidMount() { + this.populateUsers(); + } + /** * Fetches the list of users stored on the database. */ populateUsers = async () => { + runInAction(() => this.users = []); const userList = await RequestPromise.get(Utils.prepend("/getUsers")); const raw = JSON.parse(userList) as User[]; const evaluating = raw.map(async user => { - const isCandidate = user.email !== Doc.CurrentUserEmail; - if (isCandidate) { - const userDocument = await DocServer.GetRefField(user.userDocumentId); - if (userDocument instanceof Doc) { - const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); - runInAction(() => { - if (notificationDoc instanceof Doc) { - this.users.push(user.email); - } - }); - } + // const isCandidate = user.email !== Doc.CurrentUserEmail; + // if (isCandidate) { + const userDocument = await DocServer.GetRefField(user.userDocumentId); + if (userDocument instanceof Doc) { + const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc); + runInAction(() => { + if (notificationDoc instanceof Doc) { + this.users.push(user.email); + } + }); } + // } }); return Promise.all(evaluating); } @@ -99,7 +106,7 @@ export default class GroupManager extends React.Component<{}> { close = () => { this.isOpen = false; this.currentGroup = undefined; - this.users = []; + // this.users = []; this.createGroupModalOpen = false; } @@ -304,6 +311,7 @@ export default class GroupManager extends React.Component<{}> {
{ * A getter that @returns the main interface for the GroupManager. */ private get groupInterface() { + + const sortGroups = (d1: Doc, d2: Doc) => { + const g1 = StrCast(d1.groupName); + const g2 = StrCast(d2.groupName); + + return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; + }; + + let groups = this.getAllGroups(); + groups = this.groupSort === "ascending" ? groups.sort(sortGroups) : this.groupSort === "descending" ? groups.sort(sortGroups).reverse() : groups; + return (
{this.groupCreationModal} @@ -375,19 +394,30 @@ export default class GroupManager extends React.Component<{}> {
-
- {this.getAllGroups().map(group => -
-
{group.groupName}
-
this.currentGroup = group)}> - -
- {/* */} -
- )} +
+ )} +
+
); } diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss index 9bdf832e0..a34e5b989 100644 --- a/src/client/util/GroupMemberView.scss +++ b/src/client/util/GroupMemberView.scss @@ -8,7 +8,7 @@ // color: black; hr { - margin-top: 10; + margin-top: 20; } button { @@ -17,7 +17,7 @@ border-radius: 5px; border: 0px; color: #fcfbf7; - // text-transform: uppercase; + text-transform: none; letter-spacing: 2px; font-size: 75%; padding: 10px; @@ -45,15 +45,33 @@ outline: none; color: black; margin-top: -5; + height: 20; + text-overflow: ellipsis; + + &:hover { + text-overflow: visible; + overflow-x: auto; + } + } + + .sort-emails { + float: left; + margin: -18 0 0 5; + cursor: pointer; } .group-buttons { display: flex; margin-top: 5; + margin-bottom: 25; .add-member-dropdown { width: 65%; margin: 0 5; + + input { + height: 30; + } } } } @@ -61,7 +79,7 @@ .editing-contents { overflow-y: auto; // max-height: 67%; - height: 67%; + height: 65%; width: 100%; color: black; margin-top: -15px; @@ -70,6 +88,7 @@ display: flex; align-items: center; margin-bottom: 10px; + position: relative; // border: 1px solid; // border-radius: 10px; @@ -83,7 +102,7 @@ .remove-button { position: absolute; - right: 30; + right: 10; cursor: pointer; } } diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 2462ee4d5..ebe9830ba 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import GroupManager, { UserOptions } from "./GroupManager"; import { library } from "@fortawesome/fontawesome-svg-core"; import { StrCast } from "../../fields/Types"; -import { action } from "mobx"; +import { action, observable } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as fa from '@fortawesome/free-solid-svg-icons'; import Select from "react-select"; @@ -21,10 +21,17 @@ interface GroupMemberViewProps { @observer export default class GroupMemberView extends React.Component { + @observable private memberSort: "ascending" | "descending" | "none" = "none"; private get editingInterface() { - const members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : []; + let members: string[] = this.props.group ? JSON.parse(StrCast(this.props.group.members)) : []; + members = this.memberSort === "ascending" ? members.sort() : this.memberSort === "descending" ? members.sort().reverse() : members; + const options: UserOptions[] = this.props.group ? GroupManager.Instance.options.filter(option => !(JSON.parse(StrCast(this.props.group.members)) as string[]).includes(option.value)) : []; + console.log(this.props.group, options); + console.log(GroupManager.Instance.options); + + return (!this.props.group ? null :
@@ -59,11 +66,19 @@ export default class GroupMemberView extends React.Component GroupManager.Instance.deleteGroup(this.props.group)}>Delete group
: null} +
this.memberSort = this.memberSort === "ascending" ? "descending" : this.memberSort === "descending" ? "none" : "ascending")}> + Emails {this.memberSort === "ascending" ? "↑" : this.memberSort === "descending" ? "↓" : ""} {/* → */} +

{members.map(member => ( -
+
{member}
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index ce23ce413..572b94ffb 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,4 +1,4 @@ -@import "../views/globalCssVariables"; +// @import "../views/globalCssVariables"; .sharing-interface { // display: flex; @@ -40,6 +40,10 @@ .user-search { width: 90%; + + input { + height: 30; + } } .permissions-select { @@ -68,6 +72,12 @@ display: flex; flex-direction: column; + .user-sort { + text-align: left; + margin-left: 10; + cursor: pointer; + } + .share-title { margin-top: 20px; margin-bottom: 20px; @@ -103,12 +113,12 @@ } button { - background: $darker-alt-accent; + // background: $darker-alt-accent; outline: none; border-radius: 5px; border: 0px; color: #fcfbf7; - text-transform: uppercase; + text-transform: none; letter-spacing: 2px; font-size: 75%; padding: 0 10; @@ -173,6 +183,10 @@ padding: 0; align-items: center; + .group-info { + cursor: pointer; + } + &:hover .padding { white-space: unset; } @@ -183,12 +197,13 @@ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; - max-width: 48%; + max-width: 40%; } .permissions-dropdown { border: none; height: 25; + background-color: #e8e8e8; } .edit-actions { diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 6c7c634eb..817b7c6b8 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -75,6 +75,9 @@ export default class SharingManager extends React.Component<{}> { @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; @observable private permissions: SharingPermissions = SharingPermissions.Edit; + @observable private individualSort: "ascending" | "descending" | "none" = "none"; + @observable private groupSort: "ascending" | "descending" | "none" = "none"; + // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -151,8 +154,6 @@ export default class SharingManager extends React.Component<{}> { // group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); users.forEach(({ notificationDoc }) => { - - DocListCastAsync(notificationDoc[storage]).then(res => console.log(res)); DocListCastAsync(notificationDoc[storage]).then(resolved => { if (permission !== SharingPermissions.None) Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); else Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); @@ -353,28 +354,44 @@ export default class SharingManager extends React.Component<{}> { this.selectedUsers = null; } + sortUsers = (u1: ValidatedUser, u2: ValidatedUser) => { + const { email: e1 } = u1.user; + const { email: e2 } = u2.user; + return e1 < e2 ? -1 : e1 === e2 ? 0 : 1; + } + + sortGroups = (group1: Doc, group2: Doc) => { + const g1 = StrCast(group1.groupName); + const g2 = StrCast(group2.groupName); + return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; + } + private get sharingInterface() { - const existOtherUsers = this.users.length > 0; - const existGroups = GroupManager.Instance?.getAllGroups().length > 0; - // const manager = this.sharingDoc!; + const groupList = GroupManager.Instance?.getAllGroups() || []; + + const sortedUsers = this.users.sort(this.sortUsers) + .map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })); + const sortedGroups = groupList.sort(this.sortGroups) + .map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })); const options: GroupOptions[] = GroupManager.Instance ? [ { label: 'Individuals', - options: this.users.map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })) + options: sortedUsers }, { label: 'Groups', - options: GroupManager.Instance.getAllGroups().map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })) + options: sortedGroups } ] : []; - console.log(this.users); + const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users; + const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList; - const userListContents: (JSX.Element | null)[] = this.users.map(({ user, notificationDoc }) => { // can't use async here + const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.email.replace('.', '_'); // const userKey = user.userDocumentId; const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None); @@ -422,7 +439,7 @@ export default class SharingManager extends React.Component<{}> { ) ); - const groupListContents = GroupManager.Instance?.getAllGroups().map(group => { + const groupListContents = groups.map(group => { const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`], SharingPermissions.None); // const color = ColorMapping.get(permissions); @@ -431,7 +448,10 @@ export default class SharingManager extends React.Component<{}> { key={StrCast(group.groupName)} className={"container"} > - {group.groupName} +
{group.groupName}
+
GroupManager.Instance.currentGroup = group)}> + +
-
); }); - const displayUserList = userListContents?.every(user => user === null); - const displayGroupList = groupListContents?.every(group => group === null); + const displayUserList = !userListContents?.every(user => user === null); + const displayGroupList = !groupListContents?.every(group => group === null); return (
@@ -515,10 +534,14 @@ export default class SharingManager extends React.Component<{}> { }
-
Individuals
-
{/*200*/} +
this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}> + Individuals {this.individualSort === "ascending" ? "↑" : this.individualSort === "descending" ? "↓" : ""} {/* → */} +
+
{/*200*/} { - displayUserList ? + !displayUserList ?
@@ -530,10 +553,15 @@ export default class SharingManager extends React.Component<{}> {
-
Groups
-
{/*200*/} +
this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}> + Groups {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""} {/* → */} + +
+
{/*200*/} { - displayGroupList ? + !displayGroupList ?
-- cgit v1.2.3-70-g09d2 From 3ad593cc8865d3fa1dc22bf403ad7cfaf1a751e6 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Wed, 15 Jul 2020 21:52:23 +0530 Subject: bugfix + cleanup --- src/client/util/GroupManager.scss | 25 -------- src/client/util/GroupManager.tsx | 4 -- src/client/util/GroupMemberView.scss | 10 --- src/client/util/GroupMemberView.tsx | 3 - src/client/util/SharingManager.scss | 30 --------- src/client/util/SharingManager.tsx | 106 +++++--------------------------- src/client/views/nodes/DocumentView.tsx | 22 +++---- src/fields/Doc.ts | 2 +- 8 files changed, 28 insertions(+), 174 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/GroupManager.scss b/src/client/util/GroupManager.scss index 8a2c616b1..34d4f40f8 100644 --- a/src/client/util/GroupManager.scss +++ b/src/client/util/GroupManager.scss @@ -1,8 +1,4 @@ -// @import "../views/globalCssVariables"; - .group-interface { - // background-color: whitesmoke !important; - // color: grey; width: 550px; height: 300px; @@ -12,21 +8,17 @@ flex-direction: column; height: 90%; justify-content: space-between; - // flex-basis: 30%; margin-left: 5px; input { border-radius: 5px; - // border: none; padding: 8px; min-width: 100%; - // margin: 4px 0 4px 0; border: 1px solid hsl(0, 0%, 80%); outline: none; height: 30; &:focus { - // border: unset; border: 2.5px solid #2684FF; } } @@ -43,18 +35,12 @@ } } - // .dialogue-box { - // width: 450; - // height: 300; - // } button { - // background: $lighter-alt-accent; align-self: center; outline: none; border-radius: 5px; border: 0px; - // color: #fcfbf7; text-transform: none; letter-spacing: 2px; font-size: 75%; @@ -94,10 +80,8 @@ p { font-size: 20px; text-align: left; - // margin: 0 0 20px 0; margin-right: 15px; color: black; - // width: 60%; } } @@ -112,14 +96,10 @@ } .group-body { - // display: flex; justify-content: space-between; - // max-height: 80%; height: 220; background-color: #e8e8e8; - // flex-direction: column; - // padding-left: 1em; padding-right: 1em; justify-content: space-around; text-align: left; @@ -129,17 +109,12 @@ .group-row { display: flex; - // position: relative; margin-bottom: 5px; min-height: 30px; - // border: 1px solid; - // border-radius: 10px; align-items: center; .group-name { - // position: relative; max-width: 65%; - // left: 10; margin: 0 10; color: black; } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 2d8930660..12951f2ab 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -286,7 +286,6 @@ export default class GroupManager extends React.Component<{}> { */ @action createGroup = () => { - // this.createGroupModalOpen = true; if (!this.inputRef.current?.value) { alert("Please enter a group name"); return; @@ -410,9 +409,6 @@ export default class GroupManager extends React.Component<{}> {
this.currentGroup = group)}>
- {/* */}
)}
diff --git a/src/client/util/GroupMemberView.scss b/src/client/util/GroupMemberView.scss index a34e5b989..c609c5c7b 100644 --- a/src/client/util/GroupMemberView.scss +++ b/src/client/util/GroupMemberView.scss @@ -1,18 +1,12 @@ -// @import "../views/globalCssVariables"; - .editing-interface { - // background-color: whitesmoke !important; - // color: grey; width: 100%; height: 100%; - // color: black; hr { margin-top: 20; } button { - // background: $darker-alt-accent; outline: none; border-radius: 5px; border: 0px; @@ -78,7 +72,6 @@ .editing-contents { overflow-y: auto; - // max-height: 67%; height: 65%; width: 100%; color: black; @@ -89,11 +82,8 @@ align-items: center; margin-bottom: 10px; position: relative; - // border: 1px solid; - // border-radius: 10px; .user-email { - // position: relative; min-width: 65%; word-break: break-all; padding: 0 5; diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index ebe9830ba..f20670c4e 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -28,9 +28,6 @@ export default class GroupMemberView extends React.Component !(JSON.parse(StrCast(this.props.group.members)) as string[]).includes(option.value)) : []; - console.log(this.props.group, options); - console.log(GroupManager.Instance.options); - return (!this.props.group ? null :
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 572b94ffb..130785672 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -1,16 +1,7 @@ -// @import "../views/globalCssVariables"; - .sharing-interface { - // display: flex; - // flex-direction: column; width: 600px; height: 360px; - // .dialogue-box { - // width: 450; - // height: 300; - // } - .overlay { transform: translate(-20px, -20px); } @@ -87,7 +78,6 @@ .users-list { font-style: italic; background: #e8e8e8; - // border: 1px solid black; padding-left: 10px; padding-right: 10px; overflow-y: scroll; @@ -98,7 +88,6 @@ align-items: center; text-align: center; justify-content: center; - // color: red; color: black; height: 250px; margin: 0 2; @@ -113,7 +102,6 @@ } button { - // background: $darker-alt-accent; outline: none; border-radius: 5px; border: 0px; @@ -135,8 +123,6 @@ p { font-size: 20px; text-align: left; - // font-style: italic; - // padding: 0; margin: 0 0 20px 0; color: black; } @@ -174,8 +160,6 @@ -ms-user-select: none; user-select: none; width: 100%; - // min-width: 700px; - // max-width: 700px; text-align: left; font-style: normal; font-size: 14; @@ -249,18 +233,4 @@ padding-top: 12px; } } - - // .close-button { - // border-radius: 5px; - // margin-top: 20px; - // padding: 10px 0; - // background: aliceblue; - // transition: 0.5s ease all; - // border: 1px solid; - // border-color: aliceblue; - // } - - // .close-button:hover { - // border-color: black; - // } } \ No newline at end of file diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index be86b183f..8d4e508ac 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -36,13 +36,6 @@ export enum SharingPermissions { None = "Not Shared" } -// const ColorMapping = new Map([ -// [SharingPermissions.None, "red"], -// [SharingPermissions.View, "maroon"], -// [SharingPermissions.Add, "blue"], -// [SharingPermissions.Edit, "green"] -// ]); - interface GroupOptions { label: string; options: UserOptions[]; @@ -67,10 +60,9 @@ export default class SharingManager extends React.Component<{}> { public static Instance: SharingManager; @observable private isOpen = false; @observable private users: ValidatedUser[] = []; - // @observable private groups: Doc[] = []; @observable private targetDoc: Doc | undefined; @observable private targetDocView: DocumentView | undefined; - @observable private copied = false; + // @observable private copied = false; @observable private dialogueBoxOpacity = 1; @observable private overlayOpacity = 0.4; @observable private selectedUsers: UserOptions[] | null = null; @@ -90,32 +82,20 @@ export default class SharingManager extends React.Component<{}> { this.targetDoc = target.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; - // if (!this.sharingDoc) { - // this.sharingDoc = new Doc; - // } })); - // runInAction(() => this.groups = GroupManager.Instance.getAllGroups()); } public close = action(() => { this.isOpen = false; this.users = []; setTimeout(action(() => { - this.copied = false; + // this.copied = false; DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500); }); - // private get sharingDoc() { - // return this.targetDoc ? Cast(this.targetDoc[SharingKey], Doc) as Doc : undefined; - // } - - // private set sharingDoc(value: Doc | undefined) { - // this.targetDoc && (this.targetDoc[SharingKey] = value); - // } - constructor(props: {}) { super(props); SharingManager.Instance = this; @@ -152,7 +132,6 @@ export default class SharingManager extends React.Component<{}> { Doc.GetProto(target)[ACL] = permission; group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List).push(target)) : group.docsShared = new List([target]); - // group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List).push(target) : group.docsShared = new List([target]); users.forEach(({ notificationDoc }) => { DocListCastAsync(notificationDoc[storage]).then(resolved => { @@ -171,7 +150,6 @@ export default class SharingManager extends React.Component<{}> { DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); }); }); - // DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); } } @@ -184,7 +162,6 @@ export default class SharingManager extends React.Component<{}> { DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); }); }); - // DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.notificationDoc[storage])) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); } } @@ -208,14 +185,10 @@ export default class SharingManager extends React.Component<{}> { setInternalSharing = (recipient: ValidatedUser, permission: string) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; - // const manager = this.sharingDoc!; const key = user.email.replace('.', '_'); - // const key = user.userDocumentId; const ACL = `ACL-${key}`; - // const permissions: { [key: string]: number } = target[ACL] ? JSON.parse(StrCast(target[ACL])) : {}; - target[ACL] = permission; Doc.GetProto(target)[ACL] = permission; @@ -228,47 +201,12 @@ export default class SharingManager extends React.Component<{}> { } else { DocListCastAsync(notificationDoc[storage]).then(resolved => { - Doc.IndexOf(target, resolved!) === -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); + Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); }); } } - - // let metadata = await DocCastAsync(manager[key]); - // const permissions: { [key: string]: number } = metadata?.permissions ? JSON.parse(StrCast(metadata.permissions)) : {}; - // permissions[StrCast(group ? group.groupName : Doc.CurrentUserEmail)] = parseInt(HierarchyMapping.get(permission)!); - // const max = Math.max(...Object.values(permissions)); - - // switch (max) { - // case 0: - // // if (metadata) { - // // const sharedAlias = (await DocCastAsync(metadata.sharedAlias))!; - // // Doc.RemoveDocFromList(notificationDoc, storage, sharedAlias); - // // manager[key] = undefined; - // // } - // Doc.RemoveDocFromList(notificationDoc, storage, target); - // break; - - // case 1: case 2: case 3: - - // Doc.AddDocToList(notificationDoc, storage, target); - - // if (!metadata) { - // metadata = new Doc; - // const sharedAlias = Doc.MakeAlias(target); - // Doc.AddDocToList(notificationDoc, storage, target); - // metadata.sharedAlias = sharedAlias; - // manager[key] = metadata; - // } - // metadata.permissions = JSON.stringify(permissions); - // // metadata.usersShared = JSON.stringify(keys); - // break; - // } - - // if (metadata) metadata.maxPermission = HierarchyMapping.get(`${max}`); - - // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; // if (!sharingDoc) { @@ -277,20 +215,20 @@ export default class SharingManager extends React.Component<{}> { // sharingDoc[PublicKey] = permission; // } - private get sharingUrl() { - if (!this.targetDoc) { - return undefined; - } - const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]); - return `${baseUrl}?sharing=true`; - } + // private get sharingUrl() { + // if (!this.targetDoc) { + // return undefined; + // } + // const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]); + // return `${baseUrl}?sharing=true`; + // } - copy = action(() => { - if (this.sharingUrl) { - Utils.CopyText(this.sharingUrl); - this.copied = true; - } - }); + // copy = action(() => { + // if (this.sharingUrl) { + // Utils.CopyText(this.sharingUrl); + // this.copied = true; + // } + // }); private get sharingOptions() { return Object.values(SharingPermissions).map(permission => { @@ -394,14 +332,7 @@ export default class SharingManager extends React.Component<{}> { const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => { // can't use async here const userKey = user.email.replace('.', '_'); - // const userKey = user.userDocumentId; const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None); - // const color = ColorMapping.get(permissions); - - // console.log(manager); - // const metadata = manager[userKey] as Doc; - // const usersShared = StrCast(metadata?.usersShared, ""); - // console.log(usersShared) return permissions === SharingPermissions.None || user.email === this.targetDoc?.author ? null : (
{ className={"container"} > {user.email} - {/*
{usersShared}
*/}
this.setInternalGroupSharing(group, e.currentTarget.value)} > {this.sharingOptions} @@ -582,7 +509,6 @@ export default class SharingManager extends React.Component<{}> { } render() { - // console.log(this.sharingDoc); return ( (Docu !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); - const existingAcls = cm.findByDescription("Privacy..."); - const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : []; - aclItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Make Editable", event: () => this.setAcl("write"), icon: "concierge-bell" }); - aclItems.push({ description: "Test Private", event: () => this.testAcl("ownerOnly"), icon: "concierge-bell" }); - aclItems.push({ description: "Test Readonly", event: () => this.testAcl("readOnly"), icon: "concierge-bell" }); - !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" }); - - cm.addItem({ description: `${getPlaygroundMode() ? "Disable" : "Enable"} playground mode`, event: togglePlaygroundMode, icon: "concierge-bell" }); + // const existingAcls = cm.findByDescription("Privacy..."); + // const aclItems: ContextMenuProps[] = existingAcls && "subitems" in existingAcls ? existingAcls.subitems : []; + // aclItems.push({ description: "Make Add Only", event: () => this.setAcl(SharingPermissions.Add), icon: "concierge-bell" }); + // aclItems.push({ description: "Make Read Only", event: () => this.setAcl(SharingPermissions.View), icon: "concierge-bell" }); + // aclItems.push({ description: "Make Private", event: () => this.setAcl(SharingPermissions.None), icon: "concierge-bell" }); + // aclItems.push({ description: "Make Editable", event: () => this.setAcl(SharingPermissions.Edit), icon: "concierge-bell" }); + // aclItems.push({ description: "Test Private", event: () => this.testAcl(SharingPermissions.None), icon: "concierge-bell" }); + // aclItems.push({ description: "Test Readonly", event: () => this.testAcl(SharingPermissions.View), icon: "concierge-bell" }); + // !existingAcls && cm.addItem({ description: "Privacy...", subitems: aclItems, icon: "question" }); + + // cm.addItem({ description: `${getPlaygroundMode() ? "Disable" : "Enable"} playground mode`, event: togglePlaygroundMode, icon: "concierge-bell" }); // const recommender_subitems: ContextMenuProps[] = []; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 8ab4735a7..5dfc14a4a 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -109,7 +109,7 @@ const AclMap = new Map([ ]); export function fetchProto(doc: Doc) { - if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions + if (doc.author !== Doc.CurrentUserEmail) { untracked(() => { const permissions: { [key: string]: symbol } = {}; -- cgit v1.2.3-70-g09d2 From 7543ca061700fda8286e6dd3f4374a877ccf929c Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 16 Jul 2020 13:00:12 +0530 Subject: can't change acl field to non-acl (through kvp) now + added playground mode button to settings --- src/client/util/SettingsManager.scss | 2 ++ src/client/util/SettingsManager.tsx | 13 +++++++++++-- src/client/util/SharingManager.tsx | 2 -- src/client/views/nodes/DocumentView.tsx | 2 +- src/fields/util.ts | 3 +++ 5 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index 6d394a38d..c1627e69f 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -52,6 +52,7 @@ .settings-body { display: flex; justify-content: space-between; + margin-top: -10; .settings-type { display: flex; @@ -105,6 +106,7 @@ text-transform: uppercase; letter-spacing: 2px; font-size: 120%; + margin-top: 0; } .container { diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index d54a39943..9d91568cf 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -14,8 +14,9 @@ import { Doc } from "../../fields/Doc"; import GroupManager from "./GroupManager"; import HypothesisAuthenticationManager from "../apis/HypothesisAuthenticationManager"; import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager"; +import { togglePlaygroundMode } from "../../fields/util"; -library.add(fa.faWindowClose); +library.add(fa.faTimes); @observer export default class SettingsManager extends React.Component<{}> { @@ -26,6 +27,7 @@ export default class SettingsManager extends React.Component<{}> { @observable private settingsContent = "password"; @observable private errorText = ""; @observable private successText = ""; + @observable private playgroundMode = false; private curr_password_ref = React.createRef(); private new_password_ref = React.createRef(); private new_confirm_ref = React.createRef(); @@ -95,19 +97,26 @@ export default class SettingsManager extends React.Component<{}> { HypothesisAuthenticationManager.Instance.fetchAccessToken(true) } + @action + togglePlaygroundMode = () => { + togglePlaygroundMode(); + this.playgroundMode = !this.playgroundMode; + } + private get settingsInterface() { return (

settings

- +
+ + -
{this.settingsContent === "password" ?
@@ -155,8 +155,6 @@ export default class SettingsManager extends React.Component<{}> { contents={this.settingsInterface} isDisplayed={this.isOpen} interactive={true} - dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} - overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} /> ); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index fe7324d5c..8b3ac2613 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,13 +1,12 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocListCastAsync } from "../../fields/Doc"; +import { Doc, Opt, DocListCastAsync, DataSym, DocListCast } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; import { Utils } from "../../Utils"; import "./SharingManager.scss"; -import { Id } from "../../fields/FieldSymbols"; import { observer } from "mobx-react"; import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; @@ -82,6 +81,7 @@ export default class SharingManager extends React.Component<{}> { this.targetDoc = target.props.Document; DictationOverlay.Instance.hasActiveModal = true; this.isOpen = true; + this.permissions = SharingPermissions.Edit; })); } @@ -127,9 +127,11 @@ export default class SharingManager extends React.Component<{}> { const target = this.targetDoc!; const ACL = `ACL-${StrCast(group.groupName)}`; + // fix this - not needed (here and setinternalsharing and removegroup) + // target[ACL] = permission; + // Doc.GetProto(target)[ACL] = permission; - target[ACL] = permission; - Doc.GetProto(target)[ACL] = permission; + this.distributeAcls(ACL, permission as SharingPermissions); group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List).push(target)) : group.docsShared = new List([target]); @@ -170,7 +172,9 @@ export default class SharingManager extends React.Component<{}> { DocListCastAsync(group.docsShared).then(resolved => { resolved?.forEach(doc => { const ACL = `ACL-${StrCast(group.groupName)}`; - doc[ACL] = "Not Shared"; + // doc[ACL] = doc[DataSym][ACL] = "Not Shared"; + + this.distributeAcls(ACL, SharingPermissions.None, doc); const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -189,8 +193,10 @@ export default class SharingManager extends React.Component<{}> { const ACL = `ACL-${key}`; - target[ACL] = permission; - Doc.GetProto(target)[ACL] = permission; + // target[ACL] = permission; + // Doc.GetProto(target)[ACL] = permission; + + this.distributeAcls(ACL, permission as SharingPermissions); if (permission !== SharingPermissions.None) { DocListCastAsync(notificationDoc[storage]).then(resolved => { @@ -202,6 +208,40 @@ export default class SharingManager extends React.Component<{}> { Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); }); } + } + + @action + distributeAcls = (key: string, acl: SharingPermissions, doc?: Doc) => { + const target = doc ? doc : this.targetDoc!; + const dataDoc = target[DataSym]; + target[key] = acl; + if (dataDoc) dataDoc[key] = acl; + // dataDoc[key] = target[key] = acl; + // next line distributes the acl to all children of the target + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { + if (d.author === Doc.CurrentUserEmail) { + this.distributeAcls(key, acl, d); + d[key] = acl; + } + const data = d[DataSym]; + if (data && data.author === Doc.CurrentUserEmail) { + this.distributeAcls(key, acl, data); + data[key] = acl; + } + }); + + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { + if (d.author === Doc.CurrentUserEmail) { + this.distributeAcls(key, acl, d); + d[key] = acl; + } + const data = d[DataSym]; + if (data && data.author === Doc.CurrentUserEmail) { + this.distributeAcls(key, acl, data); + data[key] = acl; + } + console.log(d, d[DataSym]); + }); } @@ -308,9 +348,9 @@ export default class SharingManager extends React.Component<{}> { const groupList = GroupManager.Instance?.getAllGroups() || []; const sortedUsers = this.users.sort(this.sortUsers) - .map(({ user: { email } }) => ({ label: email, value: "!indType/" + email })); + .map(({ user: { email } }) => ({ label: email, value: indType + email })); const sortedGroups = groupList.sort(this.sortGroups) - .map(({ groupName }) => ({ label: StrCast(groupName), value: "!groupType/" + StrCast(groupName) })); + .map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) })); const options: GroupOptions[] = GroupManager.Instance ? [ diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index eb58d8a3e..8740d17c2 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,4 +1,4 @@ -import { Doc, Opt, DataSym, DocListCast, AclReadonly, AclAddonly } from '../../fields/Doc'; +import { Doc, Opt, DataSym, DocListCast, AclReadonly, AclAddonly, AclPrivate, AclEdit, AclSym } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; @@ -7,7 +7,8 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { GetEffectiveAcl } from '../../fields/util'; +import { GetEffectiveAcl, getPlaygroundMode } from '../../fields/util'; +import { SharingPermissions } from '../util/SharingManager'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -92,6 +93,13 @@ export function ViewBoxAnnotatableComponent

([ + [AclPrivate, SharingPermissions.None], + [AclReadonly, SharingPermissions.View], + [AclAddonly, SharingPermissions.Add], + [AclEdit, SharingPermissions.Edit] + ]); + lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; styleFromLayoutString = (scale: number) => { @@ -139,11 +147,21 @@ export function ViewBoxAnnotatableComponent

!docList.includes(d)); const effectiveAcl = GetEffectiveAcl(this.dataDoc); + + if (this.props.Document[AclSym]) { + added.forEach(d => { + const dataDoc = d[DataSym]; + dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + dataDoc[key] = d[key] = this.AclMap.get(value); + } + }); + } if (added.length) { - if (effectiveAcl === AclReadonly) { + if (effectiveAcl === AclReadonly && !getPlaygroundMode()) { return false; } else if (effectiveAcl === AclAddonly) { - added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc)); + added.map(doc => console.log(Doc.AddDocToList(targetDataDoc, this.annotationKey, doc))); } else { added.map(doc => doc.context = this.props.Document); targetDataDoc[this.annotationKey] = new List([...docList, ...added]); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5c6781f4c..61d2246db 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -91,7 +91,7 @@ export class MainView extends React.Component { public isPointerDown = false; componentDidMount() { - DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus", "data-annotations"]); // can play with these fields on someone else's const tag = document.createElement('script'); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 50d66c567..17567ea73 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../fields/DateField'; -import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit } from '../../../fields/Doc'; +import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -48,6 +48,7 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import CollectionMenu from './CollectionMenu'; +import { SharingPermissions } from '../../util/SharingManager'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -106,6 +107,13 @@ export class CollectionView extends Touchable([ + [AclPrivate, SharingPermissions.None], + [AclReadonly, SharingPermissions.View], + [AclAddonly, SharingPermissions.Add], + [AclEdit, SharingPermissions.Edit] + ]); + get collectionViewType(): CollectionViewType | undefined { const viewField = StrCast(this.props.Document._viewType); if (CollectionView._safeMode) { @@ -128,11 +136,26 @@ export class CollectionView extends Touchable !docList.includes(d)); const effectiveAcl = GetEffectiveAcl(this.props.Document); + if (this.props.Document[AclSym]) { + // change so it only adds if more restrictive + added.forEach(d => { + console.log(d[Id]); + const dataDoc = d[DataSym]; + console.log(dataDoc[Id]); + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + dataDoc[key] = d[key] = this.AclMap.get(value); + } + dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + + }); + } + if (added.length) { if (effectiveAcl === AclReadonly && !getPlaygroundMode()) { return false; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5dfc14a4a..ef57171bf 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -109,15 +109,15 @@ const AclMap = new Map([ ]); export function fetchProto(doc: Doc) { - if (doc.author !== Doc.CurrentUserEmail) { - untracked(() => { - const permissions: { [key: string]: symbol } = {}; + // if (doc.author !== Doc.CurrentUserEmail) { + untracked(() => { + const permissions: { [key: string]: symbol } = {}; - Object.keys(doc).filter(key => key.startsWith("ACL")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!); + Object.keys(doc).filter(key => key.startsWith("ACL")).forEach(key => permissions[key] = AclMap.get(StrCast(doc[key]))!); - if (Object.keys(permissions).length) doc[AclSym] = permissions; - }); - } + if (Object.keys(permissions).length) doc[AclSym] = permissions; + }); + // } if (doc.proto instanceof Promise) { doc.proto.then(fetchProto); diff --git a/src/fields/util.ts b/src/fields/util.ts index 6d2d715bd..ee01f6213 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -129,28 +129,31 @@ export function setGroups(groups: string[]) { export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclEdit; - const HierarchyMapping = new Map([ - [AclPrivate, 0], - [AclReadonly, 1], - [AclAddonly, 2], - [AclEdit, 3] - ]); - if (!target[AclSym] && target instanceof Doc) { fetchProto(target); } + if (target[AclSym] && Object.keys(target[AclSym]).length) { - if (target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit; + // console.log(target[AclSym]); + + if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit; if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; - if (target[AclSym].ACL) return target[AclSym].ACL; + // if (target[AclSym].ACL) return target[AclSym].ACL; let effectiveAcl = AclPrivate; let aclPresent = false; + const HierarchyMapping = new Map([ + [AclPrivate, 0], + [AclReadonly, 1], + [AclAddonly, 2], + [AclEdit, 3] + ]); + for (const [key, value] of Object.entries(target[AclSym])) { if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { if (HierarchyMapping.get(value as symbol)! >= HierarchyMapping.get(effectiveAcl)!) { -- cgit v1.2.3-70-g09d2 From c4499c610f377be4b80cf2999d25f97b619d4727 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Tue, 21 Jul 2020 17:17:47 +0530 Subject: distributing acls shifted to util.ts --- src/client/util/SharingManager.tsx | 47 +++------------ src/client/views/DocComponent.tsx | 34 ++++++----- src/client/views/collections/CollectionView.tsx | 76 +++++++++++++------------ src/fields/util.ts | 46 ++++++++++++++- 4 files changed, 109 insertions(+), 94 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 8b3ac2613..d3bc84770 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,4 +1,4 @@ -import { observable, runInAction, action } from "mobx"; +import { observable, runInAction, action, computed } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; import { Doc, Opt, DocListCastAsync, DataSym, DocListCast } from "../../fields/Doc"; @@ -20,6 +20,7 @@ import GroupMemberView from "./GroupMemberView"; import Select from "react-select"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { List } from "../../fields/List"; +import { distributeAcls } from "../../fields/util"; library.add(fa.faCopy, fa.faTimes); @@ -131,7 +132,7 @@ export default class SharingManager extends React.Component<{}> { // target[ACL] = permission; // Doc.GetProto(target)[ACL] = permission; - this.distributeAcls(ACL, permission as SharingPermissions); + distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!); group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List).push(target)) : group.docsShared = new List([target]); @@ -174,7 +175,7 @@ export default class SharingManager extends React.Component<{}> { const ACL = `ACL-${StrCast(group.groupName)}`; // doc[ACL] = doc[DataSym][ACL] = "Not Shared"; - this.distributeAcls(ACL, SharingPermissions.None, doc); + distributeAcls(ACL, SharingPermissions.None, doc); const members: string[] = JSON.parse(StrCast(group.members)); const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email)); @@ -186,6 +187,7 @@ export default class SharingManager extends React.Component<{}> { } } + // @action setInternalSharing = (recipient: ValidatedUser, permission: string) => { const { user, notificationDoc } = recipient; const target = this.targetDoc!; @@ -196,7 +198,7 @@ export default class SharingManager extends React.Component<{}> { // target[ACL] = permission; // Doc.GetProto(target)[ACL] = permission; - this.distributeAcls(ACL, permission as SharingPermissions); + distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!); if (permission !== SharingPermissions.None) { DocListCastAsync(notificationDoc[storage]).then(resolved => { @@ -210,40 +212,6 @@ export default class SharingManager extends React.Component<{}> { } } - @action - distributeAcls = (key: string, acl: SharingPermissions, doc?: Doc) => { - const target = doc ? doc : this.targetDoc!; - const dataDoc = target[DataSym]; - target[key] = acl; - if (dataDoc) dataDoc[key] = acl; - // dataDoc[key] = target[key] = acl; - // next line distributes the acl to all children of the target - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail) { - this.distributeAcls(key, acl, d); - d[key] = acl; - } - const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail) { - this.distributeAcls(key, acl, data); - data[key] = acl; - } - }); - - DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { - if (d.author === Doc.CurrentUserEmail) { - this.distributeAcls(key, acl, d); - d[key] = acl; - } - const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail) { - this.distributeAcls(key, acl, data); - data[key] = acl; - } - console.log(d, d[DataSym]); - }); - - } // private setExternalSharing = (permission: string) => { // const sharingDoc = this.sharingDoc; @@ -344,7 +312,6 @@ export default class SharingManager extends React.Component<{}> { } private get sharingInterface() { - const groupList = GroupManager.Instance?.getAllGroups() || []; const sortedUsers = this.users.sort(this.sortUsers) @@ -368,7 +335,7 @@ export default class SharingManager extends React.Component<{}> { const users = this.individualSort === "ascending" ? this.users.sort(this.sortUsers) : this.individualSort === "descending" ? this.users.sort(this.sortUsers).reverse() : this.users; const groups = this.groupSort === "ascending" ? groupList.sort(this.sortGroups) : this.groupSort === "descending" ? groupList.sort(this.sortGroups).reverse() : groupList; - const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => { // can't use async here + const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => { const userKey = user.email.replace('.', '_'); const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2519360da..655be80ef 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -150,24 +150,28 @@ export function ViewBoxAnnotatableComponent

!docList.includes(d)); const effectiveAcl = GetEffectiveAcl(this.dataDoc); - if (this.props.Document[AclSym]) { - added.forEach(d => { - const dataDoc = d[DataSym]; - dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - dataDoc[key] = d[key] = this.AclMap.get(value); - } - }); - } if (added.length) { if (effectiveAcl === AclReadonly && !getPlaygroundMode()) { return false; - } else if (effectiveAcl === AclAddonly) { - added.map(doc => console.log(Doc.AddDocToList(targetDataDoc, this.annotationKey, doc))); - } else { - added.map(doc => doc.context = this.props.Document); - targetDataDoc[this.annotationKey] = new List([...docList, ...added]); - targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now())); + } + else { + if (this.props.Document[AclSym]) { + added.forEach(d => { + const dataDoc = d[DataSym]; + dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + dataDoc[key] = d[key] = this.AclMap.get(value); + } + }); + } + if (effectiveAcl === AclAddonly) { + added.map(doc => console.log(Doc.AddDocToList(targetDataDoc, this.annotationKey, doc))); + } + else { + added.map(doc => doc.context = this.props.Document); + targetDataDoc[this.annotationKey] = new List([...docList, ...added]); + targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now())); + } } } return true; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 17567ea73..5cef6c44e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -17,7 +17,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { TraceMobx, GetEffectiveAcl, getPlaygroundMode } from '../../../fields/util'; +import { TraceMobx, GetEffectiveAcl, getPlaygroundMode, distributeAcls } from '../../../fields/util'; import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -142,46 +142,48 @@ export class CollectionView extends Touchable !docList.includes(d)); const effectiveAcl = GetEffectiveAcl(this.props.Document); - if (this.props.Document[AclSym]) { - // change so it only adds if more restrictive - added.forEach(d => { - console.log(d[Id]); - const dataDoc = d[DataSym]; - console.log(dataDoc[Id]); - for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - dataDoc[key] = d[key] = this.AclMap.get(value); - } - dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; - - }); - } if (added.length) { if (effectiveAcl === AclReadonly && !getPlaygroundMode()) { return false; - } else if (effectiveAcl === AclAddonly) { - added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc)); - } else { - added.map(doc => { - const context = Cast(doc.context, Doc, null); - if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { - const pushpin = Docs.Create.FontIconDocument({ - title: "pushpin", - icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7", - _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) - }); - pushpin.isPushpin = true; - Doc.GetProto(pushpin).annotationOn = doc.annotationOn; - Doc.SetInPlace(doc, "annotationOn", undefined, true); - Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin); - const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", ""); - doc.displayTimecode = undefined; - } - doc.context = this.props.Document; - }); - added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add)); - targetDataDoc[this.props.fieldKey] = new List([...docList, ...added]); - targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + } + else { + if (this.props.Document[AclSym]) { + // change so it only adds if more restrictive + added.forEach(d => { + const dataDoc = d[DataSym]; + for (const [key, value] of Object.entries(this.props.Document[AclSym])) { + distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d); + } + dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + }); + } + + if (effectiveAcl === AclAddonly) { + added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc)); + } + else { + added.map(doc => { + const context = Cast(doc.context, Doc, null); + if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { + const pushpin = Docs.Create.FontIconDocument({ + title: "pushpin", + icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7", + _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) + }); + pushpin.isPushpin = true; + Doc.GetProto(pushpin).annotationOn = doc.annotationOn; + Doc.SetInPlace(doc, "annotationOn", undefined, true); + Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin); + const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", ""); + doc.displayTimecode = undefined; + } + doc.context = this.props.Document; + }); + added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add)); + targetDataDoc[this.props.fieldKey] = new List([...docList, ...added]); + targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + } } } return true; diff --git a/src/fields/util.ts b/src/fields/util.ts index ee01f6213..a714b01e3 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto } from "./Doc"; +import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto, DataSym, DocListCast } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; @@ -8,7 +8,8 @@ import { action, trace } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; -import { ScriptCast } from "./Types"; +import { ScriptCast, StrCast } from "./Types"; +import { SharingPermissions } from "../client/util/SharingManager"; function _readOnlySetter(): never { @@ -168,6 +169,47 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) return AclEdit; } +export function distributeAcls(key: string, acl: SharingPermissions, target: Doc) { + + const HierarchyMapping = new Map([ + ["Not Shared", 0], + ["Can View", 1], + ["Can Add", 2], + ["Can Edit", 3] + ]); + + const dataDoc = target[DataSym]; + + if (!target[key] || HierarchyMapping.get(StrCast(target[key]))! < HierarchyMapping.get(acl)!) target[key] = acl; + + if (dataDoc && (!dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! < HierarchyMapping.get(acl)!)) { + dataDoc[key] = acl; + + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { + if (d.author === Doc.CurrentUserEmail && d[key] && HierarchyMapping.get(StrCast(d[key]))! < HierarchyMapping.get(acl)!) { + distributeAcls(key, acl, d); + d[key] = acl; + } + const data = d[DataSym]; + if (data && data.author === Doc.CurrentUserEmail && data[key] && HierarchyMapping.get(StrCast(data[key]))! < HierarchyMapping.get(acl)!) { + distributeAcls(key, acl, data); + data[key] = acl; + } + }); + + DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { + if (d.author === Doc.CurrentUserEmail && d[key] && HierarchyMapping.get(StrCast(d[key]))! < HierarchyMapping.get(acl)!) { + distributeAcls(key, acl, d); + d[key] = acl; + } + const data = d[DataSym]; + if (data && data.author === Doc.CurrentUserEmail && data[key] && HierarchyMapping.get(StrCast(data[key]))! < HierarchyMapping.get(acl)!) { + distributeAcls(key, acl, data); + data[key] = acl; + } + }); + } +} const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; -- cgit v1.2.3-70-g09d2 From 3d06cdd362d58dfbc8d6efdcd9dc59250ab003a4 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Tue, 21 Jul 2020 23:16:49 +0530 Subject: distributeAcls only changes if container is more restrictive --- src/client/util/SharingManager.tsx | 14 +++++--------- src/client/views/DocComponent.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 6 +++--- src/client/views/nodes/DocumentView.tsx | 5 +++-- src/fields/util.ts | 18 +++++++----------- 5 files changed, 19 insertions(+), 26 deletions(-) (limited to 'src/client/util/SharingManager.tsx') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index d3bc84770..9c857a7c0 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,7 +1,7 @@ -import { observable, runInAction, action, computed } from "mobx"; +import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocListCastAsync, DataSym, DocListCast } from "../../fields/Doc"; +import { Doc, Opt, DocListCastAsync } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; @@ -41,9 +41,9 @@ interface GroupOptions { options: UserOptions[]; } -const SharingKey = "sharingPermissions"; -const PublicKey = "publicLinkPermissions"; -const DefaultColor = "black"; +// const SharingKey = "sharingPermissions"; +// const PublicKey = "publicLinkPermissions"; +// const DefaultColor = "black"; const groupType = "!groupType/"; const indType = "!indType/"; @@ -192,12 +192,8 @@ export default class SharingManager extends React.Component<{}> { const { user, notificationDoc } = recipient; const target = this.targetDoc!; const key = user.email.replace('.', '_'); - const ACL = `ACL-${key}`; - // target[ACL] = permission; - // Doc.GetProto(target)[ACL] = permission; - distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!); if (permission !== SharingPermissions.None) { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 655be80ef..95c1bcda8 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -165,7 +165,7 @@ export function ViewBoxAnnotatableComponent

console.log(Doc.AddDocToList(targetDataDoc, this.annotationKey, doc))); + added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc)); } else { added.map(doc => doc.context = this.props.Document); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 5cef6c44e..9b04deff5 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -151,11 +151,11 @@ export class CollectionView extends Touchable { - const dataDoc = d[DataSym]; + // const dataDoc = d[DataSym]; for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d); + distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); } - dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; + // dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym]; }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 803720417..0b5bd707b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate, AclReadonly } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate, AclEdit } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -707,6 +707,7 @@ export class DocumentView extends DocComponent(Docu if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl; }); } + @undoBatch @action testAcl = (acl: SharingPermissions) => { @@ -806,7 +807,7 @@ export class DocumentView extends DocComponent(Docu }); moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); } - moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); + GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" }); moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "external-link-alt" }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); diff --git a/src/fields/util.ts b/src/fields/util.ts index a714b01e3..81ccbf6d9 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -137,14 +137,10 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) if (target[AclSym] && Object.keys(target[AclSym]).length) { - // console.log(target[AclSym]); - if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit; if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; - // if (target[AclSym].ACL) return target[AclSym].ACL; - let effectiveAcl = AclPrivate; let aclPresent = false; @@ -169,7 +165,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) return AclEdit; } -export function distributeAcls(key: string, acl: SharingPermissions, target: Doc) { +export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) { const HierarchyMapping = new Map([ ["Not Shared", 0], @@ -180,30 +176,30 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc const dataDoc = target[DataSym]; - if (!target[key] || HierarchyMapping.get(StrCast(target[key]))! < HierarchyMapping.get(acl)!) target[key] = acl; + if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) target[key] = acl; - if (dataDoc && (!dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! < HierarchyMapping.get(acl)!)) { + if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { dataDoc[key] = acl; DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail && d[key] && HierarchyMapping.get(StrCast(d[key]))! < HierarchyMapping.get(acl)!) { + if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d); d[key] = acl; } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && data[key] && HierarchyMapping.get(StrCast(data[key]))! < HierarchyMapping.get(acl)!) { + if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data); data[key] = acl; } }); DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { - if (d.author === Doc.CurrentUserEmail && d[key] && HierarchyMapping.get(StrCast(d[key]))! < HierarchyMapping.get(acl)!) { + if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d); d[key] = acl; } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && data[key] && HierarchyMapping.get(StrCast(data[key]))! < HierarchyMapping.get(acl)!) { + if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data); data[key] = acl; } -- cgit v1.2.3-70-g09d2