From a22f9b3c98c11d1170d1b0548bab61fc73fbc44f Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 23 Jul 2020 22:20:49 +0530
Subject: groups bugfix

---
 src/client/util/GroupManager.tsx | 8 ++++++++
 1 file changed, 8 insertions(+)

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

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 2e5ecc543..a679c9087 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -48,6 +48,14 @@ export default class GroupManager extends React.Component<{}> {
 
     componentDidMount() {
         this.populateUsers();
+        DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
+            groups?.forEach(group => {
+                const members: string[] = JSON.parse(StrCast(group.members));
+                if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
+            });
+
+            setGroups(this.currentUserGroups);
+        });
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 9d3308dd8fbcbbd854c66a3e53ca4ba66853dd30 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Thu, 23 Jul 2020 22:24:23 +0530
Subject: groups bugfix

---
 src/client/util/GroupManager.tsx | 28 ++++++++++++----------------
 1 file changed, 12 insertions(+), 16 deletions(-)

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

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index a679c9087..7d02b1e66 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -48,14 +48,6 @@ export default class GroupManager extends React.Component<{}> {
 
     componentDidMount() {
         this.populateUsers();
-        DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
-            groups?.forEach(group => {
-                const members: string[] = JSON.parse(StrCast(group.members));
-                if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
-            });
-
-            setGroups(this.currentUserGroups);
-        });
     }
 
     /**
@@ -82,6 +74,17 @@ export default class GroupManager extends React.Component<{}> {
         return Promise.all(evaluating);
     }
 
+    populateGroups = () => {
+        DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
+            groups?.forEach(group => {
+                const members: string[] = JSON.parse(StrCast(group.members));
+                if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
+            });
+
+            setGroups(this.currentUserGroups);
+        });
+    }
+
     /**
      * @returns the options to be rendered in the dropdown menu to add users and create a group.
      */
@@ -97,14 +100,7 @@ export default class GroupManager extends React.Component<{}> {
         SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
-        DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
-            groups?.forEach(group => {
-                const members: string[] = JSON.parse(StrCast(group.members));
-                if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
-            });
-
-            setGroups(this.currentUserGroups);
-        });
+        this.populateGroups();
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 17ccb0a1a37c906c0151d3da5e92de3d94205924 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 24 Jul 2020 01:45:05 +0530
Subject: groups and acl bugfix

---
 src/client/util/GroupManager.tsx | 1 +
 src/fields/util.ts               | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

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

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 7d02b1e66..a727d21d0 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -48,6 +48,7 @@ export default class GroupManager extends React.Component<{}> {
 
     componentDidMount() {
         this.populateUsers();
+        this.populateGroups();
     }
 
     /**
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 20b072786..cd1e24826 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -210,7 +210,8 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
         return true;
     }
 
-    if (typeof prop === "string" && prop.startsWith("ACL") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
+    if (typeof prop === "string" && prop.startsWith("ACL") && ((target.author && Doc.CurrentUserEmail !== target.author) || !["Can Edit", "Can Add", "Can View", "Not Shared", 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;
 
     if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
         if (!prop.startsWith("_")) {
-- 
cgit v1.2.3-70-g09d2


From 2f49497e9d74eda97b2327ca4dbcb0a11ac6c6c9 Mon Sep 17 00:00:00 2001
From: usodhi <61431818+usodhi@users.noreply.github.com>
Date: Fri, 24 Jul 2020 17:11:09 +0530
Subject: added admin acl + renamed LinkCreatedBox to TaskCompletedBox and
 added custom text functionality + popups on sharing docs and creating groups
 + relocated SharingPermissions

---
 src/client/util/GroupManager.tsx                | 15 +++++++-
 src/client/util/SharingManager.tsx              | 46 +++++++++++++++----------
 src/client/views/DocComponent.tsx               |  3 +-
 src/client/views/MainView.tsx                   |  4 +--
 src/client/views/collections/CollectionView.tsx |  8 ++---
 src/client/views/nodes/DocumentLinksButton.tsx  | 18 +++++-----
 src/client/views/nodes/DocumentView.tsx         | 17 +++++----
 src/client/views/nodes/LinkCreatedBox.scss      | 21 -----------
 src/client/views/nodes/LinkCreatedBox.tsx       | 31 -----------------
 src/client/views/nodes/LinkDescriptionPopup.tsx |  4 +--
 src/client/views/nodes/TaskCompletedBox.scss    | 20 +++++++++++
 src/client/views/nodes/TaskCompletedBox.tsx     | 32 +++++++++++++++++
 src/fields/Doc.ts                               |  7 ++--
 src/fields/util.ts                              | 28 +++++++++------
 14 files changed, 143 insertions(+), 111 deletions(-)
 delete mode 100644 src/client/views/nodes/LinkCreatedBox.scss
 delete mode 100644 src/client/views/nodes/LinkCreatedBox.tsx
 create mode 100644 src/client/views/nodes/TaskCompletedBox.scss
 create mode 100644 src/client/views/nodes/TaskCompletedBox.tsx

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

diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index a727d21d0..72fba5c1b 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -16,6 +16,7 @@ import { StrCast, Cast } from "../../fields/Types";
 import GroupMemberView from "./GroupMemberView";
 import { setGroups } from "../../fields/util";
 import { DocServer } from "../DocServer";
+import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 library.add(fa.faPlus, fa.faTimes, fa.faInfoCircle);
 
@@ -36,11 +37,13 @@ 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[] = [];
     @observable private buttonColour: "#979797" | "black" = "#979797";
     @observable private groupSort: "ascending" | "descending" | "none" = "none";
 
 
+
     constructor(props: Readonly<{}>) {
         super(props);
         GroupManager.Instance = this;
@@ -303,6 +306,14 @@ export default class GroupManager extends React.Component<{}> {
         this.selectedUsers = null;
         this.inputRef.current.value = "";
         this.buttonColour = "#979797";
+
+        const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect();
+        TaskCompletionBox.popupX = left - 2 * width;
+        TaskCompletionBox.popupY = top;
+        TaskCompletionBox.textDisplayed = "Group created!";
+        TaskCompletionBox.taskCompleted = true;
+        setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
+
     }
 
     private get groupCreationModal() {
@@ -345,7 +356,9 @@ export default class GroupManager extends React.Component<{}> {
                         })
                     }}
                 />
-                <button onClick={this.createGroup}
+                <button
+                    ref={this.createGroupButtonRef}
+                    onClick={this.createGroup}
                     style={{ background: this.buttonColour }}
                     disabled={this.buttonColour === "#979797"}
                 >
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 9c857a7c0..452a58d21 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -20,7 +20,8 @@ import GroupMemberView from "./GroupMemberView";
 import Select from "react-select";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { List } from "../../fields/List";
-import { distributeAcls } from "../../fields/util";
+import { distributeAcls, SharingPermissions } from "../../fields/util";
+import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
 
 library.add(fa.faCopy, fa.faTimes);
 
@@ -29,13 +30,6 @@ export interface User {
     userDocumentId: string;
 }
 
-export enum SharingPermissions {
-    Edit = "Can Edit",
-    Add = "Can Add",
-    View = "Can View",
-    None = "Not Shared"
-}
-
 interface GroupOptions {
     label: string;
     options: UserOptions[];
@@ -69,6 +63,8 @@ export default class SharingManager extends React.Component<{}> {
     @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();
+
 
 
     // private get linkVisible() {
@@ -90,6 +86,8 @@ export default class SharingManager extends React.Component<{}> {
     public close = action(() => {
         this.isOpen = false;
         this.users = [];
+        this.selectedUsers = null;
+
         setTimeout(action(() => {
             // this.copied = false;
             DictationOverlay.Instance.hasActiveModal = false;
@@ -235,7 +233,7 @@ export default class SharingManager extends React.Component<{}> {
     private get sharingOptions() {
         return Object.values(SharingPermissions).map(permission => {
             return (
-                <option key={permission} value={permission}>
+                <option key={permission} value={permission} selected={permission === SharingPermissions.Edit}>
                     {permission}
                 </option>
             );
@@ -284,15 +282,25 @@ export default class SharingManager extends React.Component<{}> {
 
     @action
     share = () => {
-        this.selectedUsers?.forEach(user => {
-            if (user.value.includes(indType)) {
-                this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
-            }
-            else {
-                this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
-            }
-        });
-        this.selectedUsers = null;
+        if (this.selectedUsers) {
+            this.selectedUsers.forEach(user => {
+                if (user.value.includes(indType)) {
+                    this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+                }
+                else {
+                    this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
+                }
+            });
+
+            const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
+            TaskCompletionBox.popupX = left - 1.5 * width;
+            TaskCompletionBox.popupY = top - height;
+            TaskCompletionBox.textDisplayed = "Document shared!";
+            TaskCompletionBox.taskCompleted = true;
+            setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
+
+            this.selectedUsers = null;
+        }
     }
 
     sortUsers = (u1: ValidatedUser, u2: ValidatedUser) => {
@@ -456,7 +464,7 @@ export default class SharingManager extends React.Component<{}> {
                             <select className="permissions-select" onChange={this.handlePermissionsChange}>
                                 {this.sharingOptions}
                             </select>
-                            <button className="share-button" onClick={this.share}>
+                            <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
                                 Share
                             </button>
                         </div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 95c1bcda8..00d570670 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -7,8 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 import { List } from '../../fields/List';
 import { DateField } from '../../fields/DateField';
 import { ScriptField } from '../../fields/ScriptField';
-import { GetEffectiveAcl, getPlaygroundMode } from '../../fields/util';
-import { SharingPermissions } from '../util/SharingManager';
+import { GetEffectiveAcl, getPlaygroundMode, SharingPermissions } from '../../fields/util';
 
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 225fb2e8e..81f2feee0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -61,7 +61,7 @@ import { DocumentManager } from '../util/DocumentManager';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { LinkMenu } from './linking/LinkMenu';
 import { LinkDocPreview } from './nodes/LinkDocPreview';
-import { LinkCreatedBox } from './nodes/LinkCreatedBox';
+import { TaskCompletionBox } from './nodes/TaskCompletedBox';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
 import FormatShapePane from "./collections/collectionFreeForm/FormatShapePane";
 import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager';
@@ -623,7 +623,7 @@ export class MainView extends React.Component {
                 {this.mainContent}
             </GestureOverlay>
             <PreviewCursor />
-            <LinkCreatedBox />
+            <TaskCompletionBox />
             <ContextMenu />
             <FormatShapePane />
             <RadialMenu />
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 53fd83f26..4848508be 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -8,7 +8,7 @@ import * as React from 'react';
 import Lightbox from 'react-image-lightbox-with-rotate';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 import { DateField } from '../../../fields/DateField';
-import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate } from '../../../fields/Doc';
+import { AclAddonly, AclReadonly, DataSym, Doc, DocListCast, Field, Opt, AclEdit, AclSym, AclPrivate, AclAdmin } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
@@ -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 } from '../../../fields/util';
+import { TraceMobx, GetEffectiveAcl, getPlaygroundMode, distributeAcls, 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';
@@ -48,7 +48,6 @@ import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
 import './CollectionView.scss';
 import CollectionMenu from './CollectionMenu';
-import { SharingPermissions } from '../../util/SharingManager';
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -111,7 +110,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
         [AclPrivate, SharingPermissions.None],
         [AclReadonly, SharingPermissions.View],
         [AclAddonly, SharingPermissions.Add],
-        [AclEdit, SharingPermissions.Edit]
+        [AclEdit, SharingPermissions.Edit],
+        [AclAdmin, SharingPermissions.Admin]
     ]);
 
     get collectionViewType(): CollectionViewType | undefined {
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 13bec5d7f..f0e80045d 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -10,7 +10,7 @@ import React = require("react");
 import { DocUtils } from "../../documents/Documents";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { LinkDocPreview } from "./LinkDocPreview";
-import { LinkCreatedBox } from "./LinkCreatedBox";
+import { TaskCompletionBox } from "./TaskCompletedBox";
 import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
 import { LinkManager } from "../../util/LinkManager";
 import { Tooltip } from "@material-ui/core";
@@ -101,15 +101,15 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
                         runInAction(() => {
                             if (linkDoc) {
-                                LinkCreatedBox.popupX = e.screenX;
-                                LinkCreatedBox.popupY = e.screenY - 133;
-                                LinkCreatedBox.linkCreated = true;
+                                TaskCompletionBox.popupX = e.screenX;
+                                TaskCompletionBox.popupY = e.screenY - 133;
+                                TaskCompletionBox.taskCompleted = true;
 
                                 LinkDescriptionPopup.popupX = e.screenX;
                                 LinkDescriptionPopup.popupY = e.screenY - 100;
                                 LinkDescriptionPopup.descriptionPopup = true;
 
-                                setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+                                setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
                             }
 
                         });
@@ -134,9 +134,9 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
 
                     runInAction(() => {
                         if (linkDoc) {
-                            LinkCreatedBox.popupX = e.screenX;
-                            LinkCreatedBox.popupY = e.screenY - 133;
-                            LinkCreatedBox.linkCreated = true;
+                            TaskCompletionBox.popupX = e.screenX;
+                            TaskCompletionBox.popupY = e.screenY - 133;
+                            TaskCompletionBox.taskCompleted = true;
 
                             if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) {
                                 LinkDescriptionPopup.popupX = e.screenX;
@@ -144,7 +144,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                                 LinkDescriptionPopup.descriptionPopup = true;
                             }
 
-                            setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500);
+                            setTimeout(action(() => { TaskCompletionBox.taskCompleted = false; }), 2500);
                         }
                     });
                 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9e492650b..1d4b61545 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema";
 import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types";
-import { TraceMobx, GetEffectiveAcl } from '../../../fields/util';
+import { TraceMobx, GetEffectiveAcl, SharingPermissions } from '../../../fields/util';
 import { GestureUtils } from '../../../pen-gestures/GestureUtils';
 import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
@@ -25,7 +25,7 @@ import { InteractionUtils } from '../../util/InteractionUtils';
 import { Scripting } from '../../util/Scripting';
 import { SearchUtil } from '../../util/SearchUtil';
 import { SelectionManager } from "../../util/SelectionManager";
-import SharingManager, { SharingPermissions } from '../../util/SharingManager';
+import SharingManager from '../../util/SharingManager';
 import { Transform } from "../../util/Transform";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { CollectionView, CollectionViewType } from '../collections/CollectionView';
@@ -41,7 +41,7 @@ import { RadialMenu } from './RadialMenu';
 import React = require("react");
 import { DocumentLinksButton } from './DocumentLinksButton';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { LinkCreatedBox } from './LinkCreatedBox';
+import { TaskCompletionBox } from './TaskCompletedBox';
 import { LinkDescriptionPopup } from './LinkDescriptionPopup';
 import { LinkManager } from '../../util/LinkManager';
 
@@ -629,15 +629,15 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         const makeLink = action((linkDoc: Doc) => {
             LinkManager.currentLink = linkDoc;
 
-            LinkCreatedBox.popupX = de.x;
-            LinkCreatedBox.popupY = de.y - 33;
-            LinkCreatedBox.linkCreated = true;
+            TaskCompletionBox.popupX = de.x;
+            TaskCompletionBox.popupY = de.y - 33;
+            TaskCompletionBox.taskCompleted = true;
 
             LinkDescriptionPopup.popupX = de.x;
             LinkDescriptionPopup.popupY = de.y;
             LinkDescriptionPopup.descriptionPopup = true;
 
-            setTimeout(action(() => LinkCreatedBox.linkCreated = false), 2500);
+            setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
         });
         if (de.complete.annoDragData) {
             /// this whole section for handling PDF annotations looks weird.  Need to rethink this to make it cleaner
@@ -818,6 +818,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
         !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" });
         cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
 
+        // to be removed for baseline
+        cm.addItem({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+
         // 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" });
diff --git a/src/client/views/nodes/LinkCreatedBox.scss b/src/client/views/nodes/LinkCreatedBox.scss
deleted file mode 100644
index 3cbd38b55..000000000
--- a/src/client/views/nodes/LinkCreatedBox.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-.linkCreatedBox-fade {
-    border: 1px solid rgb(100, 100, 100);
-
-
-    width: auto;
-    position: absolute;
-
-    height: auto;
-    z-index: 10000;
-    border-radius: 13px;
-    font-size: 13px;
-    white-space: nowrap;
-
-    color: rgb(100, 100, 100);
-    background-color: rgba(250, 250, 250, 0.85);
-    padding-top: 6.5px;
-    padding-bottom: 6.5px;
-    font-weight: bold;
-    padding-left: 9px;
-    padding-right: 9px;
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkCreatedBox.tsx b/src/client/views/nodes/LinkCreatedBox.tsx
deleted file mode 100644
index 648ae23c8..000000000
--- a/src/client/views/nodes/LinkCreatedBox.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React = require("react");
-import { observer } from "mobx-react";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
-import "./LinkCreatedBox.scss";
-import { observable, action } from "mobx";
-import { Fade } from "@material-ui/core";
-
-
-@observer
-export class LinkCreatedBox extends React.Component<{}> {
-
-    @observable public static linkCreated: boolean = false;
-    @observable public static popupX: number = 500;
-    @observable public static popupY: number = 150;
-
-    @action
-    public static changeLinkCreated = () => {
-        LinkCreatedBox.linkCreated = !LinkCreatedBox.linkCreated;
-    }
-
-    render() {
-        return <Fade in={LinkCreatedBox.linkCreated}>
-            <div className="linkCreatedBox-fade"
-                style={{
-                    left: LinkCreatedBox.popupX ? LinkCreatedBox.popupX : 500,
-                    top: LinkCreatedBox.popupY ? LinkCreatedBox.popupY : 150,
-                }}>Link Created</div>
-        </Fade>;
-    }
-} 
\ No newline at end of file
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index 06e8d30d1..d8fe47f4e 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -4,7 +4,7 @@ import "./LinkDescriptionPopup.scss";
 import { observable, action } from "mobx";
 import { EditableView } from "../EditableView";
 import { LinkManager } from "../../util/LinkManager";
-import { LinkCreatedBox } from "./LinkCreatedBox";
+import { TaskCompletionBox } from "./TaskCompletedBox";
 
 
 @observer
@@ -31,7 +31,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
     onClick = (e: PointerEvent) => {
         if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) {
             LinkDescriptionPopup.descriptionPopup = false;
-            LinkCreatedBox.linkCreated = false;
+            TaskCompletionBox.taskCompleted = false;
         }
     }
 
diff --git a/src/client/views/nodes/TaskCompletedBox.scss b/src/client/views/nodes/TaskCompletedBox.scss
new file mode 100644
index 000000000..80b750b39
--- /dev/null
+++ b/src/client/views/nodes/TaskCompletedBox.scss
@@ -0,0 +1,20 @@
+.taskCompletedBox-fade {
+    border: 1px solid rgb(100, 100, 100);
+
+    width: auto;
+    position: absolute;
+
+    height: auto;
+    z-index: 10000;
+    border-radius: 13px;
+    font-size: 13px;
+    white-space: nowrap;
+
+    color: rgb(100, 100, 100);
+    background-color: rgba(250, 250, 250, 0.85);
+    padding-top: 6.5px;
+    padding-bottom: 6.5px;
+    font-weight: bold;
+    padding-left: 9px;
+    padding-right: 9px;
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/TaskCompletedBox.tsx b/src/client/views/nodes/TaskCompletedBox.tsx
new file mode 100644
index 000000000..1ba2d1713
--- /dev/null
+++ b/src/client/views/nodes/TaskCompletedBox.tsx
@@ -0,0 +1,32 @@
+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";
+
+
+@observer
+export class TaskCompletionBox extends React.Component<{}> {
+
+    @observable public static taskCompleted: boolean = false;
+    @observable public static popupX: number = 500;
+    @observable public static popupY: number = 150;
+    @observable public static textDisplayed: string = "Link Created";
+
+    @action
+    public static toggleTaskCompleted = () => {
+        TaskCompletionBox.taskCompleted = !TaskCompletionBox.taskCompleted;
+    }
+
+    render() {
+        return <Fade in={TaskCompletionBox.taskCompleted}>
+            <div className="taskCompletedBox-fade"
+                style={{
+                    left: TaskCompletionBox.popupX ? TaskCompletionBox.popupX : 500,
+                    top: TaskCompletionBox.popupY ? TaskCompletionBox.popupY : 150,
+                }}>{TaskCompletionBox.textDisplayed}</div>
+        </Fade>;
+    }
+} 
\ No newline at end of file
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 917a6853c..0c816461c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -19,9 +19,8 @@ import { DateField } from "./DateField";
 import { listSpec } from "./Schema";
 import { ComputedField } from "./ScriptField";
 import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
-import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl } from "./util";
+import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl, SharingPermissions } from "./util";
 import { LinkManager } from "../client/util/LinkManager";
-import { SharingPermissions } from "../client/util/SharingManager";
 import JSZip = require("jszip");
 import { saveAs } from "file-saver";
 
@@ -103,6 +102,7 @@ export const AclPrivate = Symbol("AclOwnerOnly");
 export const AclReadonly = Symbol("AclReadOnly");
 export const AclAddonly = Symbol("AclAddonly");
 export const AclEdit = Symbol("AclEdit");
+export const AclAdmin = Symbol("AclAdmin");
 export const UpdatingFromServer = Symbol("UpdatingFromServer");
 const CachedUpdates = Symbol("Cached updates");
 
@@ -110,7 +110,8 @@ const AclMap = new Map<string, symbol>([
     [SharingPermissions.None, AclPrivate],
     [SharingPermissions.View, AclReadonly],
     [SharingPermissions.Add, AclAddonly],
-    [SharingPermissions.Edit, AclEdit]
+    [SharingPermissions.Edit, AclEdit],
+    [SharingPermissions.Admin, AclAdmin]
 ]);
 
 export function fetchProto(doc: Doc) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index cd1e24826..eba398dfb 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,5 +1,5 @@
 import { UndoManager } from "../client/util/UndoManager";
-import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto, DataSym, DocListCast } from "./Doc";
+import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, fetchProto, DataSym, DocListCast, AclAdmin } from "./Doc";
 import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField, PrefetchProxy } from "./Proxy";
 import { RefField } from "./RefField";
@@ -9,7 +9,6 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
 import { DocServer } from "../client/DocServer";
 import { ComputedField } from "./ScriptField";
 import { ScriptCast, StrCast } from "./Types";
-import { SharingPermissions } from "../client/util/SharingManager";
 
 
 function _readOnlySetter(): never {
@@ -127,12 +126,20 @@ export function setGroups(groups: string[]) {
     currentUserGroups = groups;
 }
 
+export enum SharingPermissions {
+    Admin = "Admin",
+    Edit = "Can Edit",
+    Add = "Can Add",
+    View = "Can View",
+    None = "Not Shared"
+}
+
 export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol {
     if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclEdit;
 
     if (target[AclSym] && Object.keys(target[AclSym]).length) {
 
-        if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclEdit;
+        if (target.__fields?.author === Doc.CurrentUserEmail || target.author === Doc.CurrentUserEmail || currentUserGroups.includes("admin")) return AclAdmin;
 
         if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
 
@@ -143,7 +150,8 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
             [AclPrivate, 0],
             [AclReadonly, 1],
             [AclAddonly, 2],
-            [AclEdit, 3]
+            [AclEdit, 3],
+            [AclAdmin, 4]
         ]);
 
         for (const [key, value] of Object.entries(target[AclSym])) {
@@ -157,7 +165,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number)
         }
         return aclPresent ? effectiveAcl : AclEdit;
     }
-    return AclEdit;
+    return AclAdmin;
 }
 
 export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean) {
@@ -166,7 +174,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         ["Not Shared", 0],
         ["Can View", 1],
         ["Can Add", 2],
-        ["Can Edit", 3]
+        ["Can Edit", 3],
+        ["Admin", 4]
     ]);
 
     const dataDoc = target[DataSym];
@@ -206,11 +215,10 @@ const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHe
     "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
-    if (GetEffectiveAcl(target, in_prop) !== AclEdit) {
-        return true;
-    }
+    const effectiveAcl = GetEffectiveAcl(target, in_prop);
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true;
 
-    if (typeof prop === "string" && prop.startsWith("ACL") && ((target.author && Doc.CurrentUserEmail !== target.author) || !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value))) return true;
+    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;
 
     if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
-- 
cgit v1.2.3-70-g09d2

