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/fields/Doc.ts | 8 ++++---- src/fields/util.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/fields') 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 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/fields') 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 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/fields') 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/fields') 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/fields') 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/fields') 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 From 9d530f8fc7ca9621274a38260faebe797f66dd60 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jul 2020 23:57:55 -0400 Subject: fixed problem where adding to a collection would cause a remote synchronized collection to erase and redraw since all of its list items got replaced. the fix was to update the 'cache' field of ProxyFields when they are deserialized to use an existing cached value if available. --- src/fields/Proxy.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/fields') diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts index 555faaad0..62734d3d2 100644 --- a/src/fields/Proxy.ts +++ b/src/fields/Proxy.ts @@ -9,7 +9,12 @@ import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; import { Plugins } from "./util"; -@Deserializable("proxy") +function deserializeProxy(field: any) { + if (!field.cache) { + field.cache = DocServer.GetCachedRefField(field.fieldId) as any; + } +} +@Deserializable("proxy", deserializeProxy) export class ProxyField extends ObjectField { constructor(); constructor(value: T); @@ -17,6 +22,7 @@ export class ProxyField extends ObjectField { constructor(value?: T | string) { super(); if (typeof value === "string") { + this.cache = DocServer.GetCachedRefField(value) as any; this.fieldId = value; } else if (value) { this.cache = value; -- cgit v1.2.3-70-g09d2