From c041b8f619957d4058c42cca894409418fde54f6 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Mon, 27 Jul 2020 11:43:38 +0530
Subject: css changes

---
 src/client/util/SharingManager.tsx | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 452a58d21..9fbdfa8e5 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -294,7 +294,7 @@ export default class SharingManager extends React.Component<{}> {
 
             const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
             TaskCompletionBox.popupX = left - 1.5 * width;
-            TaskCompletionBox.popupY = top - height;
+            TaskCompletionBox.popupY = top - 1.5 * height;
             TaskCompletionBox.textDisplayed = "Document shared!";
             TaskCompletionBox.taskCompleted = true;
             setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
@@ -460,6 +460,11 @@ export default class SharingManager extends React.Component<{}> {
                                 options={options}
                                 onChange={this.handleUsersChange}
                                 value={this.selectedUsers}
+                                styles={{
+                                    indicatorSeparator: () => ({
+                                        visibility: "hidden"
+                                    })
+                                }}
                             />
                             <select className="permissions-select" onChange={this.handlePermissionsChange}>
                                 {this.sharingOptions}
-- 
cgit v1.2.3-70-g09d2


From 716751fa420eda3a3f8322f579255a6532ddc7f5 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Mon, 27 Jul 2020 18:34:31 +0530
Subject: dropdown filters + comments

---
 src/client/util/GroupManager.tsx    |   5 +-
 src/client/util/SharingManager.scss |  62 +++++++++-----
 src/client/util/SharingManager.tsx  | 164 ++++++++++++++++++++++--------------
 src/fields/util.ts                  |  55 ++++++++----
 4 files changed, 183 insertions(+), 103 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 72fba5c1b..551216fa4 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -20,6 +20,9 @@ import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 library.add(fa.faPlus, fa.faTimes, fa.faInfoCircle);
 
+/**
+ * Interface for options for the react-select component
+ */
 export interface UserOptions {
     label: string;
     value: string;
@@ -30,8 +33,6 @@ export default class GroupManager extends React.Component<{}> {
 
     static Instance: GroupManager;
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
-    @observable private dialogueBoxOpacity: number = 1; // opacity of the dialogue box div of the MainViewModal.
-    @observable private overlayOpacity: number = 0.4; // opacity of the overlay div of the MainViewModal.
     @observable private users: string[] = []; // list of users populated from the database.
     @observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown.
     @observable currentGroup: Opt<Doc>; // the currently selected group.
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index d71ff0cf6..2ce9f232c 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -23,33 +23,51 @@
             z-index: 999;
         }
 
-        .share-setup {
-            display: flex;
-            margin-bottom: 20px;
-            align-items: center;
-            height: 36;
+        .share-container {
+            .share-setup {
+                display: flex;
+                margin-bottom: 20px;
+                align-items: center;
+                height: 36;
 
-            .user-search {
-                width: 90%;
+                .user-search {
+                    width: 90%;
 
-                input {
-                    height: 30;
+                    input {
+                        height: 30;
+                    }
+                }
+
+                .permissions-select {
+                    z-index: 1;
+                    margin-left: -100;
+                    border: none;
+                    outline: none;
+                    text-align: justify; // for Edge
+                    text-align-last: end;
                 }
-            }
 
-            .permissions-select {
-                z-index: 1;
-                margin-left: -100;
-                border: none;
-                outline: none;
-                text-align: justify; // for Edge
-                text-align-last: end;
+                .share-button {
+                    height: 105%;
+                    margin-left: 2%;
+                    background-color: #979797;
+                }
             }
 
-            .share-button {
-                height: 105%;
-                margin-left: 2%;
-                background-color: #979797;
+            .sort-checkboxes {
+                float: left;
+                margin-top: -17px;
+                margin-bottom: 10px;
+                font-size: 10px;
+
+                input {
+                    height: 10px;
+                }
+
+                label {
+                    font-weight: normal;
+                    font-style: italic;
+                }
             }
         }
 
@@ -92,10 +110,8 @@
                     height: 250px;
                     margin: 0 2;
 
-
                     .none {
                         font-style: italic;
-
                     }
                 }
             }
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 9fbdfa8e5..ec04bdd81 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -30,7 +30,10 @@ export interface User {
     userDocumentId: string;
 }
 
-interface GroupOptions {
+/**
+ * Interface for grouped options for the react-select component.
+ */
+interface GroupedOptions {
     label: string;
     options: UserOptions[];
 }
@@ -39,9 +42,13 @@ interface GroupOptions {
 // const PublicKey = "publicLinkPermissions";
 // const DefaultColor = "black";
 
-const groupType = "!groupType/";
+// used to differentiate between individuals and groups when sharing
 const indType = "!indType/";
+const groupType = "!groupType/";
 
+/**
+ * A user who also has a notificationDoc.
+ */
 interface ValidatedUser {
     user: User;
     notificationDoc: Doc;
@@ -52,18 +59,21 @@ const storage = "data";
 @observer
 export default class SharingManager extends React.Component<{}> {
     public static Instance: SharingManager;
-    @observable private isOpen = false;
-    @observable private users: ValidatedUser[] = [];
-    @observable private targetDoc: Doc | undefined;
-    @observable private targetDocView: DocumentView | undefined;
+    @observable private isOpen = false; // whether the SharingManager modal is open or not
+    @observable private users: ValidatedUser[] = []; // the list of users with notificationDocs
+    @observable private targetDoc: Doc | undefined; // the document being shared
+    @observable private targetDocView: DocumentView | undefined; // the DocumentView of the document being shared
     // @observable private copied = false;
-    @observable private dialogueBoxOpacity = 1;
-    @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 shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
+    @observable private dialogueBoxOpacity = 1; // for the modal
+    @observable private overlayOpacity = 0.4; // for the modal
+    @observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with
+    @observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users
+    @observable private individualSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of individuals
+    @observable private groupSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of groups
+    private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
+    // if both showUserOptions and showGroupOptions are false then both are displayed
+    @observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component)
+    @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
 
 
 
@@ -85,7 +95,7 @@ export default class SharingManager extends React.Component<{}> {
 
     public close = action(() => {
         this.isOpen = false;
-        this.users = [];
+        this.users = []; // resets the list of users and seleected users (in the react-select component)
         this.selectedUsers = null;
 
         setTimeout(action(() => {
@@ -100,6 +110,9 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    /**
+     * Populates the list of validated users (this.users) by adding registered users which have a rightSidebarCollection.
+     */
     populateUsers = async () => {
         const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
         const raw = JSON.parse(userList) as User[];
@@ -120,58 +133,74 @@ export default class SharingManager extends React.Component<{}> {
         return Promise.all(evaluating);
     }
 
+    /**
+     * Sets the permission on the target for the group.
+     * @param group 
+     * @param permission 
+     */
     setInternalGroupSharing = (group: Doc, permission: string) => {
         const members: string[] = JSON.parse(StrCast(group.members));
         const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
         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;
 
-        distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!);
+        distributeAcls(ACL, permission as SharingPermissions, target);
 
+        // if documents have been shared, add the target to that list if it doesn't already exist, otherwise create a new list with the target
         group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List<Doc>).push(target)) : group.docsShared = new List<Doc>([target]);
 
         users.forEach(({ notificationDoc }) => {
             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);
+                if (permission !== SharingPermissions.None) Doc.IndexOf(target, resolved!) === -1 && Doc.AddDocToList(notificationDoc, storage, target); // add the target to the notificationDoc if it hasn't already been added
+                else Doc.IndexOf(target, resolved!) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target); // remove the target from the list if it already exists
             });
         });
     }
 
+    /**
+     * Shares the documents shared with a group with a new user who has been added to that group.
+     * @param group 
+     * @param emailId 
+     */
     shareWithAddedMember = (group: Doc, emailId: string) => {
         const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
 
         if (group.docsShared) {
             DocListCastAsync(group.docsShared).then(docsShared => {
                 docsShared?.forEach(doc => {
-                    DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc));
+                    DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) === -1 && Doc.AddDocToList(user.notificationDoc, storage, doc)); // add the doc if it isn't already in the list
                 });
             });
         }
     }
 
+    /**
+     * Removes the documents shared with a user through a group when the user is removed from the group.
+     * @param group 
+     * @param emailId 
+     */
     removeMember = (group: Doc, emailId: string) => {
         const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
 
         if (group.docsShared) {
             DocListCastAsync(group.docsShared).then(docsShared => {
                 docsShared?.forEach(doc => {
-                    DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc));
+                    DocListCastAsync(user.notificationDoc[storage]).then(resolved => Doc.IndexOf(doc, resolved!) !== -1 && Doc.RemoveDocFromList(user.notificationDoc, storage, doc)); // remove the doc only if it is in the list
                 });
             });
         }
     }
 
+    /**
+     * Removes a group's permissions from documents that have been shared with it.
+     * @param group 
+     */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
             DocListCastAsync(group.docsShared).then(resolved => {
                 resolved?.forEach(doc => {
                     const ACL = `ACL-${StrCast(group.groupName)}`;
-                    // doc[ACL] = doc[DataSym][ACL] = "Not Shared";
 
                     distributeAcls(ACL, SharingPermissions.None, doc);
 
@@ -185,7 +214,6 @@ export default class SharingManager extends React.Component<{}> {
         }
     }
 
-    // @action
     setInternalSharing = (recipient: ValidatedUser, permission: string) => {
         const { user, notificationDoc } = recipient;
         const target = this.targetDoc!;
@@ -323,18 +351,32 @@ export default class SharingManager extends React.Component<{}> {
         const sortedGroups = groupList.sort(this.sortGroups)
             .map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) }));
 
-        const options: GroupOptions[] = GroupManager.Instance ?
-            [
-                {
+        const options: GroupedOptions[] = [];
+
+        if (GroupManager.Instance) {
+            if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) {
+                options.push({
                     label: 'Individuals',
                     options: sortedUsers
                 },
-                {
+                    {
+                        label: 'Groups',
+                        options: sortedGroups
+                    });
+            }
+            else if (this.showUserOptions) {
+                options.push({
+                    label: 'Individuals',
+                    options: sortedUsers
+                });
+            }
+            else {
+                options.push({
                     label: 'Groups',
                     options: sortedGroups
-                }
-            ]
-            : [];
+                });
+            }
+        }
 
         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;
@@ -403,7 +445,6 @@ export default class SharingManager extends React.Component<{}> {
             );
         });
 
-        const displayUserList = !userListContents?.every(user => user === null);
         const displayGroupList = !groupListContents?.every(group => group === null);
 
         return (
@@ -451,27 +492,33 @@ export default class SharingManager extends React.Component<{}> {
                     </div>
                     {this.targetDoc?.author !== Doc.CurrentUserEmail ? null
                         :
-                        <div className="share-setup">
-                            <Select
-                                className={"user-search"}
-                                placeholder={"Enter user or group name..."}
-                                isMulti
-                                closeMenuOnSelect={false}
-                                options={options}
-                                onChange={this.handleUsersChange}
-                                value={this.selectedUsers}
-                                styles={{
-                                    indicatorSeparator: () => ({
-                                        visibility: "hidden"
-                                    })
-                                }}
-                            />
-                            <select className="permissions-select" onChange={this.handlePermissionsChange}>
-                                {this.sharingOptions}
-                            </select>
-                            <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
-                                Share
+                        <div className="share-container">
+                            <div className="share-setup">
+                                <Select
+                                    className={"user-search"}
+                                    placeholder={"Enter user or group name..."}
+                                    isMulti
+                                    closeMenuOnSelect={false}
+                                    options={options}
+                                    onChange={this.handleUsersChange}
+                                    value={this.selectedUsers}
+                                    styles={{
+                                        indicatorSeparator: () => ({
+                                            visibility: "hidden"
+                                        })
+                                    }}
+                                />
+                                <select className="permissions-select" onChange={this.handlePermissionsChange}>
+                                    {this.sharingOptions}
+                                </select>
+                                <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
+                                    Share
                             </button>
+                            </div>
+                            <div className="sort-checkboxes">
+                                <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
+                                <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
+                            </div>
                         </div>
                     }
                     <div className="main-container">
@@ -481,17 +528,8 @@ export default class SharingManager extends React.Component<{}> {
                                 onClick={action(() => this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}>
                                 Individuals {this.individualSort === "ascending" ? "↑" : this.individualSort === "descending" ? "↓" : ""} {/* → */}
                             </div>
-                            <div className={"users-list"} style={{ display: !displayUserList ? "flex" : "block" }}>{/*200*/}
-                                {
-                                    !displayUserList ?
-                                        <div
-                                            className={"none"}
-                                        >
-                                            There are no users this document has been shared with.
-                                        </div>
-                                        :
-                                        userListContents
-                                }
+                            <div className={"users-list"} style={{ display: "block" }}>{/*200*/}
+                                {userListContents}
                             </div>
                         </div>
                         <div className={"group-container"}>
diff --git a/src/fields/util.ts b/src/fields/util.ts
index a62795e64..cf8e730fd 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -115,6 +115,7 @@ export function OVERRIDE_ACL(val: boolean) {
     _overrideAcl = val;
 }
 
+// playground mode allows the user to add/delete documents or make layout changes without them saving to the server
 let playgroundMode = false;
 
 export function togglePlaygroundMode() {
@@ -125,12 +126,27 @@ export function getPlaygroundMode() {
     return playgroundMode;
 }
 
+// the list of groups that the current user is a member of 
 let currentUserGroups: string[] = [];
 
+// called from GroupManager once the groups have been fetched from the server
 export function setGroups(groups: string[]) {
     currentUserGroups = groups;
 }
 
+/**
+ * These are the various levels of access a user can have to a document.
+ * 
+ * Admin: a user with admin access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), as well as change others' access rights to that document.
+ * 
+ * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document.
+ * 
+ * Add: a user with add access to a document can add documents/annotations to that document but cannot edit or delete anything.
+ * 
+ * View: a user with view access to a document can only view it - they cannot add/remove/edit anything.
+ * 
+ * None: the document is not shared with that user.
+ */
 export enum SharingPermissions {
     Admin = "Admin",
     Edit = "Can Edit",
@@ -139,18 +155,21 @@ export enum SharingPermissions {
     None = "Not Shared"
 }
 
+/**
+ * Calculates the effective access right to a document for the current user.
+ */
 export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol {
     if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin;
 
     if (target[AclSym] && Object.keys(target[AclSym]).length) {
 
+        // if the current user is the author of the document / the current user is a member of the admin group
         if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclAdmin;
 
+        // if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified)
         if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
 
         let effectiveAcl = AclPrivate;
-        let aclPresent = false;
-
         const HierarchyMapping = new Map<symbol, number>([
             [AclPrivate, 0],
             [AclReadonly, 1],
@@ -160,19 +179,26 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
         ]);
 
         for (const [key, value] of Object.entries(target[AclSym])) {
+            // there are issues with storing fields with . in the name, so they are replaced with _ during creation
+            // as a result we need to restore them again during this comparison.
             if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) {
-                if (HierarchyMapping.get(value as symbol)! >= HierarchyMapping.get(effectiveAcl)!) {
-                    aclPresent = true;
+                if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) {
                     effectiveAcl = value as symbol;
-                    if (effectiveAcl === AclEdit) break;
+                    if (effectiveAcl === AclAdmin) break;
                 }
             }
         }
-        return aclPresent ? effectiveAcl : AclEdit;
+        return effectiveAcl;
     }
     return AclAdmin;
 }
-
+/**
+ * Recursively distributes the access right for a user across the children of a document and its annotations.
+ * @param key the key storing the access right (e.g. ACL-groupname)
+ * @param acl the access right being stored (e.g. "Can Edit")
+ * @param target the document on which this access right is being set
+ * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the ACLs from the collection)
+ */
 export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) {
 
     const HierarchyMapping = new Map<string, number>([
@@ -185,32 +211,31 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
     const dataDoc = target[DataSym];
 
+    // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
     if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) target[key] = acl;
 
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
         dataDoc[key] = acl;
 
+        // maps over the children of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => {
             if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
-                distributeAcls(key, acl, d);
-                d[key] = acl;
+                distributeAcls(key, acl, d, inheritingFromCollection);
             }
             const data = d[DataSym];
             if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
-                distributeAcls(key, acl, data);
-                data[key] = acl;
+                distributeAcls(key, acl, data, inheritingFromCollection);
             }
         });
 
+        // maps over the annotations of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => {
             if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
-                distributeAcls(key, acl, d);
-                d[key] = acl;
+                distributeAcls(key, acl, d, inheritingFromCollection);
             }
             const data = d[DataSym];
             if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
-                distributeAcls(key, acl, data);
-                data[key] = acl;
+                distributeAcls(key, acl, data, inheritingFromCollection);
             }
         });
     }
-- 
cgit v1.2.3-70-g09d2


From 2fb042f575626cc6f938eec57c8e71e45ff6b1d5 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Wed, 29 Jul 2020 23:30:20 +0530
Subject: anyone can share without setting acls + fixed some inheriting stuff +
 deleting stuff + some cleanup

---
 src/client/util/SharingManager.scss             |   2 +-
 src/client/util/SharingManager.tsx              |  63 ++++++++-------
 src/client/views/DocComponent.tsx               |  18 ++---
 src/client/views/DocumentDecorations.tsx        |  27 ++++---
 src/client/views/collections/CollectionView.tsx |  34 ++++----
 src/client/views/nodes/DocumentView.tsx         | 100 ------------------------
 src/client/views/nodes/TaskCompletedBox.tsx     |   2 -
 7 files changed, 76 insertions(+), 170 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 2ce9f232c..8da80ef52 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -50,7 +50,7 @@
                 .share-button {
                     height: 105%;
                     margin-left: 2%;
-                    background-color: #979797;
+                    background-color: black;
                 }
             }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index ec04bdd81..83524f062 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, DocListCastAsync } from "../../fields/Doc";
+import { Doc, Opt, DocListCastAsync, AclAdmin, DataSym } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
 import { Cast, StrCast } from "../../fields/Types";
 import * as RequestPromise from "request-promise";
@@ -82,6 +82,7 @@ export default class SharingManager extends React.Component<{}> {
     // }
 
     public open = (target: DocumentView) => {
+        runInAction(() => this.users = []);
         SelectionManager.DeselectAll();
         this.populateUsers().then(action(() => {
             this.targetDocView = target;
@@ -145,7 +146,7 @@ export default class SharingManager extends React.Component<{}> {
         const target = this.targetDoc!;
         const ACL = `ACL-${StrCast(group.groupName)}`;
 
-        distributeAcls(ACL, permission as SharingPermissions, target);
+        target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
 
         // if documents have been shared, add the target to that list if it doesn't already exist, otherwise create a new list with the target
         group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List<Doc>).push(target)) : group.docsShared = new List<Doc>([target]);
@@ -220,7 +221,7 @@ export default class SharingManager extends React.Component<{}> {
         const key = user.email.replace('.', '_');
         const ACL = `ACL-${key}`;
 
-        distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!);
+        target.author === Doc.CurrentUserEmail && distributeAcls(ACL, permission as SharingPermissions, target);
 
         if (permission !== SharingPermissions.None) {
             DocListCastAsync(notificationDoc[storage]).then(resolved => {
@@ -385,7 +386,7 @@ export default class SharingManager extends React.Component<{}> {
             const userKey = user.email.replace('.', '_');
             const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None);
 
-            return permissions === SharingPermissions.None || user.email === this.targetDoc?.author ? null : (
+            return user.email === this.targetDoc?.author ? null : (
                 <div
                     key={userKey}
                     className={"container"}
@@ -490,36 +491,34 @@ export default class SharingManager extends React.Component<{}> {
                     <div className={"close-button"} onClick={this.close}>
                         <FontAwesomeIcon icon={fa.faTimes} color={"black"} size={"lg"} />
                     </div>
-                    {this.targetDoc?.author !== Doc.CurrentUserEmail ? null
-                        :
-                        <div className="share-container">
-                            <div className="share-setup">
-                                <Select
-                                    className={"user-search"}
-                                    placeholder={"Enter user or group name..."}
-                                    isMulti
-                                    closeMenuOnSelect={false}
-                                    options={options}
-                                    onChange={this.handleUsersChange}
-                                    value={this.selectedUsers}
-                                    styles={{
-                                        indicatorSeparator: () => ({
-                                            visibility: "hidden"
-                                        })
-                                    }}
-                                />
-                                <select className="permissions-select" onChange={this.handlePermissionsChange}>
-                                    {this.sharingOptions}
-                                </select>
-                                <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
-                                    Share
+                    {<div className="share-container">
+                        <div className="share-setup">
+                            <Select
+                                className={"user-search"}
+                                placeholder={"Enter user or group name..."}
+                                isMulti
+                                closeMenuOnSelect={false}
+                                options={options}
+                                onChange={this.handleUsersChange}
+                                value={this.selectedUsers}
+                                styles={{
+                                    indicatorSeparator: () => ({
+                                        visibility: "hidden"
+                                    })
+                                }}
+                            />
+                            <select className="permissions-select" onChange={this.handlePermissionsChange}>
+                                {this.sharingOptions}
+                            </select>
+                            <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
+                                Share
                             </button>
-                            </div>
-                            <div className="sort-checkboxes">
-                                <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
-                                <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
-                            </div>
                         </div>
+                        <div className="sort-checkboxes">
+                            <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
+                            <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
+                        </div>
+                    </div>
                     }
                     <div className="main-container">
                         <div className={"individual-container"}>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 9fd406407..ae180a78b 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -154,15 +154,15 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                     return false;
                 }
                 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 (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 => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
                     }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8d63537e7..655f330e3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,9 +1,9 @@
 import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
 import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction } from "mobx";
+import { action, computed, observable, reaction, runInAction, get } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../fields/Doc";
+import { Doc, DataSym, Field, WidthSym, HeightSym, AclEdit, AclAdmin } from "../../fields/Doc";
 import { Document } from '../../fields/documentSchemas';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, StrCast, NumCast } from "../../fields/Types";
@@ -23,6 +23,9 @@ import { SnappingManager } from '../util/SnappingManager';
 import { HtmlField } from '../../fields/HtmlField';
 import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { Tooltip } from '@material-ui/core';
+import { GetEffectiveAcl } from '../../fields/util';
+import { DocumentIcon } from './nodes/DocumentIcon';
+import { render } from 'react-dom';
 
 library.add(faCaretUp);
 library.add(faObjectGroup);
@@ -193,8 +196,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
             SelectionManager.DeselectAll();
 
             selected.map(dv => {
-                recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
-                dv.props.removeDocument?.(dv.props.Document);
+                const effectiveAcl = GetEffectiveAcl(dv.props.Document);
+                if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) { // deletes whatever you have the right to delete
+                    recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true);
+                    dv.props.removeDocument?.(dv.props.Document);
+                }
             });
         }
     }
@@ -545,17 +551,18 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
+        const canDelete = SelectionManager.SelectedDocuments().map(docView => GetEffectiveAcl(docView.props.Document)).some(permission => permission === AclAdmin || permission === AclEdit);
         const minimal = bounds.r - bounds.x < 100 ? true : false;
         const maximizeIcon = minimal ? (
             <Tooltip title={<><div className="dash-tooltip">Show context menu</div></>} placement="top">
                 <div className="documentDecorations-contextMenu" onPointerDown={this.onSettingsDown}>
                     <FontAwesomeIcon size="lg" icon="cog" />
-                </div></Tooltip>) : (
-                <Tooltip title={<><div className="dash-tooltip">Delete</div></>} placement="top">
-                    <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
-                        {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
-                        <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
-                    </div></Tooltip>);
+                </div></Tooltip>) : canDelete ? (
+                    <Tooltip title={<><div className="dash-tooltip">Delete</div></>} placement="top">
+                        <div className="documentDecorations-closeButton" onClick={this.onCloseClick}>
+                            {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
+                            <FontAwesomeIcon className="documentdecorations-times" icon={faTimes} size="lg" />
+                        </div></Tooltip>) : null;
 
         const titleArea = this._edtingTitle ?
             <>
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index a82c91383..3bb1022e0 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, distributeAcls, SharingPermissions } from '../../../fields/util';
+import { TraceMobx, GetEffectiveAcl, getPlaygroundMode, SharingPermissions } 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';
@@ -149,16 +149,16 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 return false;
             }
             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, true);
-                        }
-                        // dataDoc[AclSym] = d[AclSym] = this.props.Document[AclSym];
-                    });
-                }
+                // 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])) {
+                //             // key.substring(4).replace("_", ".") !== Doc.CurrentUserEmail && distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
+                //             distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
+                //         }
+                //     });
+                // }
 
                 if (effectiveAcl === AclAddonly) {
                     added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
@@ -192,14 +192,16 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
 
     @action.bound
     removeDocument = (doc: any): boolean => {
-        const effectiveAcl = GetEffectiveAcl(this.props.Document);
-        if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || getPlaygroundMode()) {
+        const collectionEffectiveAcl = GetEffectiveAcl(this.props.Document);
+        const docEffectiveAcl = GetEffectiveAcl(doc);
+        // you can remove the document if you either have Admin/Edit access to the collection or to the specific document
+        if (collectionEffectiveAcl === AclEdit || collectionEffectiveAcl === AclAdmin || docEffectiveAcl === AclAdmin || docEffectiveAcl === AclEdit || getPlaygroundMode()) {
             const docs = doc instanceof Doc ? [doc] : doc as Doc[];
             const targetDataDoc = this.props.Document[DataSym];
             const value = DocListCast(targetDataDoc[this.props.fieldKey]);
-            const result = value.filter(v => !docs.includes(v));
-            if (result.length !== value.length) {
-                targetDataDoc[this.props.fieldKey] = new List<Doc>(result);
+            const toRemove = value.filter(v => docs.includes(v));
+            if (toRemove.length !== 0) {
+                toRemove.forEach(doc => Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc));
                 return true;
             }
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index b59875a7e..1d89f3d24 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -697,35 +697,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true;
     }
 
-    @undoBatch
-    @action
-    setAcl = (acl: SharingPermissions) => {
-        this.dataDoc.ACL = this.props.Document.ACL = acl;
-        DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
-            if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
-            const data = d[DataSym];
-            if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl;
-        });
-    }
-
-    @undoBatch
-    @action
-    testAcl = (acl: SharingPermissions) => {
-        this.dataDoc.author = this.props.Document.author = "ADMIN";
-        this.dataDoc.ACL = this.props.Document.ACL = acl;
-        DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
-            if (d.author === Doc.CurrentUserEmail) {
-                d.author = "ADMIN";
-                d.ACL = acl;
-            }
-            const data = d[DataSym];
-            if (data && data.author === Doc.CurrentUserEmail) {
-                data.author = "ADMIN";
-                data.ACL = acl;
-            }
-        });
-    }
-
     @action
     onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
         // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
@@ -791,11 +762,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         moreItems.push({
             description: "Download document", icon: "download", event: async () => {
                 Doc.Zip(this.props.Document);
-                // const a = document.createElement("a");
-                // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
-                // a.href = url;
-                // a.download = `DocExport-${this.props.Document[Id]}.zip`;
-                // a.click();
             }
         });
         if (!Doc.UserDoc().noviceMode) {
@@ -822,72 +788,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
         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(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[] = [];
-
-        // recommender_subitems.push({
-        //     description: "Internal recommendations",
-        //     event: () => this.recommender(),
-        //     icon: "brain"
-        // });
-
-        // const ext_recommender_subitems: ContextMenuProps[] = [];
-
-        // ext_recommender_subitems.push({
-        //     description: "arXiv",
-        //     event: () => this.externalRecommendation("arxiv"),
-        //     icon: "brain"
-        // });
-        // ext_recommender_subitems.push({
-        //     description: "Bing",
-        //     event: () => this.externalRecommendation("bing"),
-        //     icon: "brain"
-        // });
-
-        // recommender_subitems.push({
-        //     description: "External recommendations",
-        //     subitems: ext_recommender_subitems,
-        //     icon: "brain"
-        // });
-
-
-        //moreItems.push({ description: "Recommender System", subitems: recommender_subitems, icon: "brain" });
-        //moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" });
-        //moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" });
-
-        // runInAction(() => {
-        //     const setWriteMode = (mode: DocServer.WriteMode) => {
-        //         DocServer.AclsMode = mode;
-        //         const mode1 = mode;
-        //         const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground;
-        //         DocServer.setFieldWriteMode("x", mode1);
-        //         DocServer.setFieldWriteMode("y", mode1);
-        //         DocServer.setFieldWriteMode("_width", mode1);
-        //         DocServer.setFieldWriteMode("_height", mode1);
-
-        //         DocServer.setFieldWriteMode("_panX", mode2);
-        //         DocServer.setFieldWriteMode("_panY", mode2);
-        //         DocServer.setFieldWriteMode("scale", mode2);
-        //         DocServer.setFieldWriteMode("_viewType", mode2);
-        //     };
-        //     const aclsMenu: ContextMenuProps[] = [];
-        //     aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" });
-        //     aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" });
-        //     aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" });
-        //     aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" });
-        //     cm.addItem({ description: "Collaboration ...", subitems: aclsMenu, icon: "share" });
-        // });
         runInAction(() => {
             if (!this.topMost && !(e instanceof Touch)) {
                 // DocumentViews should stop propagation of this event
diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx
index 89602f219..2a3dd8d2d 100644
--- a/src/client/views/nodes/TaskCompletedBox.tsx
+++ b/src/client/views/nodes/TaskCompletedBox.tsx
@@ -1,7 +1,5 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
 import "./TaskCompletedBox.scss";
 import { observable, action } from "mobx";
 import { Fade } from "@material-ui/core";
-- 
cgit v1.2.3-70-g09d2


From 611f848ff4f7b958dc913d33dd1b760ca9474c44 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 30 Jul 2020 18:33:19 +0530
Subject: permissions from the properties panel should work now

---
 src/client/util/GroupManager.tsx                   |  2 +-
 src/client/util/SharingManager.tsx                 | 29 +++++++----
 .../collectionFreeForm/PropertiesView.scss         |  2 +
 .../collectionFreeForm/PropertiesView.tsx          | 56 ++++++++++++++--------
 src/fields/util.ts                                 | 12 ++++-
 5 files changed, 67 insertions(+), 34 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 72fba5c1b..dee0f105f 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -101,7 +101,7 @@ export default class GroupManager extends React.Component<{}> {
      */
     @action
     open = () => {
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
         this.populateGroups();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 0d8b33fbe..9222f10c5 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -62,14 +62,12 @@ export default class SharingManager extends React.Component<{}> {
     @observable private groupSort: "ascending" | "descending" | "none" = "none";
     private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
 
-
-
     // private get linkVisible() {
     //     return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false;
     // }
 
     public open = (target: DocumentView) => {
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.populateUsers().then(action(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
@@ -82,7 +80,7 @@ export default class SharingManager extends React.Component<{}> {
 
     public close = action(() => {
         this.isOpen = false;
-        this.users = [];
+        // this.users = [];
         this.selectedUsers = null;
 
         setTimeout(action(() => {
@@ -97,7 +95,12 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    componentDidMount() {
+        this.populateUsers();
+    }
+
     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 => {
@@ -117,17 +120,17 @@ export default class SharingManager extends React.Component<{}> {
         return Promise.all(evaluating);
     }
 
-    setInternalGroupSharing = (group: Doc, permission: string) => {
+    setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => {
         const members: string[] = JSON.parse(StrCast(group.members));
         const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
-        const target = this.targetDoc!;
+        const target = targetDoc || 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;
 
-        distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!);
+        distributeAcls(ACL, permission as SharingPermissions, target);
 
         group.docsShared ? DocListCastAsync(group.docsShared).then(resolved => Doc.IndexOf(target, resolved!) === -1 && (group.docsShared as List<Doc>).push(target)) : group.docsShared = new List<Doc>([target]);
 
@@ -182,14 +185,20 @@ export default class SharingManager extends React.Component<{}> {
         }
     }
 
+    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => {
+        const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+        if (user) this.setInternalSharing(user, permission, target);
+        else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target);
+    }
+
     // @action
-    setInternalSharing = (recipient: ValidatedUser, permission: string) => {
+    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
         const { user, notificationDoc } = recipient;
-        const target = this.targetDoc!;
+        const target = targetDoc || this.targetDoc!;
         const key = user.email.replace('.', '_');
         const ACL = `ACL-${key}`;
 
-        distributeAcls(ACL, permission as SharingPermissions, this.targetDoc!);
+        distributeAcls(ACL, permission as SharingPermissions, target);
 
         if (permission !== SharingPermissions.None) {
             DocListCastAsync(notificationDoc[storage]).then(resolved => {
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
index cb4b7375b..4ccc0950b 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -159,6 +159,8 @@
         border-radius: 6px;
         width: 170px;
         background-color: #ececec;
+        max-height: 130px;
+        overflow-y: scroll;
 
         .propertiesView-sharingTable-item {
 
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index d5317efcb..b33b37f4b 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -2,7 +2,7 @@ import React = require("react");
 import { observer } from "mobx-react";
 import "./PropertiesView.scss";
 import { observable, action, computed, runInAction } from "mobx";
-import { Doc, Field, DocListCast, WidthSym, HeightSym } from "../../../../fields/Doc";
+import { Doc, Field, DocListCast, WidthSym, HeightSym, AclSym, AclPrivate, AclReadonly, AclAddonly, AclEdit, AclAdmin } from "../../../../fields/Doc";
 import { DocumentView } from "../../nodes/DocumentView";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { EditableView } from "../../EditableView";
@@ -18,6 +18,7 @@ import { SelectionManager } from "../../../util/SelectionManager";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip, Checkbox } from "@material-ui/core";
 import SharingManager from "../../../util/SharingManager";
+import { SharingPermissions, GetEffectiveAcl } from "../../../../fields/util";
 
 
 interface PropertiesViewProps {
@@ -249,20 +250,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         }
     }
 
-    @computed get permissionsSelect() {
-        return <select className="permissions-select" onChange={emptyFunction}>
-            <option key={"Can Edit"} value={"Can Edit"}>
-                Can Edit
-                        </option>
-            <option key={"Can Add"} value={"Can Add"}>
-                Can Add
-                        </option>
-            <option key={"Can View"} value={"Can View"}>
-                Can View
-                        </option>
-            <option key={"Not Shared"} value={"Not Shared"}>
-                Not Shared
-                        </option>
+    getPermissionsSelect(user: string) {
+        return <select className="permissions-select"
+            onChange={e => SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!)}>
+            {Object.values(SharingPermissions).map(permission => {
+                return (
+                    <option key={permission} value={permission} selected={this.selectedDoc![`ACL-${user.replace(".", "_")}`] === permission}>
+                        {permission}
+                    </option>);
+            })}
         </select>;
     }
 
@@ -286,23 +282,41 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         </Tooltip>;
     }
 
-    sharingItem(name: string, notify: boolean, editable: boolean, permission?: string) {
+    sharingItem(name: string, notify: boolean, effectiveAcl: symbol, permission?: string) {
         return <div className="propertiesView-sharingTable-item">
             <div className="propertiesView-sharingTable-item-name" style={{ width: notify ? "70px" : "80px" }}> {name} </div>
             {notify ? this.notifyIcon : null}
             <div className="propertiesView-sharingTable-item-permission">
-                {editable ? this.permissionsSelect : permission}
+                {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission}
                 {permission === "Owner" ? this.expansionIcon : null}
             </div>
         </div>;
     }
 
     @computed get sharingTable() {
+        const AclMap = new Map<symbol, string>([
+            [AclPrivate, SharingPermissions.None],
+            [AclReadonly, SharingPermissions.View],
+            [AclAddonly, SharingPermissions.Add],
+            [AclEdit, SharingPermissions.Edit],
+            [AclAdmin, SharingPermissions.Admin]
+        ]);
+
+        const effectiveAcl = GetEffectiveAcl(this.selectedDoc!);
+        const tableEntries = [];
+
+        if (this.selectedDoc![AclSym]) {
+            for (const [key, value] of Object.entries(this.selectedDoc![AclSym])) {
+                const name = key.substring(4).replace("_", ".");
+                if (name !== Doc.CurrentUserEmail && name !== this.selectedDoc!.author) tableEntries.push(this.sharingItem(name, false, effectiveAcl, AclMap.get(value)!));
+            }
+        }
+
+        tableEntries.unshift(this.sharingItem("Me", false, effectiveAcl, Doc.CurrentUserEmail === this.selectedDoc!.author ? "Owner" : StrCast(this.selectedDoc![`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`])));
+        if (Doc.CurrentUserEmail !== this.selectedDoc!.author) tableEntries.unshift(this.sharingItem(StrCast(this.selectedDoc!.author), false, effectiveAcl, "Owner"));
+
         return <div className="propertiesView-sharingTable">
-            {this.sharingItem("Me", false, false, "Owner")}
-            {this.sharingItem("Public", false, true)}
-            {this.sharingItem("Group 1", true, true)}
-            {this.sharingItem("Group 2", true, true)}
+            {tableEntries}
         </div>;
     }
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index a62795e64..267da70b4 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, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym } from "./Doc";
+import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto } from "./Doc";
 import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField, PrefetchProxy } from "./Proxy";
 import { RefField } from "./RefField";
@@ -183,12 +183,18 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         ["Admin", 4]
     ]);
 
+    let changed = false;
+
     const dataDoc = target[DataSym];
 
-    if (!inheritingFromCollection || !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;
+        changed = true;
+    }
 
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
         dataDoc[key] = acl;
+        changed = true;
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => {
             if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
@@ -214,6 +220,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
             }
         });
     }
+
+    changed && fetchProto(target);
 }
 
 const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
-- 
cgit v1.2.3-70-g09d2


From 626c7b04dcfff293a2a30ed059e661100143199b Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 30 Jul 2020 22:11:39 +0530
Subject: some playground mode stuff + minor sharingmanager changes

---
 src/client/util/GroupManager.tsx                |  2 +-
 src/client/util/SharingManager.tsx              | 41 ++++++++++++++++---------
 src/client/views/DocComponent.tsx               |  6 ++--
 src/client/views/collections/CollectionView.tsx |  7 +++--
 4 files changed, 35 insertions(+), 21 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 551216fa4..a0db9a421 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -102,7 +102,7 @@ export default class GroupManager extends React.Component<{}> {
      */
     @action
     open = () => {
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
         this.populateGroups();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 00d0691f2..7ede584c2 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, DocListCastAsync, AclAdmin, DataSym } from "../../fields/Doc";
+import { Doc, Opt, DocListCastAsync, AclAdmin, DataSym, AclPrivate } from "../../fields/Doc";
 import { DocServer } from "../DocServer";
 import { Cast, StrCast } from "../../fields/Types";
 import * as RequestPromise from "request-promise";
@@ -19,7 +19,7 @@ import GroupMemberView from "./GroupMemberView";
 import Select from "react-select";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { List } from "../../fields/List";
-import { distributeAcls, SharingPermissions } from "../../fields/util";
+import { distributeAcls, SharingPermissions, GetEffectiveAcl } from "../../fields/util";
 import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 export interface User {
@@ -80,7 +80,7 @@ export default class SharingManager extends React.Component<{}> {
 
     public open = (target: DocumentView) => {
         runInAction(() => this.users = []);
-        SelectionManager.DeselectAll();
+        // SelectionManager.DeselectAll();
         this.populateUsers().then(action(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
@@ -93,8 +93,8 @@ export default class SharingManager extends React.Component<{}> {
 
     public close = action(() => {
         this.isOpen = false;
-        this.users = []; // resets the list of users and seleected users (in the react-select component)
-        this.selectedUsers = null;
+        this.users = [];
+        this.selectedUsers = null; // resets the list of users and seleected users (in the react-select component)
 
         setTimeout(action(() => {
             // this.copied = false;
@@ -108,10 +108,15 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    componentDidMount() {
+        this.populateUsers();
+    }
+
     /**
      * Populates the list of validated users (this.users) by adding registered users which have a rightSidebarCollection.
      */
     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 => {
@@ -379,24 +384,32 @@ 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 effectiveAcl = this.targetDoc ? GetEffectiveAcl(this.targetDoc) : AclPrivate;
+
         const userListContents: (JSX.Element | null)[] = users.map(({ user, notificationDoc }) => {
             const userKey = user.email.replace('.', '_');
-            const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`], SharingPermissions.None);
+            const permissions = StrCast(this.targetDoc?.[`ACL-${userKey}`]);
 
-            return user.email === this.targetDoc?.author ? null : (
+            return !permissions || user.email === this.targetDoc?.author ? null : (
                 <div
                     key={userKey}
                     className={"container"}
                 >
                     <span className={"padding"}>{user.email}</span>
                     <div className="edit-actions">
-                        <select
-                            className={"permissions-dropdown"}
-                            value={permissions}
-                            onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)}
-                        >
-                            {this.sharingOptions}
-                        </select>
+                        {effectiveAcl === AclAdmin ? (
+                            <select
+                                className={"permissions-dropdown"}
+                                value={permissions}
+                                onChange={e => this.setInternalSharing({ user, notificationDoc }, e.currentTarget.value)}
+                            >
+                                {this.sharingOptions}
+                            </select>
+                        ) : (
+                                <div className={"permissions-dropdown"}>
+                                    {this.sharingOptions}
+                                </div>
+                            )}
                     </div>
                 </div>
             );
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 777fb5323..3bcf4b922 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -163,13 +163,13 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                     //         }
                     //     });
                     // }
-                    if (effectiveAcl === AclAddonly) {
+                    if (effectiveAcl === AclAddonly && !getPlaygroundMode()) {
                         added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
                     }
                     else {
                         added.map(doc => doc.context = this.props.Document);
-                        targetDataDoc[this.annotationKey] = new List<Doc>([...docList, ...added]);
-                        targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
+                        (targetDataDoc[this.annotationKey] as List<Doc>).push(...added);
+                        if (!getPlaygroundMode()) targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now()));
                     }
                 }
             }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 25a832994..5262c8b46 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -157,7 +157,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                 //     });
                 // }
 
-                if (effectiveAcl === AclAddonly) {
+                if (effectiveAcl === AclAddonly && !getPlaygroundMode()) {
                     added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
                 }
                 else {
@@ -179,8 +179,9 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
                         doc.context = this.props.Document;
                     });
                     added.map(add => Doc.AddDocToList(Cast(Doc.UserDoc().myCatalog, Doc, null), "data", add));
-                    targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
-                    targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+                    // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]);
+                    (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added);
+                    if (!getPlaygroundMode()) targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
                 }
             }
         }
-- 
cgit v1.2.3-70-g09d2


From 583b13be95734e22dcae558306ca94ff8ef8c7b4 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 31 Jul 2020 12:07:45 +0530
Subject: fixed acls re highlights

---
 src/client/util/SharingManager.tsx | 21 +++++++++------------
 src/client/views/nodes/WebBox.tsx  | 14 +++++++++-----
 src/client/views/pdf/PDFMenu.tsx   |  2 +-
 src/client/views/pdf/PDFViewer.tsx | 14 +++++++++-----
 4 files changed, 28 insertions(+), 23 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7ede584c2..4ab4a3c42 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -120,17 +120,14 @@ export default class SharingManager extends React.Component<{}> {
         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, notificationDoc });
-                        }
-                    });
-                }
+            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, notificationDoc });
+                    }
+                });
             }
         });
         return Promise.all(evaluating);
@@ -407,7 +404,7 @@ export default class SharingManager extends React.Component<{}> {
                             </select>
                         ) : (
                                 <div className={"permissions-dropdown"}>
-                                    {this.sharingOptions}
+                                    {permissions}
                                 </div>
                             )}
                     </div>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d30f1499e..cb82a76f1 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import { Dictionary } from "typescript-collections";
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt, AclAddonly, AclEdit, AclAdmin } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { HtmlField } from "../../../fields/HtmlField";
@@ -13,7 +13,7 @@ import { List } from "../../../fields/List";
 import { listSpec, makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { WebField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
+import { TraceMobx, GetEffectiveAcl, getPlaygroundMode } from "../../../fields/util";
 import { addStyleSheet, clearStyleSheetRules, emptyFunction, returnOne, returnZero, Utils, returnTrue } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
@@ -535,9 +535,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
     @action
     highlight = (color: string) => {
         // creates annotation documents for current highlights
-        const annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && Doc.AddDocToList(this.props.Document, this.annotationKey, annotationDoc);
-        return annotationDoc;
+        const effectiveAcl = GetEffectiveAcl(this.props.Document);
+        if ([AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) || getPlaygroundMode()) {
+            const annotationDoc = this.makeAnnotationDocument(color);
+            annotationDoc && Doc.AddDocToList(this.props.Document, this.annotationKey, annotationDoc);
+            return annotationDoc;
+        }
+        return undefined;
     }
     /**
      * This is temporary for creating annotations from highlights. It will
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index c3e1ae22f..7bea8d01b 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -93,7 +93,7 @@ export default class PDFMenu extends AntimodeMenu {
 
     @computed get highlighter() {
         const button =
-            <button className="antimodeMenu-button color-preview-button" title="" key="highilghter-button" onPointerDown={this.highlightClicked}>
+            <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onPointerDown={this.highlightClicked}>
                 <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} />
                 <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div>
             </button>;
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index c792df882..954b62332 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -4,7 +4,7 @@ const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
 import { Dictionary } from "typescript-collections";
-import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym, AclAddonly, AclEdit, AclAdmin } from "../../../fields/Doc";
 import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { InkTool } from "../../../fields/InkField";
@@ -13,7 +13,7 @@ import { createSchema, makeInterface } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast } from "../../../fields/Types";
 import { PdfField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
+import { TraceMobx, GetEffectiveAcl, getPlaygroundMode } from "../../../fields/util";
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -565,9 +565,13 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
     @action
     highlight = (color: string) => {
         // creates annotation documents for current highlights
-        const annotationDoc = this.makeAnnotationDocument(color);
-        annotationDoc && this.props.addDocument?.(annotationDoc);
-        return annotationDoc;
+        const effectiveAcl = GetEffectiveAcl(this.props.Document);
+        if ([AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) || getPlaygroundMode()) {
+            const annotationDoc = this.makeAnnotationDocument(color);
+            annotationDoc && Doc.AddDocToList(this.props.Document, this.annotationKey, annotationDoc);
+            return annotationDoc;
+        }
+        return undefined;
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 5d50404b127560d525cab4645fcd3b07367ef5a2 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 31 Jul 2020 16:19:46 +0530
Subject: comments

---
 src/client/util/GroupManager.tsx   | 37 +++++++++++++------------------------
 src/client/util/SharingManager.tsx |  8 ++++++--
 src/client/views/MainViewModal.tsx |  2 +-
 src/fields/util.ts                 |  7 +++++--
 4 files changed, 25 insertions(+), 29 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index a0db9a421..5215ea35f 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -38,8 +38,8 @@ export default class GroupManager extends React.Component<{}> {
     @observable currentGroup: Opt<Doc>; // the currently selected group.
     @observable private createGroupModalOpen: boolean = false;
     private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
-    private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
-    private currentUserGroups: string[] = [];
+    private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
+    private currentUserGroups: string[] = []; // the list of groups the current user is a member of
     @observable private buttonColour: "#979797" | "black" = "#979797";
     @observable private groupSort: "ascending" | "descending" | "none" = "none";
 
@@ -50,6 +50,9 @@ export default class GroupManager extends React.Component<{}> {
         GroupManager.Instance = this;
     }
 
+    /**
+     * Populates the list of users and groups.
+     */
     componentDidMount() {
         this.populateUsers();
         this.populateGroups();
@@ -63,8 +66,6 @@ export default class GroupManager extends React.Component<{}> {
         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);
@@ -74,11 +75,13 @@ export default class GroupManager extends React.Component<{}> {
                     }
                 });
             }
-            // }
         });
         return Promise.all(evaluating);
     }
 
+    /**
+     * Populates the list of groups the current user is a member of and sets this list to be used in the GetEffectiveAcl in util.ts
+     */
     populateGroups = () => {
         DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
             groups?.forEach(group => {
@@ -146,25 +149,8 @@ export default class GroupManager extends React.Component<{}> {
     }
 
     /**
-     * @returns a readonly copy of a single group document
+     * Returns an array of the list of members of a given group.
      */
-    getGroupCopy(groupName: string): Doc | undefined {
-        const groupDoc = this.getGroup(groupName);
-        if (groupDoc) {
-            const { members, owners } = groupDoc;
-            return Doc.assign(new Doc, { groupName, members: StrCast(members), owners: StrCast(owners) });
-        }
-        return undefined;
-    }
-    /**
-     * @returns a readonly copy of the list of group documents
-     */
-    getAllGroupsCopy(): Doc[] {
-        return this.getAllGroups().map(({ groupName, owners, members }) =>
-            Doc.assign(new Doc, { groupName: (StrCast(groupName)), owners: (StrCast(owners)), members: (StrCast(members)) })
-        );
-    }
-
     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[];
@@ -317,6 +303,9 @@ export default class GroupManager extends React.Component<{}> {
 
     }
 
+    /**
+     * @returns the MainViewModal which allows the user to create groups.
+     */
     private get groupCreationModal() {
         const contents = (
             <div className="group-create">
@@ -416,7 +405,7 @@ export default class GroupManager extends React.Component<{}> {
                     <div
                         className="sort-groups"
                         onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                        Name {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""} {/* → */}
+                        Name {this.groupSort === "ascending" ? "↑" : this.groupSort === "descending" ? "↓" : ""}
                     </div>
                     <div className="group-body">
                         {groups.map(group =>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 4ab4a3c42..64dc491ed 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -81,13 +81,14 @@ export default class SharingManager extends React.Component<{}> {
     public open = (target: DocumentView) => {
         runInAction(() => this.users = []);
         // SelectionManager.DeselectAll();
-        this.populateUsers().then(action(() => {
+        this.populateUsers();
+        runInAction(() => {
             this.targetDocView = target;
             this.targetDoc = target.props.Document;
             DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = true;
             this.permissions = SharingPermissions.Edit;
-        }));
+        });
 
     }
 
@@ -108,6 +109,9 @@ export default class SharingManager extends React.Component<{}> {
         SharingManager.Instance = this;
     }
 
+    /**
+     * Populates the list of users.
+     */
     componentDidMount() {
         this.populateUsers();
     }
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index 249715511..66ea2dbf8 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -10,7 +10,7 @@ export interface MainViewOverlayProps {
     overlayStyle?: React.CSSProperties;
     dialogueBoxDisplayedOpacity?: number;
     overlayDisplayedOpacity?: number;
-    closeOnExternalClick?: () => void;
+    closeOnExternalClick?: () => void; // the close method of a MainViewModal, triggered if there is a click on the overlay (closing the modal)
 }
 
 @observer
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 500daf0c7..16517f25f 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -184,6 +184,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
                 }
             }
         }
+        // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl)
         return playgroundMode && HierarchyMapping.get(effectiveAcl)! < 3 ? AclEdit : effectiveAcl;
     }
     return AclAdmin;
@@ -194,6 +195,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
  * @param acl the access right being stored (e.g. "Can Edit")
  * @param target the document on which this access right is being set
  * @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the ACLs from the collection)
+ * inheritingFromCollection is not currently being used but could be used if ACL assignment defaults change
  */
 export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) {
 
@@ -205,7 +207,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         ["Admin", 4]
     ]);
 
-    let changed = false;
+    let changed = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
     const dataDoc = target[DataSym];
 
     // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
@@ -241,7 +243,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         });
     }
 
-    changed && fetchProto(target); // updates aclsym when changes to acls have been made
+    changed && fetchProto(target); // updates target[AclSym] when changes to acls have been made
 }
 
 const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
@@ -251,6 +253,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
     const effectiveAcl = GetEffectiveAcl(target, in_prop);
     if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true;
 
+    // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === "string" && prop.startsWith("ACL") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;
     // if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
 
-- 
cgit v1.2.3-70-g09d2


From 38cfa8e547c1ffb7344f83ebb5dd9798d0dd0d0c Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 31 Jul 2020 22:47:36 +0530
Subject: added alias list to docs + sharingmanager ui changes

---
 src/client/util/SharingManager.tsx | 37 +++++++++++++++++++++++++++----------
 src/fields/Doc.ts                  |  4 ++++
 src/fields/util.ts                 | 11 ++++++++++-
 3 files changed, 41 insertions(+), 11 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 64dc491ed..196182b77 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -124,14 +124,17 @@ export default class SharingManager extends React.Component<{}> {
         const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
         const raw = JSON.parse(userList) as User[];
         const evaluating = raw.map(async user => {
-            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, notificationDoc });
-                    }
-                });
+            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, notificationDoc });
+                        }
+                    });
+                }
             }
         });
         return Promise.all(evaluating);
@@ -422,14 +425,28 @@ export default class SharingManager extends React.Component<{}> {
                     key={"owner"}
                     className={"container"}
                 >
-                    <span className={"padding"}>{this.targetDoc?.author}</span>
+                    <span className={"padding"}>{this.targetDoc?.author === Doc.CurrentUserEmail ? "Me" : this.targetDoc?.author}</span>
                     <div className="edit-actions">
                         <div className={"permissions-dropdown"}>
                             Owner
                         </div>
                     </div>
                 </div>
-            )
+            ),
+            this.targetDoc?.author !== Doc.CurrentUserEmail ?
+                (
+                    <div
+                        key={"me"}
+                        className={"container"}
+                    >
+                        <span className={"padding"}>Me</span>
+                        <div className="edit-actions">
+                            <div className={"permissions-dropdown"}>
+                                {this.targetDoc?.[`ACL-${Doc.CurrentUserEmail.replace(".", "_")}`]}
+                            </div>
+                        </div>
+                    </div>
+                ) : null
         );
 
         const groupListContents = groups.map(group => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 267defa4b..a1acc7060 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -508,6 +508,10 @@ export namespace Doc {
         alias.aliasOf = doc;
         alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
         alias.author = Doc.CurrentUserEmail;
+
+        if (!doc.aliases) doc.aliases = new List<Doc>([alias]);
+        else Doc.AddDocToList(doc, "aliases", alias);
+
         return alias;
     }
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 16517f25f..608ef506c 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -160,7 +160,8 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
     if (target[AclSym] && Object.keys(target[AclSym]).length) {
 
         // if the current user is the author of the document / the current user is a member of the admin group
-        if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclAdmin;
+        // but not if the doc in question is an alias - the current user will be the author of their alias rather than the original author
+        if ((Doc.CurrentUserEmail === (target.__fields?.author || target.author) && !(target.aliasOf || target.__fields?.aliasOf)) || currentUserGroups.includes("admin")) return AclAdmin;
 
         // if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified)
         if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
@@ -214,6 +215,14 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) {
         target[key] = acl;
         changed = true;
+
+        // maps over the aliases of the document
+        if (target.aliases) {
+            DocListCast(target.aliases).map(alias => {
+                distributeAcls(key, acl, alias);
+            });
+        }
+
     }
 
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
-- 
cgit v1.2.3-70-g09d2


From 6d02ae9f9c09c27fa8da87afbd1307240dcb9289 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Sun, 2 Aug 2020 17:42:56 +0530
Subject: minor changes

---
 src/client/util/SharingManager.tsx | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 196182b77..892fb6d6d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -94,7 +94,6 @@ export default class SharingManager extends React.Component<{}> {
 
     public close = action(() => {
         this.isOpen = false;
-        this.users = [];
         this.selectedUsers = null; // resets the list of users and seleected users (in the react-select component)
 
         setTimeout(action(() => {
@@ -450,9 +449,9 @@ export default class SharingManager extends React.Component<{}> {
         );
 
         const groupListContents = groups.map(group => {
-            const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`], SharingPermissions.None);
+            const permissions = StrCast(this.targetDoc?.[`ACL-${StrCast(group.groupName)}`]);
 
-            return permissions === SharingPermissions.None ? null : (
+            return !permissions ? null : (
                 <div
                     key={StrCast(group.groupName)}
                     className={"container"}
-- 
cgit v1.2.3-70-g09d2


From 1486b398071b3e9d9ac84edc74404ebadb69f67c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sun, 2 Aug 2020 11:40:24 -0400
Subject: fixed runtime warnings.  fixed color of title text of stacking and
 masonry views

---
 src/client/util/SharingManager.tsx                             |  4 ++--
 src/client/views/EditableView.tsx                              |  2 +-
 src/client/views/collections/CollectionMasonryViewFieldRow.tsx |  2 --
 src/client/views/collections/CollectionStackingView.tsx        |  2 +-
 .../views/collections/CollectionStackingViewFieldColumn.tsx    | 10 +++-------
 5 files changed, 7 insertions(+), 13 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 892fb6d6d..5896bdf92 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -352,9 +352,9 @@ export default class SharingManager extends React.Component<{}> {
     private get sharingInterface() {
         const groupList = GroupManager.Instance?.getAllGroups() || [];
 
-        const sortedUsers = this.users.sort(this.sortUsers)
+        const sortedUsers = this.users.slice().sort(this.sortUsers)
             .map(({ user: { email } }) => ({ label: email, value: indType + email }));
-        const sortedGroups = groupList.sort(this.sortGroups)
+        const sortedGroups = groupList.slice().sort(this.sortGroups)
             .map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) }));
 
         const options: GroupedOptions[] = [];
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index ad61d3f91..c7b149842 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -187,7 +187,7 @@ export class EditableView extends React.Component<EditableProps> {
                     {this.renderEditor()}
                 </div> : this.renderEditor();
         } else {
-            this.props.autosuggestProps?.resetValue();
+            setTimeout(() => this.props.autosuggestProps?.resetValue(), 0);
             return (this.props.contents instanceof ObjectField ? (null) :
                 <div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
                     ref={this._ref}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 9a7ea2c93..c772dcfe7 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -238,7 +238,6 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             contents: "+ NEW",
             HeadingObject: this.props.headingObject,
             toggle: this.toggleVisibility,
-            color: this.color
         };
         const showChrome = (chromeStatus !== 'view-mode' && chromeStatus !== 'disabled');
         const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `;
@@ -278,7 +277,6 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
             oneLine: true,
             HeadingObject: this.props.headingObject,
             toggle: this.toggleVisibility,
-            color: this.color
         };
         return this.props.parent.props.Document.miniHeaders ?
             <div className="collectionStackingView-miniHeader">
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0332b4bf2..312dd92f0 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -481,7 +481,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
                     })}
                     onDrop={this.onExternalDrop.bind(this)}
                     onContextMenu={this.onContextMenu}
-                    onWheel={e => this.props.active() && e.stopPropagation()} >
+                    onWheel={e => this.props.active(true) && e.stopPropagation()} >
                     {this.renderedSections}
                     {!this.showAddAGroup ? (null) :
                         <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 76af70cd1..a6983de05 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -298,7 +298,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             oneLine: true,
             HeadingObject: this.props.headingObject,
             toggle: this.toggleVisibility,
-            color: this._color
         };
         const newEditableViewProps = {
             GetValue: () => "",
@@ -306,7 +305,6 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
             contents: "+ NEW",
             HeadingObject: this.props.headingObject,
             toggle: this.toggleVisibility,
-            color: this._color
         };
         const headingView = this.props.headingObject ?
             <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
@@ -332,11 +330,9 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                             {this._paletteOn ? this.renderColorPicker() : (null)}
                         </div>
                     }
-                    {evContents === `NO ${key.toUpperCase()} VALUE` ?
-                        (null) :
-                        <button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
-                            <FontAwesomeIcon icon="trash" size="lg" />
-                        </button>}
+                    {<button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
+                        <FontAwesomeIcon icon="trash" size="lg" />
+                    </button>}
                     {evContents === `NO  ${key.toUpperCase()} VALUE` ? (null) :
                         <div className="collectionStackingView-sectionOptions">
                             <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
-- 
cgit v1.2.3-70-g09d2


From 3257bfdb28fbd4573adc7d507827d6a6957fc7d2 Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Sun, 2 Aug 2020 12:15:58 -0500
Subject: merging menuiconbox into fonticonbox

---
 src/client/documents/Documents.ts               |  9 +---
 src/client/util/CurrentUserUtils.ts             |  5 ++-
 src/client/util/SharingManager.tsx              |  6 +++
 src/client/views/MainView.tsx                   | 18 ++++++--
 src/client/views/nodes/DocumentContentsView.tsx |  3 +-
 src/client/views/nodes/FontIconBox.scss         | 56 ++++++++++++++++++++++-
 src/client/views/nodes/FontIconBox.tsx          | 59 ++++++++++++++++++-------
 src/client/views/nodes/MenuIconBox.scss         | 49 --------------------
 src/client/views/nodes/MenuIconBox.tsx          | 33 --------------
 9 files changed, 123 insertions(+), 115 deletions(-)
 delete mode 100644 src/client/views/nodes/MenuIconBox.scss
 delete mode 100644 src/client/views/nodes/MenuIconBox.tsx

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index de0557185..300af4e50 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -31,7 +31,6 @@ import { ColorBox } from "../views/nodes/ColorBox";
 import { ComparisonBox } from "../views/nodes/ComparisonBox";
 import { DocHolderBox } from "../views/nodes/DocHolderBox";
 import { FontIconBox } from "../views/nodes/FontIconBox";
-import { MenuIconBox } from "../views/nodes/MenuIconBox";
 import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { ImageBox } from "../views/nodes/ImageBox";
 import { KeyValueBox } from "../views/nodes/KeyValueBox";
@@ -152,6 +151,7 @@ export interface DocumentOptions {
     annotationOn?: Doc;
     removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
     dbDoc?: Doc;
+    menuIcon?: boolean; // if the font icon box is in the menu
     linkRelationship?: string; // type of relatinoship a link represents
     ischecked?: ScriptField; // returns whether a font icon box is checked
     activeInkPen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts)
@@ -301,9 +301,6 @@ export namespace Docs {
                 layout: { view: FontIconBox, dataField: defaultDataKey },
                 options: { _width: 40, _height: 40, borderRounding: "100%" },
             }],
-            [DocumentType.MENUICON, {
-                layout: { view: MenuIconBox, dataField: defaultDataKey },
-            }],
             // [DocumentType.RECOMMENDATION, {
             //     layout: { view: RecommendationsBox, dataField: defaultDataKey },
             //     options: { _width: 200, _height: 200 },
@@ -794,10 +791,6 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) });
         }
 
-        export function MenuIconDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.MENUICON), undefined, { hideLinkButton: true, ...(options || {}) });
-        }
-
         export function PresElementBoxDocument(options?: DocumentOptions) {
             return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) });
         }
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b37c91c56..7b6868f73 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -48,6 +48,8 @@ export class CurrentUserUtils {
     @observable public static closedStack: any | undefined;
     @observable public static searchStack: any | undefined;
 
+    @observable public static selectedPanel: string = "none";
+
     // sets up the default User Templates - slideView, queryView, descriptionView
     static setupUserTemplateButtons(doc: Doc) {
         if (doc["template-button-query"] === undefined) {
@@ -520,8 +522,9 @@ export class CurrentUserUtils {
     static setupMenuPanel(doc: Doc) {
         if (doc.menuStack === undefined) {
             const buttons = CurrentUserUtils.menuBtnDescriptions();
-            const menuBtns = buttons.map(({ title, icon, click }) => Docs.Create.MenuIconDocument({
+            const menuBtns = buttons.map(({ title, icon, click }) => Docs.Create.FontIconDocument({
                 icon,
+                menuIcon: true,
                 title,
                 _backgroundColor: "black",
                 stayInCollection: true,
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 000b3e46c..eda12a5e9 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -179,6 +179,12 @@ export default class SharingManager extends React.Component<{}> {
         }
     }
 
+    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, target: Doc) => {
+        const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+        if (user) this.setInternalSharing(user, permission, target);
+        else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, target);
+    }
+
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
      * @param group 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index d095a9836..c83aeee25 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -234,7 +234,7 @@ export class MainView extends React.Component {
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            _width: this._panelWidth * .7 - this.propertiesWidth(),
+            _width: this._panelWidth * .7 - this.propertiesWidth() * 0.7,
             _height: this._panelHeight,
             title: "Collection " + workspaceCount,
         };
@@ -367,7 +367,7 @@ export class MainView extends React.Component {
     @computed get dockingContent() {
         TraceMobx();
         const mainContainer = this.mainContainer;
-        const width = this.flyoutWidth;
+        const width = this.flyoutWidth + this.propertiesWidth();
         return <div className="mainContent-div" onDrop={this.onDrop} style={{ width: `calc(100% - ${width}px)` }}>
             {!mainContainer ? (null) : this.mainDocView}
         </div>;
@@ -388,10 +388,16 @@ export class MainView extends React.Component {
             setupMoveUpEvents(this, e, action((e: PointerEvent) => {
                 this.flyoutWidth = Math.max(e.clientX, 0);
                 this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30;
+                if (this.flyoutWidth === 0) {
+                    CurrentUserUtils.selectedPanel = "none";
+                }
                 return false;
             }), emptyFunction, action(() => {
                 this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0;
                 this.flyoutWidth && (this.sidebarButtonsDoc._columnWidth = this.flyoutWidth / 3 - 30);
+                if (this.flyoutWidth === 0) {
+                    CurrentUserUtils.selectedPanel = "none";
+                }
             }));
         }
     }
@@ -464,7 +470,7 @@ export class MainView extends React.Component {
                 onClick={undefined}
                 ScreenToLocalTransform={this.mainContainerXf}
                 ContentScaling={returnOne}
-                PanelWidth={() => 70}
+                PanelWidth={() => 60}
                 PanelHeight={this.getContentsHeight}
                 renderDepth={0}
                 focus={emptyFunction}
@@ -484,6 +490,7 @@ export class MainView extends React.Component {
 
     @action @undoBatch
     closeFlyout = () => {
+        CurrentUserUtils.selectedPanel = "none";
         this.panelContent = "none";
         this.flyoutWidth = 0;
     }
@@ -493,10 +500,12 @@ export class MainView extends React.Component {
     @action @undoBatch
     selectMenu = (str: string) => {
         if (this.panelContent === str && this.flyoutWidth !== 0) {
+            CurrentUserUtils.selectedPanel = "none";
             this.panelContent = "none";
             this.flyoutWidth = 0;
         } else {
             this.panelContent = str;
+            CurrentUserUtils.selectedPanel = str;
             switch (this.panelContent) {
                 case "Tools": this.sidebarContent.proto = CurrentUserUtils.toolsStack; break;
                 case "Workspace": this.sidebarContent.proto = CurrentUserUtils.workspaceStack; break;
@@ -505,7 +514,8 @@ export class MainView extends React.Component {
                 case "Settings": this.sidebarContent.proto = SettingsManager.Instance.open(); break;
                 case "Sharing": this.sidebarContent.proto = GroupManager.Instance.open(); break;
             }
-            if (str === "Settings" || str === "Sharing" || str === "Help") {
+            if (str === "Settings" || str === "Sharing" || str === "Help" || str === "Import") {
+                CurrentUserUtils.selectedPanel = "none";
                 this.panelContent = "none";
                 this.flyoutWidth = 0;
             } else {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e3f258b8d..e8173d103 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -18,7 +18,6 @@ import { DocHolderBox } from "./DocHolderBox";
 import { DocumentViewProps } from "./DocumentView";
 import "./DocumentView.scss";
 import { FontIconBox } from "./FontIconBox";
-import { MenuIconBox } from "./MenuIconBox";
 import { FieldView, FieldViewProps } from "./FieldView";
 import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import { ImageBox } from "./ImageBox";
@@ -190,7 +189,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
                 blacklistedAttrs={[]}
                 renderInWrapper={false}
                 components={{
-                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, MenuIconBox, LabelBox, SliderBox, FieldView,
+                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, SliderBox, FieldView,
                     CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
                     PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, QueryBox,
                     ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, DocHolderBox, LinkBox, ScriptingBox,
diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss
index 69c835318..13af511a6 100644
--- a/src/client/views/nodes/FontIconBox.scss
+++ b/src/client/views/nodes/FontIconBox.scss
@@ -10,9 +10,9 @@
 
     .fontIconBox-label {
         background: gray;
-        color:white;
+        color: white;
         border-radius: 8px;
-        width:100%;
+        width: 100%;
         position: absolute;
         text-align: center;
         font-size: 8px;
@@ -22,6 +22,58 @@
         overflow: hidden;
     }
 
+    svg {
+        width: 95% !important;
+        height: 95%;
+    }
+}
+
+.menuButton {
+    //padding: 7px;
+    padding-left: 5px;
+    width: 100%;
+    width: 60px;
+    height: 70px;
+    padding-top: 3px;
+    padding-bottom: 3px;
+
+    .menuButton-wrap {
+        width: 45px;
+        /* padding: 5px; */
+        touch-action: none;
+        background: black;
+        transform-origin: top left;
+        /* margin-bottom: 5px; */
+        margin-top: 5px;
+        margin-right: 25px;
+        border-radius: 8px;
+
+        &:hover {
+            background: rgb(61, 61, 61);
+            cursor: pointer;
+        }
+    }
+
+    .menuButton-label {
+        color: white;
+        margin-right: 4px;
+        border-radius: 8px;
+        width: 42px;
+        position: relative;
+        text-align: center;
+        font-size: 7px;
+        margin-top: 1px;
+        letter-spacing: normal;
+        padding: 3px;
+        background-color: inherit;
+    }
+
+    .menuButton-icon {
+        width: auto;
+        height: 32px;
+        padding: 5px;
+    }
+
     svg {
         width: 95% !important;
         height: 95%;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index 2611d2ca7..720d6a15d 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -12,8 +12,10 @@ import { Doc } from '../../../fields/Doc';
 import { ContextMenu } from '../ContextMenu';
 import { ScriptField } from '../../../fields/ScriptField';
 import { Tooltip } from '@material-ui/core';
+import { createUnionOrIntersectionTypeNode } from 'typescript';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 const FontIconSchema = createSchema({
-    icon: "string"
+    icon: "string",
 });
 
 type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
@@ -59,20 +61,45 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
     }
 
     render() {
-        const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
-        const refLayout = Doc.Layout(referenceDoc);
-        const button = <button className="fontIconBox-outerDiv" ref={this._ref} onContextMenu={this.specificContextMenu}
-            style={{
-                padding: Cast(this.layoutDoc._xPadding, "number", null),
-                background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
-                boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
-            }}>
-            <FontAwesomeIcon className="fontIconBox-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
-            {!this.rootDoc.title ? (null) : <div className="fontIconBox-label" style={{ width: this.rootDoc.label ? "max-content" : undefined }}> {StrCast(this.rootDoc.label, StrCast(this.rootDoc.title).substring(0, 6))} </div>}
-        </button>;
-        return !this.layoutDoc.toolTip ? button :
-            <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
-                {button}
-            </Tooltip>;
+
+        //style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }}>
+
+        if (this.layoutDoc.menuIcon) {
+
+            let backgroundColor = "black";
+            if (this.dataDoc.title === "Sharing" || this.dataDoc.title === "Help" || this.dataDoc.title === "Settings" || this.dataDoc.title === "Import") {
+                backgroundColor = "black";
+            } else {
+                backgroundColor = CurrentUserUtils.selectedPanel === this.dataDoc.title ? "lightgrey" : "black";
+            }
+
+            const color = backgroundColor === "lightgrey" ? "black" : "white";
+            const menuBTN = <div className="menuButton" style={{ backgroundColor: backgroundColor }}>
+
+                <div className="menuButton-wrap" style={{ backgroundColor: backgroundColor }}>
+                    <FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size="lg" />
+                    <div className="menuButton-label" style={{ color: color }}> {this.dataDoc.title} </div>
+                </div>
+            </div>;
+
+            return menuBTN;
+        } else {
+            const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
+            const refLayout = Doc.Layout(referenceDoc);
+            const button = <button className="fontIconBox-outerDiv" ref={this._ref} onContextMenu={this.specificContextMenu}
+                style={{
+                    padding: Cast(this.layoutDoc._xPadding, "number", null),
+                    background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
+                    boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
+                }}>
+                <FontAwesomeIcon className="fontIconBox-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
+                {!this.rootDoc.title ? (null) : <div className="fontIconBox-label" style={{ width: this.rootDoc.label ? "max-content" : undefined }}> {StrCast(this.rootDoc.label, StrCast(this.rootDoc.title).substring(0, 6))} </div>}
+            </button>;
+            return !this.layoutDoc.toolTip ? button :
+                <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+                    {button}
+                </Tooltip>;
+        }
+
     }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/MenuIconBox.scss b/src/client/views/nodes/MenuIconBox.scss
deleted file mode 100644
index 1b72f5a8f..000000000
--- a/src/client/views/nodes/MenuIconBox.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-.menuButton {
-    //padding: 7px;
-    padding-left: 7px;
-    width: 100%;
-    width: 60px;
-    height: 70px;
-
-    .menuButton-wrap {
-        width: 45px;
-        /* padding: 5px; */
-        touch-action: none;
-        background: black;
-        transform-origin: top left;
-        /* margin-bottom: 5px; */
-        margin-top: 5px;
-        margin-right: 25px;
-        border-radius: 8px;
-
-        &:hover {
-            background: rgb(61, 61, 61);
-            cursor: pointer;
-        }
-    }
-
-    .menuButton-label {
-        color: white;
-        margin-right: 4px;
-        border-radius: 8px;
-        width: 42px;
-        position: relative;
-        text-align: center;
-        font-size: 8px;
-        margin-top: 1px;
-        letter-spacing: normal;
-        padding: 3px;
-        background-color: inherit;
-    }
-
-    .menuButton-icon {
-        width: auto;
-        height: 35px;
-        padding: 5px;
-    }
-
-    svg {
-        width: 95% !important;
-        height: 95%;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/MenuIconBox.tsx b/src/client/views/nodes/MenuIconBox.tsx
deleted file mode 100644
index 0aa7b327e..000000000
--- a/src/client/views/nodes/MenuIconBox.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { createSchema, makeInterface } from '../../../fields/Schema';
-import { StrCast } from '../../../fields/Types';
-import { DocComponent } from '../DocComponent';
-import { FieldView, FieldViewProps } from './FieldView';
-import './MenuIconBox.scss';
-const MenuIconSchema = createSchema({
-    icon: "string"
-});
-
-type MenuIconDocument = makeInterface<[typeof MenuIconSchema]>;
-const MenuIconDocument = makeInterface(MenuIconSchema);
-@observer
-export class MenuIconBox extends DocComponent<FieldViewProps, MenuIconDocument>(MenuIconDocument) {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MenuIconBox, fieldKey); }
-    _ref: React.RefObject<HTMLButtonElement> = React.createRef();
-
-    render() {
-
-        const color = this.props.backgroundColor?.(this.props.Document) === "lightgrey" ? "black" : "white";
-        const menuBTN = <div className="menuButton" style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }}>
-            <div className="menuButton-wrap"
-                style={{ backgroundColor: this.props.backgroundColor?.(this.props.Document) }} >
-                <FontAwesomeIcon className="menuButton-icon" icon={StrCast(this.dataDoc.icon, "user") as any} color={color} size="lg" />
-                <div className="menuButton-label" style={{ color: color }}> {this.dataDoc.title} </div>
-            </div>
-        </div>;
-
-        return menuBTN;
-    }
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 5bbfed1556e47546eb6768681a86859b011a4c18 Mon Sep 17 00:00:00 2001
From: anika-ahluwalia <anika.ahluwalia@gmail.com>
Date: Sun, 2 Aug 2020 13:55:38 -0500
Subject: sharing fix

---
 src/client/util/SharingManager.tsx                               | 4 ++--
 .../views/collections/collectionFreeForm/PropertiesView.scss     | 6 +++---
 .../views/collections/collectionFreeForm/PropertiesView.tsx      | 9 ++++++---
 3 files changed, 11 insertions(+), 8 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index eda12a5e9..7a0fe4140 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -142,7 +142,7 @@ export default class SharingManager extends React.Component<{}> {
      * @param group 
      * @param permission 
      */
-    setInternalGroupSharing = (group: Doc, permission: string) => {
+    setInternalGroupSharing = (group: Doc, permission: string, targetDoc?: Doc) => {
         const members: string[] = JSON.parse(StrCast(group.members));
         const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
@@ -224,7 +224,7 @@ export default class SharingManager extends React.Component<{}> {
         }
     }
 
-    setInternalSharing = (recipient: ValidatedUser, permission: string) => {
+    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
         const { user, notificationDoc } = recipient;
         const target = targetDoc || this.targetDoc!;
         const key = user.email.replace('.', '_');
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
index 74f32275a..a96716659 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss
@@ -241,7 +241,7 @@
 
             .propertiesView-sharingTable-item-name {
                 font-weight: bold;
-                width: 80px;
+                width: 95px;
                 overflow-x: hidden;
                 display: inline-block;
                 text-overflow: ellipsis;
@@ -259,8 +259,8 @@
                     border: none;
                     background-color: inherit;
                     width: 75px;
-                    text-align: justify; // for Edge
-                    text-align-last: end;
+                    //text-align: justify; // for Edge
+                    //text-align-last: end;
 
                     &:hover {
                         cursor: pointer;
diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
index 3eb3ef8ab..73f0f1bdd 100644
--- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
+++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx
@@ -156,7 +156,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
             const rows: JSX.Element[] = [];
             for (const key of Object.keys(ids).slice().sort()) {
-                if (key[0] === key[0].toUpperCase() || key[0] === "#" || key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) {
+                if ((key[0] === key[0].toUpperCase() && key.substring(0, 3) !== "ACL")
+                    || key[0] === "#" || key === "author" ||
+                    key === "creationDate" || key.indexOf("lastModified") !== -1) {
+
                     const contents = doc[key];
                     if (key[0] === "#") {
                         rows.push(<div className="uneditable-field" key={key}>
@@ -297,8 +300,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     sharingItem(name: string, effectiveAcl: symbol, permission?: string) {
         return <div className="propertiesView-sharingTable-item">
-            <div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "70px" : "80px" }}> {name} </div>
-            {name !== "Me" ? this.notifyIcon : null}
+            <div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "85px" : "80px" }}> {name} </div>
+            {/* {name !== "Me" ? this.notifyIcon : null} */}
             <div className="propertiesView-sharingTable-item-permission">
                 {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name) : permission}
                 {permission === "Owner" ? this.expansionIcon : null}
-- 
cgit v1.2.3-70-g09d2


From 061a5a676ad9ebd821b2ba17d28fef54d8a773d0 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 3 Aug 2020 15:44:39 -0400
Subject: added sharing panel

---
 package.json                         |  2 +-
 src/client/util/CurrentUserUtils.ts  |  6 +++---
 src/client/util/GroupManager.tsx     |  2 +-
 src/client/util/SharingManager.tsx   |  4 ++--
 src/client/views/MainView.tsx        |  9 +++++----
 src/client/views/MainViewNotifs.scss |  2 ++
 src/client/views/MainViewNotifs.tsx  | 29 +++++++++++++++++------------
 src/mobile/AudioUpload.tsx           |  2 +-
 src/mobile/MobileInterface.tsx       |  8 ++++----
 9 files changed, 36 insertions(+), 28 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

diff --git a/package.json b/package.json
index ddf4c3b24..0a3a63ec3 100644
--- a/package.json
+++ b/package.json
@@ -254,4 +254,4 @@
         "xoauth2": "^1.2.0",
         "xregexp": "^4.3.0"
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a99d9451b..4b823231e 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -498,11 +498,11 @@ export class CurrentUserUtils {
         title: string, icon: string, click: string,
     }[] {
         return [
+            { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu(self, "Sharing")' },
             { title: "Workspace", icon: "desktop", click: 'scriptContext.selectMenu(self, "Workspace")' },
             { title: "Catalog", icon: "file", click: 'scriptContext.selectMenu(self, "Catalog")' },
             { title: "Archive", icon: "archive", click: 'scriptContext.selectMenu(self, "Archive")' },
             { title: "Import", icon: "upload", click: 'scriptContext.selectMenu(self, "Import")' },
-            { title: "Sharing", icon: "users", click: 'scriptContext.selectMenu(self, "Sharing")' },
             { title: "Tools", icon: "wrench", click: 'scriptContext.selectMenu(self, "Tools")' },
             { title: "Help", icon: "question-circle", click: 'scriptContext.selectMenu(self, "Help")' },
             { title: "Settings", icon: "cog", click: 'scriptContext.selectMenu(self, "Settings")' },
@@ -821,8 +821,8 @@ export class CurrentUserUtils {
 
     // Right sidebar is where mobile uploads are contained
     static setupRightSidebar(doc: Doc) {
-        if (doc.rightSidebarCollection === undefined) {
-            doc.rightSidebarCollection = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Mobile Uploads" }));
+        if (doc["sidebar-sharing"] === undefined) {
+            doc["sidebar-sharing"] = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "Mobile Uploads" }));
         }
     }
 
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 5215ea35f..229a846a9 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -68,7 +68,7 @@ export default class GroupManager extends React.Component<{}> {
         const evaluating = raw.map(async user => {
             const userDocument = await DocServer.GetRefField(user.userDocumentId);
             if (userDocument instanceof Doc) {
-                const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc);
+                const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
                 runInAction(() => {
                     if (notificationDoc instanceof Doc) {
                         this.users.push(user.email);
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7a0fe4140..9d91ce1ba 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -114,7 +114,7 @@ export default class SharingManager extends React.Component<{}> {
     }
 
     /**
-     * Populates the list of validated users (this.users) by adding registered users which have a rightSidebarCollection.
+     * Populates the list of validated users (this.users) by adding registered users which have a sidebar-sharing.
      */
     populateUsers = async () => {
         runInAction(() => this.users = []);
@@ -125,7 +125,7 @@ export default class SharingManager extends React.Component<{}> {
             if (isCandidate) {
                 const userDocument = await DocServer.GetRefField(user.userDocumentId);
                 if (userDocument instanceof Doc) {
-                    const notificationDoc = await Cast(userDocument.rightSidebarCollection, Doc);
+                    const notificationDoc = await Cast(userDocument["sidebar-sharing"], Doc);
                     runInAction(() => {
                         if (notificationDoc instanceof Doc) {
                             this.users.push({ user, notificationDoc });
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ca5154ef7..66545ea1f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -288,7 +288,7 @@ export class MainView extends React.Component {
         }
         // if there is a pending doc, and it has new data, show it (syip: we use a timeout to prevent collection docking view from being uninitialized)
         setTimeout(async () => {
-            const col = this.userDoc && await Cast(this.userDoc.rightSidebarCollection, Doc);
+            const col = this.userDoc && await Cast(this.userDoc["sidebar-sharing"], Doc);
             col && Cast(col.data, listSpec(Doc)) && runInAction(() => MainViewNotifs.NotifsCol = col);
         }, 100);
         return true;
@@ -511,7 +511,7 @@ export class MainView extends React.Component {
                 case "Catalog": panelDoc = Doc.UserDoc()["sidebar-catalog"] as Doc ?? undefined; break;
                 case "Archive": panelDoc = Doc.UserDoc()["sidebar-recentlyClosed"] as Doc ?? undefined; break;
                 case "Settings": SettingsManager.Instance.open(); break;
-                case "Sharing": GroupManager.Instance.open(); break;
+                case "Sharing": panelDoc = Doc.UserDoc()["sidebar-sharing"] as Doc ?? undefined; break;
                 case "UserDoc": panelDoc = Doc.UserDoc()["sidebar-userDoc"] as Doc ?? undefined; break;
             }
             this.sidebarContent.proto = panelDoc;
@@ -561,7 +561,8 @@ export class MainView extends React.Component {
                             top: (this.flyoutWidth !== 0 && this._flyoutTranslate) ? "" : "0"
                         }} />
                         <div className="mainview-libraryHandle-icon">
-                            <FontAwesomeIcon icon="chevron-left" color="black" size="sm" /> </div>
+                            <FontAwesomeIcon icon="chevron-left" color="black" size="sm" />
+                        </div>
                     </div> : null}
                     <div className="mainView-libraryFlyout" style={{
                         //transformOrigin: this._flyoutTranslate ? "" : "left center",
@@ -574,6 +575,7 @@ export class MainView extends React.Component {
                     </div>
                 </div>
                 {this.dockingContent}
+                <MainViewNotifs />
                 {this.showProperties ? (null) :
                     <div className="mainView-propertiesDragger" title="Properties View Dragger" onPointerDown={this.onPropertiesPointerDown}
                         style={{ right: rightFlyout, top: "50%" }}>
@@ -632,7 +634,6 @@ export class MainView extends React.Component {
         if (dockedBtns instanceof Doc) {
             return <div className="mainView-docButtons" ref={this._docBtnRef}
                 style={{ height: !dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
-                <MainViewNotifs />
                 <CollectionLinearView
                     Document={dockedBtns}
                     DataDoc={undefined}
diff --git a/src/client/views/MainViewNotifs.scss b/src/client/views/MainViewNotifs.scss
index 25ec95643..92d7d6ee3 100644
--- a/src/client/views/MainViewNotifs.scss
+++ b/src/client/views/MainViewNotifs.scss
@@ -1,5 +1,7 @@
 .mainNotifs-container {
     position:absolute;
+    z-index: 1000;
+    top: 12px;
 
     .mainNotifs-badge {
         position: absolute;
diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx
index 05f890485..ce47e1cf1 100644
--- a/src/client/views/MainViewNotifs.tsx
+++ b/src/client/views/MainViewNotifs.tsx
@@ -3,28 +3,33 @@ import { observer } from 'mobx-react';
 import "normalize.css";
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { emptyFunction } from '../../Utils';
-import { SetupDrag } from '../util/DragManager';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../Utils';
+import { SetupDrag, DragManager } from '../util/DragManager';
 import "./MainViewNotifs.scss";
-import { CollectionDockingView } from './collections/CollectionDockingView';
+import { MainView } from './MainView';
 
 
 @observer
 export class MainViewNotifs extends React.Component {
-
     @observable static NotifsCol: Opt<Doc>;
-    openNotifsCol = () => {
-        if (MainViewNotifs.NotifsCol) {
-            CollectionDockingView.AddRightSplit(MainViewNotifs.NotifsCol);
-        }
+    _notifsRef = React.createRef<HTMLDivElement>();
+
+    onPointerDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e,
+            (e: PointerEvent) => {
+                const dragData = new DragManager.DocumentDragData([MainViewNotifs.NotifsCol!]);
+                DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+                return true;
+            },
+            returnFalse,
+            () => MainViewNotifs.NotifsCol && MainView.Instance.selectMenu(MainViewNotifs.NotifsCol, "Sharing"));
     }
+
     render() {
         const length = MainViewNotifs.NotifsCol ? DocListCast(MainViewNotifs.NotifsCol.data).length : 0;
-        const notifsRef = React.createRef<HTMLDivElement>();
-        const dragNotifs = action(() => MainViewNotifs.NotifsCol!);
-        return <div className="mainNotifs-container" ref={notifsRef}>
+        return <div className="mainNotifs-container" style={{ width: 15, height: 15 }} ref={this._notifsRef}>
             <button className="mainNotifs-badge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
-                onClick={this.openNotifsCol} onPointerDown={MainViewNotifs.NotifsCol ? SetupDrag(notifsRef, dragNotifs) : emptyFunction}>
+                onPointerDown={this.onPointerDown} >
                 {length}
             </button>
         </div>;
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index b75102e43..8ae504f1b 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -53,7 +53,7 @@ export class AudioUpload extends React.Component {
      * Pushing the audio doc onto Dash Web through the right side bar
      */
     uploadAudio = () => {
-        const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const audioRightSidebar = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         const audioDoc = this._audioCol;
         const data = Cast(audioRightSidebar.data, listSpec(Doc));
         for (let i = 1; i < 8; i++) {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 3a889b0db..40aa65372 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -436,7 +436,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
@@ -454,7 +454,7 @@ export class MobileInterface extends React.Component {
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
         if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.UserDoc().rightSidebarCollection && this._activeDoc.title !== "WORKSPACES") {
+            this._activeDoc !== Doc.UserDoc()["sidebar-sharing"] && this._activeDoc.title !== "WORKSPACES") {
             return (
                 <div className="docButton"
                     style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
@@ -605,7 +605,7 @@ export class MobileInterface extends React.Component {
 
     // Returns the image upload pop up
     @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         return <Uploader Document={doc} />;
     }
 
@@ -628,7 +628,7 @@ export class MobileInterface extends React.Component {
      */
     @action
     switchToMobileUploads = () => {
-        const mobileUpload = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc;
+        const mobileUpload = Cast(Doc.UserDoc()["sidebar-sharing"], Doc) as Doc;
         this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
     }
-- 
cgit v1.2.3-70-g09d2

