From 62666034718378f4d970331665019b23659c1f3d Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 22 Mar 2023 17:01:17 -0400
Subject: visual changes to collaboration on doc and in PropertiesView

---
 src/client/views/DocumentDecorations.tsx | 64 +++++++++++++++++++++++---------
 1 file changed, 46 insertions(+), 18 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 2982f8a99..d87c13ce6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -1,6 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import { StylesProvider, Tooltip } from '@material-ui/core';
 import { IconButton } from 'browndash-components';
 import { action, computed, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -11,7 +11,7 @@ import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions } from '../../fields/util';
 import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -27,10 +27,11 @@ import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
-import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView';
+import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
+import { SharingManager } from '../util/SharingManager';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -283,12 +284,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
                     Doc.deiconifyView(openDoc);
                 }
-                selectedDocs[0].props.addDocTab(openDoc, OpenWhere.lightbox);
-                // LightboxView.SetLightboxDoc(
-                //     openDoc,
-                //     undefined,
-                //     selectedDocs.slice(1).map(view => view.props.Document)
-                // );
+                LightboxView.SetLightboxDoc(
+                    openDoc,
+                    undefined,
+                    selectedDocs.slice(1).map(view => view.props.Document)
+                );
             }
         }
         SelectionManager.DeselectAll();
@@ -735,6 +735,29 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             setTimeout(action(() => (this._showNothing = true)));
             return null;
         }
+
+        // sharing
+        const docShareMode = seldocview.rootDoc["acl-Public"];
+        const shareMode = StrCast(docShareMode);
+        var shareSymbolIcon = null;
+        switch(shareMode){
+            case ("Edit"):
+                shareSymbolIcon = "⬢ ";
+                break;
+            case ("Self-Edit"):
+                shareSymbolIcon = "⬢ ";
+                break;
+            case ("Augment"):
+                shareSymbolIcon = "⬟ ";
+                break;
+            case ("View"):
+                shareSymbolIcon = "♦ ";
+                break;
+            case ("Not-Shared"):
+                shareSymbolIcon = "▲ ";
+                break;
+        }
+
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
         const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating;
@@ -775,7 +798,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 undoBatch(e => click!(e))
                             ))
                     }>
-                    <FontAwesomeIcon icon={icon as any} />
+                    <FontAwesomeIcon icon={ icon as any} />
                 </div>
             </Tooltip>
         );
@@ -797,7 +820,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
 
         // Radius constants
-        const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
+        const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox;
         const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
         const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
@@ -820,15 +843,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         ) : (
             <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}>
                 <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
-                {!useLock ? null : (
-                    <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
-                        <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
-                            <FontAwesomeIcon size="sm" icon="lock" />
-                        </div>
-                    </Tooltip>
-                )}
             </div>
         );
+        const sharingMenu = docShareMode ? (
+            <div className={'documentDecorations-share'}>
+                <div className={`documentDecorations-share${shareMode}`}>
+                <span>{shareSymbolIcon + " " + shareMode}</span>
+                {/* <span>{shareMode}</span> */}
+                </div>
+            </div> ) : (<div/>
+        );
         return (
             <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}>
                 <div
@@ -862,6 +886,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
+                                {sharingMenu}
+                                {hideOpenButton ? <div /> :
+                                    seldocview.rootDoc._lockedPosition?  topBtn('lock', 'lock', this.onLockDown, undefined, 'Toggle ability to interact with document')
+                                                                    :   topBtn('lock', 'unlock', this.onLockDown, undefined, 'Toggle ability to interact with document')}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
-- 
cgit v1.2.3-70-g09d2


From 4265feca9e63cad6067055497ecabd354ead84f4 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 22 Mar 2023 17:45:32 -0400
Subject: collaboration icons

---
 src/client/views/DocumentDecorations.tsx |  3 +++
 src/client/views/PropertiesView.tsx      | 26 +++++++++++++-------------
 2 files changed, 16 insertions(+), 13 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d87c13ce6..b2b18b245 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -756,6 +756,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             case ("Not-Shared"):
                 shareSymbolIcon = "▲ ";
                 break;
+            default:
+                shareSymbolIcon = "";
+                break;
         }
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c34760d13..e58558aea 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -504,25 +504,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // users in common between all docs
         const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
-
         const tableEntries = [];
 
         // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
-        if (commonKeys.length) {
-            for (const key of commonKeys) {
-                const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
-                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
-                }
-            }
-        }
-
+        // if (commonKeys.length) {
+        //     for (const key of commonKeys) {
+        //         const name = denormalizeEmail(key.substring(4));
+        //         const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
+        //         if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
+        //             tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+        //         }
+        //     }
+        // }
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
         if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-
         // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
         // tableEntries.unshift(
         //     this.sharingItem(
@@ -532,7 +529,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         //         !ownerSame
         //     )
         // );
-        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, doc['acl-Public'], true)));
+
+        SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false)));
+
+        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true)));
 
         return (
             <div> Sharing Mode
-- 
cgit v1.2.3-70-g09d2


From d1e4d4a9daae4f037cf8b22e1011d97680a01e27 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Tue, 2 May 2023 17:44:00 -0400
Subject: update

properties view, document decorations, and sharing manager show + use correct acl
---
 package-lock.json                                  |   6 -
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/SharingManager.scss                |   8 +-
 src/client/util/SharingManager.tsx                 | 126 ++++++++++++++++++---
 src/client/views/DocumentDecorations.tsx           |  60 ++++++----
 src/client/views/PropertiesView.scss               |   7 +-
 src/client/views/PropertiesView.tsx                | 124 ++++++++++++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  18 +--
 src/fields/Doc.ts                                  |   7 +-
 src/fields/util.ts                                 |  47 ++++++--
 10 files changed, 292 insertions(+), 113 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/package-lock.json b/package-lock.json
index 42a7babb6..d74073e7f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22164,12 +22164,6 @@
             "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
             "dev": true
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3e89c8347..f5510d7e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -12,7 +12,7 @@ import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
 import { ComputedField, ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
 import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
-import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { Upload } from '../../server/SharedMediaTypes';
 import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
 import { YoutubeBox } from '../apis/youtube/YoutubeBox';
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 932e94664..f47c48805 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -6,7 +6,7 @@
         transform: translate(-20px, -20px);
     }
 
-    select {
+    .select {
         text-align: justify;
         text-align-last: end
     }
@@ -212,9 +212,9 @@
             cursor: pointer;
         }
 
-        &:hover .padding {
-            white-space: unset;
-        }
+        // &:hover .padding {
+        //     white-space: unset;
+        // }
 
         .padding {
             padding: 0 10px 0 0;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3c05af4bb..5e61f6d3c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -12,6 +12,7 @@ import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { CollectionView } from '../views/collections/CollectionView';
 import { DictationOverlay } from '../views/DictationOverlay';
 import { MainViewModal } from '../views/MainViewModal';
@@ -157,21 +158,42 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
+        
+        // var docs: Doc[] = [];
+        // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+        // const dashboard = dashboardList[0]
+        // docs.push(dashboard!)
+        // const tabs = DocListCast(dashboard?.data)
+        // tabs.forEach(tab => {
+        //     docs.push(tab)
+        //     var newDocs = DocListCast(tab.data)
+        //     newDocs.forEach(newDoc => 
+        //         docs.push(newDoc))
+        // })
+
+        // if (!docs){
+        //     return null;
+        // }
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+
+        //var docs: Doc[] = [];
+        docs.push(targetDoc!)
+
+        // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
+                
                 if (permission === SharingPermissions.None) {
                     if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
+                doc = targetDoc
 
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
                 else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
@@ -190,8 +212,18 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
+        var docs: Doc[] = [];
+        const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+        const dashboard = dashboardList[0]
+        const tabs = DocListCast(dashboard?.data)
+        tabs.forEach(tab => {
+            var newDocs = DocListCast(tab.data)
+            newDocs.forEach(newDoc => docs.push(newDoc))
+        })
+        
+        if (!docs){
+            return null;
+        }
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -334,13 +366,20 @@ export class SharingManager extends React.Component<{}> {
     //     targetDoc["acl-" + PublicKey] = permission;
     // }
 
-    // private get sharingUrl() {
-    //     if (!this.targetDoc) {
-    //         return undefined;
-    //     }
-    //     const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
-    //     return `${baseUrl}?sharing=true`;
-    // }
+    private get sharingUrl() {
+        if (!this.targetDoc) {
+            return undefined;
+        }
+        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+        return `${baseUrl}?sharing=true`;
+    }
+
+    /**
+     * Copies the Public sharing url to the user's clipboard.
+     */
+    private copyURL = (e: any) => {
+        Utils.CopyText(this.sharingUrl!)
+    }
 
     // copy = action(() => {
     //     if (this.sharingUrl) {
@@ -357,7 +396,7 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift('-multiple-');
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues
-            .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any))
+            .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
             .map(permission => (
                 <option key={permission} value={permission}>
                     {permission}
@@ -422,6 +461,7 @@ export class SharingManager extends React.Component<{}> {
             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);
                 }
@@ -466,6 +506,7 @@ export class SharingManager extends React.Component<{}> {
         if (!this.targetDoc) return null;
         TraceMobx();
         const groupList = GroupManager.Instance?.allGroups || [];
+
         const sortedUsers = this.users
             .slice()
             .sort(this.sortUsers)
@@ -505,13 +546,48 @@ export class SharingManager extends React.Component<{}> {
         // users in common between all docs
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
 
+        // including all users on dashboard
+        // SharingManager.Instance.users.forEach(eachUser => {
+        //     var userOnDashboard = true;
+        //     if(Doc.ActiveDashboard){
+        //         if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+        //             userOnDashboard = false;
+        //         }
+        //     }
+        //     if (userOnDashboard && !commonKeys.includes(`acl-${normalizeEmail(eachUser.user.email)}`)){
+        //         users.push(eachUser.user);
+        //     }
+        // });
+
+
         // the list of users shared with
         const userListContents: (JSX.Element | null)[] = users
-            .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            .filter(({ user }) => (docs[0]?.author !== user.email))
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+                const dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+                // const dashboard = dashboardList[0]
+                const dashboard = Doc.ActiveDashboard
+                var docToUse = dashboard
+
+                // const tabs = DocListCast(dashboard?.data)
+                // tabs.forEach(tab => {
+                //     if (tab.title == targetDoc.title){
+                //         docToUse = tab
+                //     }
+                //     var newdocsList = DocListCast(tab.data)
+                //     newdocsList.forEach(newDoc => {
+                //         if (newDoc.title == targetDoc.title){
+                //             docToUse = newDoc
+                //         }
+                //     })
+                // })
+
+                docToUse = Doc.GetProto(this.targetDoc!)
+
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
-                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                const permissions = uniform ? StrCast(docToUse?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -551,7 +627,7 @@ export class SharingManager extends React.Component<{}> {
                 </div>
             ) : null
         );
-
+    
         // the list of groups shared with
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
@@ -560,11 +636,27 @@ export class SharingManager extends React.Component<{}> {
             const uniform = docs
                 .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
                 .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+            const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
-                    <div className={'padding'}>{StrCast(group.title)}</div>
+                    <div className={'padding'} >{StrCast(group.title)}</div>
+                    <div>
+                        {StrCast(group.title)==='Public' ? (
+                            <div title={"Copy Public URL"} onClick={this.copyURL}>
+                                {/* <IconButton
+                                    size={Size.SMALL}
+                                    isCircle={true}
+                                    hoverStyle="gray"
+                                    onClick={() => this.copyURL}
+                                    icon={<FontAwesomeIcon icon="copy" />}
+                                /> */}
+                                <FontAwesomeIcon icon={'copy'} size={'sm'} />
+                            </div>
+                        ) : (
+                            <div/>
+                        )}
+                    </div>
                     {group instanceof Doc ? (
                         <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
                             <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b2b18b245..3e0a1f7a4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,12 +6,12 @@ import { action, computed, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
 import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
 import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -157,27 +157,35 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     @action onContainerDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.onBackgroundMove(true, e),
-            e => {},
-            emptyFunction
-        );
+        const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+            setupMoveUpEvents(
+                this,
+                e,
+                e => this.onBackgroundMove(true, e),
+                e => {},
+                emptyFunction
+            );
+        }
     };
 
     @action onTitleDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(
-            this,
-            e,
-            e => this.onBackgroundMove(true, e),
-            e => {},
-            action(e => {
-                !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
-                this._editingTitle = true;
-                this._keyinput.current && setTimeout(this._keyinput.current.focus);
-            })
-        );
+        const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+            setupMoveUpEvents(
+                this,
+                e,
+                e => this.onBackgroundMove(true, e),
+                e => {},
+                action(e => {
+                    !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
+                    this._editingTitle = true;
+                    this._keyinput.current && setTimeout(this._keyinput.current.focus);
+                })
+            );
+        }
     };
 
     onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
@@ -485,6 +493,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+        if (!(effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment)) return false;
         if (!first) return false;
         let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
         var fixedAspect = Doc.NativeAspect(first.layoutDoc);
@@ -737,7 +747,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const docShareMode = seldocview.rootDoc["acl-Public"];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)["acl-Public"];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch(shareMode){
@@ -781,10 +791,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                // const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+                // return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                const effectiveAcl = GetEffectiveAcl(Doc.GetProto(seldocview.rootDoc))
+                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
-
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
                 <div
@@ -852,7 +863,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             <div className={'documentDecorations-share'}>
                 <div className={`documentDecorations-share${shareMode}`}>
                 <span>{shareSymbolIcon + " " + shareMode}</span>
-                {/* <span>{shareMode}</span> */}
                 </div>
             </div> ) : (<div/>
         );
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 3edc7fea8..50bf6ce74 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -384,17 +384,14 @@
         padding: 5px; // remove when adding buttons
         border-radius: 6px; // remove when adding buttons
         margin-right: 10px; // remove when adding buttons
-        // width: 100%;
-        // display: inline-table;
         background-color: #ececec;
         max-height: 130px;
         width: 92%;
 
         .propertiesView-sharingTable-item {
             display: flex;
-            // padding: 5px;
             padding: 3px;
-            align-items: center;
+            align-items: right;
             border-bottom: 0.5px solid grey;
 
             &:hover .propertiesView-sharingTable-item-name {
@@ -429,7 +426,7 @@
         background: inherit;
         border: none;
         background: inherit;
-        width: 87px;
+        // width: 100%;
         text-align: justify; // for Edge
         text-align-last: end;
         &:hover {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index dfc43e6c8..a6a5347ad 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { denormalizeEmail, GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
@@ -34,6 +34,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { SourceMapDevToolPlugin } from 'webpack';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -366,11 +367,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
+        const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
         if (user=="Public"){
-            this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
         }
         else{
-            const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
+
             SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
         }
     };
@@ -381,15 +383,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     getPermissionsSelect(user: string, permission: string) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (permission === '-multiple-') dropdownValues.unshift(permission);
-        if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+        if (user !== 'Override') {
+            dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+            // dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+        }
         return (
             <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
                 {dropdownValues
-                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+                    .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {' '}
-                            {permission}{' '}
+                            {' '}{permission}{' '}
                         </option>
                     ))}
             </select>
@@ -432,6 +436,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns a row of the permissions panel
      */
     sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
+        if (name==Doc.CurrentUserEmail){
+            name = 'Me';
+        }
         return (
             <div
                 className="propertiesView-sharingTable-item"
@@ -445,7 +452,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
                     {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
-                    {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                    {(permission === 'Owner' && name=='Me')|| showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
         );
@@ -470,6 +477,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 dropDownText = "▲ ";
                 break;
         }
+
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
@@ -490,7 +498,24 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         // const docToUse = this.selectedDocumentView?.rootDoc;
+
+        // const dashboard = Doc.ActiveDashboard
+        // var docToUse = dashboard
+        // const tabs = DocListCast(dashboard?.data)
+        // tabs.forEach(tab => {
+        //     if (tab.title == this.selectedDoc?.title){
+        //         docToUse = tab
+        //     }
+        //     var newdocsList = DocListCast(tab.data)
+        //     newdocsList.forEach(newDoc => {
+        //         if (newDoc.title == this.selectedDoc?.title){
+        //             docToUse = newDoc
+        //         }
+        //     })
+        // })
+
         const docToUse = this.selectedDoc;
+
         if (!docToUse){
             return null;
         }
@@ -501,44 +526,69 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const target = docs[0];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
-        const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+        // const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
+        // const effectiveAcls = GetEffectiveAcl(docToUse)
+        const effectiveAcls = GetEffectiveAcl(target)
+
+        // const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+        const showAdmin= effectiveAcls==AclAdmin || docToUse!['acl-'+normalizeEmail(Doc.CurrentUserEmail)]=='Owner';
 
         // users in common between all docs
         const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
         const tableEntries = [];
+        const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
+        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
+
+        usersAdded.push(target.author);
+        SharingManager.Instance.users.forEach(eachUser => {
+            var userOnDashboard = true;
+            var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`])
+            if(Doc.ActiveDashboard){
+                if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+                    userOnDashboard = false;
+                }
+            }
+            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public'){
+                tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
+                usersAdded.push(eachUser.user.email);
+            }
+        });
 
-        // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
-        // if (commonKeys.length) {
-        //     for (const key of commonKeys) {
-        //         const name = denormalizeEmail(key.substring(4));
-        //         const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
-        //         if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
-        //             tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+        // commonKeys.forEach(user => {
+        //     const userEmail = user.slice(4)
+        //     var userOnDashboard = true;
+        //     if(Doc.ActiveDashboard){
+        //         if(Doc.ActiveDashboard[user]=='' || Doc.ActiveDashboard[user]==undefined){
+        //             userOnDashboard = false;
         //         }
         //     }
-        // }
-        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
-        // shifts the current user, owner, public to the top of the doc.
-        // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
-        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
-        // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-'));
-        // tableEntries.unshift(
-        //     this.sharingItem(
-        //         'Me',
-        //         showAdmin,
-        //         docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
-        //         !ownerSame
-        //     )
-        // );
-
-        SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false)));
-
-        docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true)));
+        //     if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
+        //         tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(docToUse![`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+        //         usersAdded.push(denormalizeEmail(userEmail));
+        //     }
+        // })
+
+        // current user
+        const userEmail = Doc.CurrentUserEmail
+        var userOnDashboard = true;
+        if(Doc.ActiveDashboard){
+            if(Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]==undefined){
+                userOnDashboard = false;
+            }
+        }
+        if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
+            tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+            usersAdded.push(denormalizeEmail(userEmail));
+        }
+
+        // if (ownerSame ) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
+        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
 
         return (
-            <div> Sharing Mode
-                <div>{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}</div>
+            <div > Sharing Mode
+                <div>{
+                    this.publicACLDropDown(true, StrCast(target['acl-Public']), false)}
+                </div>
             <div>  <br></br> Who has access to the Dashboard? </div>
             <div className="propertiesView-sharingTable">{
                 <div> {tableEntries}</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a2c1195cb..0e4330fa5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -19,7 +19,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
+import { GetEffectiveAcl, normalizeEmail, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from '../../../DocServer';
@@ -295,11 +295,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
-            const effectiveAcl = GetEffectiveAcl(dataDoc);
-
+            // const effectiveAcl = GetEffectiveAcl(dataDoc);
+            const effectiveAcl = GetEffectiveAcl(this.rootDoc);
+            
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
-
-            if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
+            
+            if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -1689,7 +1690,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation();
         for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
             }
         }
@@ -1708,7 +1709,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             default:
                 if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case ' ':
-                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) &&
+                if (e.code == "Space"){
+                    break;
+                }
+                [AclEdit, AclAugment, AclSelfEdit, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
                     this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4d82551db..5e2e40979 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -135,7 +135,6 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
 // this recursively updates all protos as well.
 export function updateCachedAcls(doc: Doc) {
     if (!doc) return;
-
     const target = (doc as any)?.__fields ?? doc;
     const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
     Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
@@ -1117,7 +1116,7 @@ export namespace Doc {
                 target[targetKey] = new PrefetchProxy(templateDoc);
             } else {
                 titleTarget && (Doc.GetProto(target).title = titleTarget);
-                const setDoc = [AclAdmin, AclEdit].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
+                const setDoc = [AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
                 setDoc[targetKey] = new PrefetchProxy(templateDoc);
             }
         }
@@ -1295,7 +1294,9 @@ export namespace Doc {
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) {
+            return DocBrushStatus.unbrushed;
+        }
         const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index eca4d1351..c1e1a7111 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -9,9 +9,11 @@ import { returnZero } from '../Utils';
 import CursorField from './CursorField';
 import {
     AclAdmin,
+    AclAugment,
     AclEdit,
     aclLevel,
     AclPrivate,
+    AclReadonly,
     AclSelfEdit,
     AclSym,
     DataSym,
@@ -37,6 +39,8 @@ import { RichTextField } from './RichTextField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { SharingManager } from '../client/util/SharingManager';
+import { PropertiesView } from '../client/views/PropertiesView';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -82,8 +86,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     const fromServer = target[UpdatingFromServer];
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
-    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -139,13 +143,13 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
-    return;
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
         const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
         key.startsWith('acl') && distributeAcls(key, permission, child);
     }
+    return;
 }
 
 /**
@@ -182,6 +186,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
+    target = Doc.GetProto(target)
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
     return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
@@ -205,8 +210,24 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[AclSym];
-    if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
-
+    if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName(SharingPermissions.Admin)) return AclAdmin;
+    if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]){
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Admin){
+            return AclAdmin
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Edit){
+            return AclEdit
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Augment){
+            return AclAugment
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.View){
+            return AclReadonly
+        }
+        if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.None){
+            return AclPrivate
+        }
+    }
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
     if (targetAcls && Object.keys(targetAcls).length) {
         let effectiveAcl = AclPrivate;
@@ -225,13 +246,22 @@ function getEffectiveAcl(target: any, user?: string): symbol {
         //const override = targetAcls['acl-Override'];
         // if (override !== AclUnset && override !== undefined) effectiveAcl = override;
 
-        // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl)
+
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
     }
     // authored documents are private until an ACL is set.
     const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
+    let acl = AclPrivate
+    if (user){
+        acl = target['acl-'+user]
+    }
+    else{
+        acl = target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]         
+    }
+    console.log(target['acl-'+normalizeEmail(Doc.CurrentUserEmail)])
+    return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(acl)!.level < aclLevel.editable ? AclEdit : acl;
 }
 /**
  * Recursively distributes the access right for a user across the children of a document and its annotations.
@@ -247,9 +277,11 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
     if ((target._viewType === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
         target[key] = acl;
+        Doc.GetProto(target)[key] = acl
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
+
         }
         return;
     }
@@ -260,7 +292,6 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
         target[key] = acl;
         layoutDocChanged = true;
-
         if (isDashboard) {
             DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => {
                 docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited));
@@ -299,7 +330,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
     const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop);
-    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) 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;
 
-- 
cgit v1.2.3-70-g09d2


From 50876028b994044f2a9426a4efb85a363f0d7168 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 4 May 2023 10:46:20 -0400
Subject: properties view + sharing manager are consistent, and all text
 updates properly

---
 src/client/documents/Documents.ts                  |   2 +-
 src/client/util/SharingManager.scss                |   5 +
 src/client/util/SharingManager.tsx                 | 120 ++++-----------------
 src/client/views/DocComponent.tsx                  |  10 +-
 src/client/views/DocumentDecorations.scss          |   3 +-
 src/client/views/DocumentDecorations.tsx           |   6 +-
 src/client/views/Main.tsx                          |   2 +-
 src/client/views/MarqueeAnnotator.tsx              |   4 +-
 src/client/views/PropertiesView.scss               |   4 +-
 src/client/views/PropertiesView.tsx                |  93 ++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |   9 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |  12 +--
 src/fields/Doc.ts                                  |   3 -
 src/fields/util.ts                                 |   6 +-
 14 files changed, 81 insertions(+), 198 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f5510d7e1..9c67f1f37 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -506,7 +506,7 @@ export namespace Docs {
                         description: '',
                         showCaption: 'description',
                         backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
-                        _removeDropProperties: new List(['isLinkButton']),
+                        // _removeDropsties: new List(['isLinkButton']),
                     },
                 },
             ],
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index f47c48805..3cab77816 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -23,6 +23,11 @@
             z-index: 999;
         }
 
+        .share-title {
+            display: inline-flex;
+            gap: 5px;
+        }
+
         .share-container {
             .share-setup {
                 display: flex;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 5e61f6d3c..40a282db0 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -25,6 +25,7 @@ import { GroupMemberView } from './GroupMemberView';
 import { LinkManager } from './LinkManager';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
+import { IconButton, Size } from 'browndash-components';
 
 export interface User {
     email: string;
@@ -159,27 +160,14 @@ export class SharingManager extends React.Component<{}> {
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
-        // var docs: Doc[] = [];
-        // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-        // const dashboard = dashboardList[0]
-        // docs.push(dashboard!)
-        // const tabs = DocListCast(dashboard?.data)
-        // tabs.forEach(tab => {
-        //     docs.push(tab)
-        //     var newDocs = DocListCast(tab.data)
-        //     newDocs.forEach(newDoc => 
-        //         docs.push(newDoc))
-        // })
-
-        // if (!docs){
-        //     return null;
-        // }
+        // setting the same acl for a docs within the doc being shared
+        var childDocs = DocListCast(target.data)
+        childDocs.map(doc => {
+            this.setInternalSharing(recipient, permission, doc)
+        })
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
-        //var docs: Doc[] = [];
-        docs.push(targetDoc!)
-
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -191,7 +179,6 @@ export class SharingManager extends React.Component<{}> {
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
-                doc = targetDoc
 
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
@@ -211,19 +198,15 @@ export class SharingManager extends React.Component<{}> {
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        var docs: Doc[] = [];
-        const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
-        const dashboard = dashboardList[0]
-        const tabs = DocListCast(dashboard?.data)
-        tabs.forEach(tab => {
-            var newDocs = DocListCast(tab.data)
-            newDocs.forEach(newDoc => docs.push(newDoc))
-        })
         
-        if (!docs){
-            return null;
-        }
+        // setting the same acl for a docs within the doc being shared
+        var childDocs = DocListCast(target.data)
+        childDocs.map(doc => {
+            this.setInternalGroupSharing(group, permission, doc)
+        })
+
+        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -292,6 +275,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
+                this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data))
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
@@ -364,30 +348,15 @@ export class SharingManager extends React.Component<{}> {
     //         return;
     //     }
     //     targetDoc["acl-" + PublicKey] = permission;
-    // }
-
-    private get sharingUrl() {
-        if (!this.targetDoc) {
-            return undefined;
-        }
-        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
-        return `${baseUrl}?sharing=true`;
-    }
+    // }s
 
     /**
      * Copies the Public sharing url to the user's clipboard.
      */
     private copyURL = (e: any) => {
-        Utils.CopyText(this.sharingUrl!)
+        Utils.CopyText(Utils.shareUrl(this.targetDoc![Id]))
     }
 
-    // copy = action(() => {
-    //     if (this.sharingUrl) {
-    //         Utils.CopyText(this.sharingUrl);
-    //         this.copied = true;
-    //     }
-    // });
-
     /**
      * Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share
      */
@@ -396,7 +365,6 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift('-multiple-');
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues
-            .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
             .map(permission => (
                 <option key={permission} value={permission}>
                     {permission}
@@ -546,19 +514,6 @@ export class SharingManager extends React.Component<{}> {
         // users in common between all docs
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
 
-        // including all users on dashboard
-        // SharingManager.Instance.users.forEach(eachUser => {
-        //     var userOnDashboard = true;
-        //     if(Doc.ActiveDashboard){
-        //         if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
-        //             userOnDashboard = false;
-        //         }
-        //     }
-        //     if (userOnDashboard && !commonKeys.includes(`acl-${normalizeEmail(eachUser.user.email)}`)){
-        //         users.push(eachUser.user);
-        //     }
-        // });
-
 
         // the list of users shared with
         const userListContents: (JSX.Element | null)[] = users
@@ -570,19 +525,6 @@ export class SharingManager extends React.Component<{}> {
                 const dashboard = Doc.ActiveDashboard
                 var docToUse = dashboard
 
-                // const tabs = DocListCast(dashboard?.data)
-                // tabs.forEach(tab => {
-                //     if (tab.title == targetDoc.title){
-                //         docToUse = tab
-                //     }
-                //     var newdocsList = DocListCast(tab.data)
-                //     newdocsList.forEach(newDoc => {
-                //         if (newDoc.title == targetDoc.title){
-                //             docToUse = newDoc
-                //         }
-                //     })
-                // })
-
                 docToUse = Doc.GetProto(this.targetDoc!)
 
                 const userKey = `acl-${normalizeEmail(user.email)}`;
@@ -641,22 +583,6 @@ export class SharingManager extends React.Component<{}> {
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
                     <div className={'padding'} >{StrCast(group.title)}</div>
-                    <div>
-                        {StrCast(group.title)==='Public' ? (
-                            <div title={"Copy Public URL"} onClick={this.copyURL}>
-                                {/* <IconButton
-                                    size={Size.SMALL}
-                                    isCircle={true}
-                                    hoverStyle="gray"
-                                    onClick={() => this.copyURL}
-                                    icon={<FontAwesomeIcon icon="copy" />}
-                                /> */}
-                                <FontAwesomeIcon icon={'copy'} size={'sm'} />
-                            </div>
-                        ) : (
-                            <div/>
-                        )}
-                    </div>
                     {group instanceof Doc ? (
                         <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
                             <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
@@ -679,19 +605,16 @@ export class SharingManager extends React.Component<{}> {
             <div className={'sharing-interface'}>
                 {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
                 <div className="sharing-contents">
-                    <p className={'share-title'}>
+                    <p className="share-title">
                         <b>Share </b>
                         {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
+                        {/* <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/> */}
+                        <IconButton size={Size.SMALL} isCircle={true} tooltip='Copy Public URL' hoverStyle="gray" onClick={this.copyURL}icon={<FontAwesomeIcon icon="copy" />}/>
                     </p>
                     <div className={'close-button'} onClick={this.close}>
                         <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
-                    {/* {this.linkVisible ?
-                        <div>
-                            {this.sharingUrl}
-                        </div> :
-                        (null)} */}
-                    {
+                    { admin ? 
                         <div className="share-container">
                             <div className="share-setup">
                                 <Select
@@ -730,6 +653,7 @@ export class SharingManager extends React.Component<{}> {
                                 )}
                             </div>
                         </div>
+                        : <br></br>
                     }
                     <div className="main-container">
                         <div className={'individual-container'}>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 0b92fd864..5a1dae8db 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -222,8 +222,12 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                             doc.context = this.props.Document;
+                            const contextDoc = Cast(doc.context, Doc, null)
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) { 
+                                if (contextDoc) inheritParentAcls(contextDoc, doc);
+                                else if (Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
+                            };
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
@@ -237,7 +241,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                                 doc.context = this.props.Document;
                                 if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                                Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
+                                const contextDoc = Cast(doc.context, Doc, null)
+                                if (contextDoc) inheritParentAcls(contextDoc, doc);
+                                else if (Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                             });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ce5378e69..32bcc872a 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -237,8 +237,7 @@ $resizeHandler: 8px;
             color: rgb(71, 71, 71);
             border-color: rgb(71, 71, 71);
         }
-        .documentDecorations-shareEdit,
-        .documentDecorations-shareSelf-Edit{
+        .documentDecorations-shareEdit{
             width: calc(100% + 10px);
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3e0a1f7a4..b769056d8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -32,6 +32,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import { SharingManager } from '../util/SharingManager';
+import { DocServer } from '../DocServer';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -754,9 +755,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             case ("Edit"):
                 shareSymbolIcon = "⬢ ";
                 break;
-            case ("Self-Edit"):
-                shareSymbolIcon = "⬢ ";
-                break;
             case ("Augment"):
                 shareSymbolIcon = "⬟ ";
                 break;
@@ -794,7 +792,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 // const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
                 // return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
                 const effectiveAcl = GetEffectiveAcl(Doc.GetProto(seldocview.rootDoc))
-                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 6b18caed0..7da9dc603 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -26,7 +26,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0 }; // bcz: not sure
     root.render(<FieldLoader />);
     window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
-    if (info.email === 'guest') DocServer.Control.makeReadOnly();
+    // if (info.email === 'guest') DocServer.Control.makeReadOnly();
     await CurrentUserUtils.loadUserDocument(info.id);
     setTimeout(() => {
         document.getElementById('root')!.addEventListener(
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 30867a774..094c9cc61 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,6 +1,6 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
@@ -202,7 +202,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
-        const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
+        const annotationDoc = [AclAugment, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return (annotationDoc as Doc) ?? undefined;
     };
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 50bf6ce74..71107a5f9 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -172,8 +172,7 @@
             border-radius: 6px;
             border: 1px solid rgb(71, 71, 71);
         }
-        & .propertiesView-shareDropDownEdit,
-        .propertiesView-shareDropDownSelf-Edit{
+        & .propertiesView-shareDropDownEdit{
             padding: 5px;
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
@@ -426,7 +425,6 @@
         background: inherit;
         border: none;
         background: inherit;
-        // width: 100%;
         text-align: justify; // for Edge
         text-align-last: end;
         &:hover {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a6a5347ad..984c2dc04 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -369,7 +369,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     changePermissions = (e: any, user: string) => {
         const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
         if (user=="Public"){
-            docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            // docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
+            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
         }
         else{
 
@@ -385,12 +386,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (permission === '-multiple-') dropdownValues.unshift(permission);
         if (user !== 'Override') {
             dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
-            // dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         }
         return (
             <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
                 {dropdownValues
-                    .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)))
+                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
                             {' '}{permission}{' '}
@@ -464,9 +464,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             case ("Edit"):
                 dropDownText = "⬢ ";
                 break;
-            case ("Self-Edit"):
-                dropDownText = "⬢ ";
-                break;
             case ("Augment"):
                 dropDownText = "⬟ ";
                 break;
@@ -493,53 +490,36 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    /**
+     * Sorting algorithm to sort users.
+     */
+    sortUsers = (u1: String, u2: String) => {
+        return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
+    };
+
     /**
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
-        // const docToUse = this.selectedDocumentView?.rootDoc;
-
-        // const dashboard = Doc.ActiveDashboard
-        // var docToUse = dashboard
-        // const tabs = DocListCast(dashboard?.data)
-        // tabs.forEach(tab => {
-        //     if (tab.title == this.selectedDoc?.title){
-        //         docToUse = tab
-        //     }
-        //     var newdocsList = DocListCast(tab.data)
-        //     newdocsList.forEach(newDoc => {
-        //         if (newDoc.title == this.selectedDoc?.title){
-        //             docToUse = newDoc
-        //         }
-        //     })
-        // })
 
         const docToUse = this.selectedDoc;
-
         if (!docToUse){
             return null;
         }
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? docToUse : docToUse?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
-
         const target = docs[0];
 
-        // tslint:disable-next-line: no-unnecessary-callback-wrapper
-        // const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
-        // const effectiveAcls = GetEffectiveAcl(docToUse)
         const effectiveAcls = GetEffectiveAcl(target)
-
-        // const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
         const showAdmin= effectiveAcls==AclAdmin || docToUse!['acl-'+normalizeEmail(Doc.CurrentUserEmail)]=='Owner';
 
         // users in common between all docs
-        const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
+        // const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
+        // const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
-        const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
 
-        usersAdded.push(target.author);
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDashboard = true;
             var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`])
@@ -548,46 +528,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     userOnDashboard = false;
                 }
             }
-            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public'){
-                tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
+            if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public' && eachUser.user.email!=target.author){
+                // tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
                 usersAdded.push(eachUser.user.email);
             }
         });
-
-        // commonKeys.forEach(user => {
-        //     const userEmail = user.slice(4)
-        //     var userOnDashboard = true;
-        //     if(Doc.ActiveDashboard){
-        //         if(Doc.ActiveDashboard[user]=='' || Doc.ActiveDashboard[user]==undefined){
-        //             userOnDashboard = false;
-        //         }
-        //     }
-        //     if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
-        //         tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(docToUse![`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
-        //         usersAdded.push(denormalizeEmail(userEmail));
-        //     }
-        // })
-
-        // current user
-        const userEmail = Doc.CurrentUserEmail
-        var userOnDashboard = true;
-        if(Doc.ActiveDashboard){
-            if(Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]==undefined){
-                userOnDashboard = false;
-            }
-        }
-        if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
-            tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
-            usersAdded.push(denormalizeEmail(userEmail));
+        
+        usersAdded.sort(this.sortUsers)
+        usersAdded.map(userEmail => {
+            const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`])
+            tableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
+        })
+
+        // add current user
+        var userEmail = Doc.CurrentUserEmail
+        if (userEmail == 'guest') userEmail = 'Public';
+        if (!usersAdded.includes(userEmail) && userEmail!='Public' && userEmail!=target.author){
+            tableEntries.unshift(this.sharingItem(userEmail, showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+            usersAdded.push(userEmail);
         }
 
-        // if (ownerSame ) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
-        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
+        // shift owner to top
+        tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
 
         return (
             <div > Sharing Mode
                 <div>{
-                    this.publicACLDropDown(true, StrCast(target['acl-Public']), false)}
+                    this.publicACLDropDown(showAdmin, StrCast(target['acl-Public']), false)}
                 </div>
             <div>  <br></br> Who has access to the Dashboard? </div>
             <div className="propertiesView-sharingTable">{
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0e4330fa5..c073c7468 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,7 +11,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
@@ -295,12 +295,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
             const json = JSON.stringify(state.toJSON());
-            // const effectiveAcl = GetEffectiveAcl(dataDoc);
             const effectiveAcl = GetEffectiveAcl(this.rootDoc);
             
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
             
-            if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
+            if ([AclEdit, AclAdmin, AclAugment].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -1690,7 +1689,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation();
         for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) ) {
                 e.preventDefault();
             }
         }
@@ -1712,7 +1711,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (e.code == "Space"){
                     break;
                 }
-                [AclEdit, AclAugment, AclSelfEdit, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
+                [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
                     this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 68b0488a2..64cc7addc 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { Schema } from 'prosemirror-model';
 import { splitListItem, wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
-import { AclAugment, AclSelfEdit, Doc } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/Doc';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
@@ -48,14 +48,6 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         switch (GetEffectiveAcl(props.DataDoc)) {
             case AclAugment:
                 return false;
-            case AclSelfEdit:
-                for (var i = state.selection.from; i < state.selection.to; i++) {
-                    const marks = state.doc.resolve(i)?.marks?.();
-                    if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) {
-                        return false;
-                    }
-                }
-                break;
         }
         return true;
     };
@@ -259,7 +251,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        if (!canEdit(state)) return true;
+        if (GetEffectiveAcl(props.DataDoc)!=AclEdit && GetEffectiveAcl(props.DataDoc)!=AclAugment && GetEffectiveAcl(props.DataDoc)!=AclAdmin) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 5e2e40979..68279372a 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -102,7 +102,6 @@ export const AclUnset = Symbol('AclUnset');
 export const AclPrivate = Symbol('AclOwnerOnly');
 export const AclReadonly = Symbol('AclReadOnly');
 export const AclAugment = Symbol('AclAugment');
-export const AclSelfEdit = Symbol('AclSelfEdit');
 export const AclEdit = Symbol('AclEdit');
 export const AclAdmin = Symbol('AclAdmin');
 export const UpdatingFromServer = Symbol('UpdatingFromServer');
@@ -115,7 +114,6 @@ export enum aclLevel {
     unshared = 0,
     viewable = 1,
     augmentable = 2,
-    selfEditable = 2.5,
     editable = 3,
     admin = 4,
 }
@@ -124,7 +122,6 @@ export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermis
     [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None }],
     [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View }],
     [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment}],
-    [AclSelfEdit, { level: aclLevel.selfEditable, name: SharingPermissions.SelfEdit }],
     [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit }],
     [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin }],
     [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset }],
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c1e1a7111..b0ad4db5d 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -14,7 +14,6 @@ import {
     aclLevel,
     AclPrivate,
     AclReadonly,
-    AclSelfEdit,
     AclSym,
     DataSym,
     Doc,
@@ -87,7 +86,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const fromServer = target[UpdatingFromServer];
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
     const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin ) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -171,7 +170,6 @@ export enum SharingPermissions {
     Unset = 'None',
     Admin = 'Admin',
     Edit = 'Edit',
-    SelfEdit = 'Self-Edit',
     Augment = 'Augment',
     View = 'View',
     None = 'Not-Shared',
@@ -330,7 +328,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
     const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop);
-    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && 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;
 
-- 
cgit v1.2.3-70-g09d2


From 20d217d825891cf29a432a048d1f8e7bc04d062a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 14 Jun 2023 15:02:39 -0400
Subject: PropertiesView changes - colors on all permissions and fixed bug with
 when the drop down arrow is displayed

---
 src/client/util/SharingManager.tsx        |  1 -
 src/client/views/DocumentDecorations.scss |  6 +++---
 src/client/views/DocumentDecorations.tsx  |  9 +++++---
 src/client/views/PropertiesView.scss      | 35 +++++++++++++++++++------------
 src/client/views/PropertiesView.tsx       | 30 ++++++++++++++------------
 src/fields/util.ts                        |  1 +
 6 files changed, 49 insertions(+), 33 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d7cc352b7..d9ab0cb5f 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -179,7 +179,6 @@ export class SharingManager extends React.Component<{}> {
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
-
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 32bcc872a..a5ebe9d58 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -230,14 +230,14 @@ $resizeHandler: 8px;
         &:hover {
             opacity: 1;
         }
-        .documentDecorations-shareNone,
-        .documentDecorations-shareAdmin{
+        .documentDecorations-shareNone{
             width: calc(100% + 10px);
             background: grey;
             color: rgb(71, 71, 71);
             border-color: rgb(71, 71, 71);
         }
-        .documentDecorations-shareEdit{
+        .documentDecorations-shareEdit,
+        .documentDecorations-shareAdmin{
             width: calc(100% + 10px);
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9aca8c339..a6b2db8f3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -11,7 +11,7 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl } from '../../fields/util';
+import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
 import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -650,7 +650,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     } else {
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
-
                         const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
@@ -756,9 +755,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // sharing
         const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
+        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
+            case 'Admin':
+                shareSymbolIcon = '⬢ ';
+                break;
             case 'Edit':
                 shareSymbolIcon = '⬢ ';
                 break;
@@ -799,7 +802,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 // const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
                 // return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
                 const effectiveAcl = GetEffectiveAcl(Doc.GetProto(seldocview.rootDoc));
-                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && GetEffectiveAcl(docView.rootDoc) !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclEdit);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 71107a5f9..3cf7f2546 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -164,23 +164,29 @@
         margin-right: 10px;
         width: 92%;
 
-        & .propertiesView-shareDropDownNone,
-        .propertiesView-shareDropDownAdmin{
-            padding: 5px;
+        & .propertiesView-shareDropDownNone{
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: grey;
             color: rgb(71, 71, 71);
             border-radius: 6px;
             border: 1px solid rgb(71, 71, 71);
         }
-        & .propertiesView-shareDropDownEdit{
-            padding: 5px;
+        & .propertiesView-shareDropDownEdit,
+        .propertiesView-shareDropDownAdmin{
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(235, 235, 145);
             color: rgb(75, 75, 5);
             border-radius: 6px;
             border: 1px solid rgb(75, 75, 5);
         }
         & .propertiesView-shareDropDownAugment{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(160, 230, 160);
             color:rgb(19, 80, 19);
             border-radius: 6px;
@@ -188,14 +194,18 @@
 
         }
         & .propertiesView-shareDropDownView{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(161, 161, 238);
             color: rgb(25, 25, 101);
             border-radius: 6px;
             border: 1px solid rgb(25, 25, 101);
         }
         & .propertiesView-shareDropDownNot-Shared{
-            padding: 5px;
+            height: 16px;
+            padding: 0px;
+            padding-left: 3px;
             background: rgb(210, 143, 143);
             color: rgb(138, 47, 47);
             border-radius: 6px;
@@ -360,13 +370,13 @@
     }
 
     .expansion-button {
-        margin-left: -20;
+        margin-left: 0;
 
         .expansion-button-icon {
             width: 11px;
             height: 11px;
             color: black;
-            margin-left: 27px;
+            margin-left: -15px;
 
             &:hover {
                 color: rgb(131, 131, 131);
@@ -410,6 +420,8 @@
 
             .propertiesView-sharingTable-item-permission {
                 display: flex;
+                position: fixed;
+                right: 30px;
                 align-items: flex-end;
                 margin-left: auto;
             }
@@ -427,9 +439,6 @@
         background: inherit;
         text-align: justify; // for Edge
         text-align-last: end;
-        &:hover {
-            cursor: pointer;
-        }
     }
 
     .propertiesView-fields {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index a5ac58f75..fb21c2db2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -319,8 +319,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {' '}
-                            {permission}{' '}
+                            {permission}
                         </option>
                     ))}
             </select>
@@ -379,16 +378,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 </div>
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
-                    {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                    {this.colorACLDropDown(name, admin, permission, showExpansionIcon)}
                     {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
         );
     }
 
-    publicACLDropDown(admin: boolean, permission: string, showExpansionIcon?: boolean) {
+    colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
+            case 'Admin':
+                dropDownText = '⬢ ';
+                break;
             case 'Edit':
                 dropDownText = '⬢ ';
                 break;
@@ -402,15 +404,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 dropDownText = '▲ ';
                 break;
         }
-
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
                         <div className="propertiesView-shareDropDown">
                             {' '}
-                            {dropDownText} {admin && permission !== 'Owner' ? this.getPermissionsSelect('Public', permission) : permission}
-                            {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                            {dropDownText}
+                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                            {/* {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null} */}
                         </div>
                     </div>
                 </div>
@@ -437,9 +439,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? docToUse : docToUse?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
         const target = docs[0];
 
-        const effectiveAcls = GetEffectiveAcl(target);
-        const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-
+        // const effectiveAcls = GetEffectiveAcl(target);
+        // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
+        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)]
+        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner'
+        
         // users in common between all docs
         // const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
         // const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
@@ -480,11 +484,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <div>
                 {' '}
-                Sharing Mode
-                <div>{this.publicACLDropDown(showAdmin, StrCast(target['acl-Public']), false)}</div>
+                Sharing Mode for Public / Guest Users
+                <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
                 <div>
                     {' '}
-                    <br></br> Who has access to the Dashboard?{' '}
+                    <br></br> Sharing Modes for Individual Users and Groups with Access to the Dashboard{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {tableEntries}</div>}</div>
             </div>
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 3860decc9..5e925cd7e 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -37,6 +37,7 @@ import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { convertCompilerOptionsFromJson } from 'typescript';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
-- 
cgit v1.2.3-70-g09d2


From 540b8310170f599c93811291ce6d3992c323cd42 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 10:14:16 -0400
Subject: properties view

---
 package-lock.json                        |  38 +++++++++++------------
 src/.DS_Store                            | Bin 10244 -> 10244 bytes
 src/client/util/SharingManager.scss      |   3 +-
 src/client/util/SharingManager.tsx       |  31 +++++++++++--------
 src/client/views/DocumentDecorations.tsx |   4 +--
 src/client/views/PropertiesView.tsx      |  50 +++++++++++++++++++++++++++++--
 6 files changed, 90 insertions(+), 36 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/package-lock.json b/package-lock.json
index 0347984a4..c55ba91eb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3170,7 +3170,7 @@
                 "textarea-caret": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
-                    "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
+                    "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
                 }
             }
         },
@@ -3269,7 +3269,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -4166,7 +4166,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "bail": {
             "version": "2.0.2",
@@ -4236,7 +4236,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -5217,7 +5217,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -5227,7 +5227,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -5947,7 +5947,7 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
         },
         "cyclist": {
             "version": "1.0.1",
@@ -7161,7 +7161,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -10351,14 +10351,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -10934,7 +10934,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -12468,7 +12468,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -12729,7 +12729,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -12737,7 +12737,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -21782,7 +21782,7 @@
         "sparse-bitfield": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
             "optional": true,
             "requires": {
                 "memory-pager": "^1.0.2"
@@ -22543,7 +22543,7 @@
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -24172,7 +24172,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
+                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -24181,7 +24181,7 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
                     "dev": true
                 },
                 "semver": {
@@ -24733,7 +24733,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "3.1.1",
diff --git a/src/.DS_Store b/src/.DS_Store
index be99aa5af..06389d6ae 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index aa1e11ef5..80d5807b4 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -107,6 +107,7 @@
             .group-container {
                 width: 50%;
                 display: flex;
+                top:0;
                 flex-direction: column;
 
                 .user-sort {
@@ -199,7 +200,7 @@
         display: flex;
         position: relative;
         margin-top: 5px;
-        margin-bottom: 10px;
+        // margin-bottom: 10px;
         font-size: 22px;
         -webkit-user-select: none;
         -moz-user-select: none;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6112b5b69..78e502e6e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -526,7 +526,7 @@ export class SharingManager extends React.Component<{}> {
                 docToUse = Doc.GetProto(this.targetDoc!);
 
                 const userKey = `acl-${normalizeEmail(user.email)}`;
-                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
+                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
@@ -574,8 +574,8 @@ export class SharingManager extends React.Component<{}> {
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs
-                .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
-                .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
+                .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
+                .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
             const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
 
             return !permissions ? null : (
@@ -670,14 +670,21 @@ export class SharingManager extends React.Component<{}> {
                         </div>
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
-                                Groups{' '}
-                                {this.groupSort === 'ascending' ? (
-                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                ) : this.groupSort === 'descending' ? (
-                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                ) : (
-                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                )}
+                                <div className="container">
+                                    Groups
+                                    &nbsp;
+                                    <div className="group-info" onClick={action(() => (GroupManager.Instance?.open()))}>
+                                        <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
+                                    </div>
+                                    &nbsp;
+                                    {this.groupSort === 'ascending' ? (
+                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                    ) : this.groupSort === 'descending' ? (
+                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                    ) : (
+                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                    )}
+                                </div>
                             </div>
                             <div className={'groups-list'}>{groupListContents}</div>
                         </div>
@@ -690,4 +697,4 @@ export class SharingManager extends React.Component<{}> {
     render() {
         return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 74cf58c7b..0cd92e2cf 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -760,8 +760,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
+        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index b6699c3bf..ca27e244d 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -6,7 +6,11 @@ import { Checkbox, Tooltip } from '@material-ui/core';
 import { action, computed, Lambda, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
+<<<<<<< Updated upstream
 import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+=======
+import { AclAdmin, AclSym, DataSym, Doc, Field, FieldResult, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+>>>>>>> Stashed changes
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
@@ -33,6 +37,8 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
+import { GroupManager } from '../util/GroupManager';
+import { intersection } from 'lodash';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -384,6 +390,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         );
     }
 
+    /**
+     * @returns a colored dropdown bar reflective of the permission
+     */
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
@@ -411,7 +420,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {' '}
                             {dropDownText}
                             {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
-                            {/* {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null} */}
                         </div>
                     </div>
                 </div>
@@ -426,6 +434,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return u1 > u2 ? -1 : u1 === u2 ? 0 : 1;
     };
 
+    /**
+     * Sorting algorithm to sort groups.
+     */
+    sortGroups = (g1: Doc, g2: Doc) => {
+        return g1 > g2 ? -1 : g1 === g2 ? 0 : 1;
+    };
+
     /**
      * @returns the sharing and permissions panel.
      */
@@ -451,6 +466,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
+<<<<<<< Updated upstream
         // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
         if (commonKeys.length) {
             for (const key of commonKeys) {
@@ -458,6 +474,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
                 if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
                     tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+=======
+        // adds each user to usersAdded
+        SharingManager.Instance.users.forEach(eachUser => {
+            var userOnDashboard = true;
+            var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`]);
+            if (Doc.ActiveDashboard) {
+                if (Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == '' || Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
+                    userOnDashboard = false;
+>>>>>>> Stashed changes
                 }
             }
             if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
@@ -466,6 +491,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
         });
 
+<<<<<<< Updated upstream
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
@@ -479,8 +505,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 !ownerSame
             )
         );
+=======
+        // adds each user to the table
+        usersAdded.sort(this.sortUsers);
+        usersAdded.map(userEmail => {
+            const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]);
+            tableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
+        });
+>>>>>>> Stashed changes
 
-        // add current user
+        // adds current user
         var userEmail = Doc.CurrentUserEmail;
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
@@ -488,6 +522,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             usersAdded.push(userEmail);
         }
 
+        // adds groups
+        const groupList = GroupManager.Instance?.allGroups || [];
+        const groups = groupList.slice().sort(this.sortGroups);
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
+        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
+        groupListMap.map(group => {
+            if (group.title != 'Public'){
+                const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
+                tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+            }
+        })
+
         // shift owner to top
         tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
 
-- 
cgit v1.2.3-70-g09d2


From 3225f565acd8edaac5992f9766dc66ac5547257f Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 11:18:19 -0400
Subject: more merge fixes

---
 src/client/views/DocumentDecorations.tsx           | 27 ++++++-------
 src/client/views/MarqueeAnnotator.tsx              |  5 ++-
 src/client/views/PropertiesView.tsx                | 24 +++++------
 .../views/nodes/formattedText/FormattedTextBox.tsx | 25 ++++++------
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 src/fields/util.ts                                 | 47 ++++++++++++----------
 7 files changed, 69 insertions(+), 63 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 0cd92e2cf..9bc4cb48f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,38 +5,37 @@ import { IconButton } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
+import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
-import { Document } from '../../fields/documentSchemas';
+import { Doc, DocListCast, Field } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl } from '../../fields/util';
-import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
-import { Docs } from '../documents/Documents';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
+import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
+import { Docs } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { LinkFollower } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { Colors } from './global/globalEnums';
-import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
+import { InkingStroke } from './InkingStroke';
 import { LightboxView } from './LightboxView';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
+import { Colors } from './global/globalEnums';
 import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import React = require('react');
 import _ = require('lodash');
-import { DocumentManager } from '../util/DocumentManager';
-import { isUndefined } from 'lodash';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -805,7 +804,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.props.hideDeleteButton ||
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
-                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DataSym]) : AclEdit;
+                const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
                 return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 11336d079..7edc1494b 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,6 +1,7 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { Doc, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData,  } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
@@ -207,7 +208,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     @action
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean, summarize?: boolean) => {
         // creates annotation documents for current highlights
-        const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
+        const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DocData]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         addAsAnnotation && !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return (annotationDoc as Doc) ?? undefined;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 25477f948..abbf9a22e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -3,39 +3,39 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@material-ui/core';
-import { action, computed, Lambda, observable } from 'mobx';
+import { intersection } from 'lodash';
+import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, StrListCast } from '../../fields/Doc';
-import { AclAdmin, DocAcl, DocData,  Height,  Width } from '../../fields/DocSymbols';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { Doc, Field, FieldResult, NumListCast, Opt, StrListCast } from '../../fields/Doc';
+import { DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { SharingPermissions, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
+import { GroupManager } from '../util/GroupManager';
 import { LinkManager } from '../util/LinkManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SharingManager } from '../util/SharingManager';
 import { Transform } from '../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../util/UndoManager';
 import { EditableView } from './EditableView';
 import { FilterPanel } from './FilterPanel';
-import { Colors } from './global/globalEnums';
 import { InkStrokeProperties } from './InkStrokeProperties';
-import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
-import { KeyValueBox } from './nodes/KeyValueBox';
-import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 import { PropertiesButtons } from './PropertiesButtons';
 import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
 import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
 import './PropertiesView.scss';
 import { DefaultStyleProvider } from './StyleProvider';
-import { GroupManager } from '../util/GroupManager';
-import { intersection } from 'lodash';
+import { Colors } from './global/globalEnums';
+import { DocumentView, OpenWhere, StyleProviderFunc } from './nodes/DocumentView';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
 const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f4cecb1dc..2f31c9c57 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,7 +12,8 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast,StrListCast,  Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width,ForceServerWrite,  UpdatingFromServer} from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -301,16 +302,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             this._editorView.updateState(state);
 
             const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
-            const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
-            const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField) : undefined; // the actual text in the text box
-            const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
-            const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
-            const json = JSON.stringify(state.toJSON());
+            const newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+            const newJson = JSON.stringify(state.toJSON());
+            const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
+            const prevLayoutData = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
+            const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const effectiveAcl = GetEffectiveAcl(dataDoc);
 
             const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
 
-            if ([AclEdit, AclAdmin, AclAugment].includes(effectiveAcl)) {
+            if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -1806,12 +1807,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             default:
                 if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case ' ':
-                if (e.code == 'Space') {
-                    break;
+                if (e.code !== 'Space') {
+                    [AclEdit, AclAugment, AclAdmin,].includes(GetEffectiveAcl(this.rootDoc)) &&
+                        this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
                 }
-                [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
-                    this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
-        }
+                break;
+            }
         this.startUndoTypingBatch();
     };
     ondrop = (e: React.DragEvent) => {
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c4a9b4a7e..ecd0bf8aa 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { Schema } from 'prosemirror-model';
 import { splitListItem, wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
-import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/DocSymbols';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 88587b3c1..555e768c1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -145,7 +145,7 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
 export function updateCachedAcls(doc: Doc) {
     if (!doc) return;
 
-    const target = (doc as any)?.__fields ?? doc;
+    const target = (doc as any)?.__fieldTuples ?? doc;
     const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
     Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
     if (Object.keys(permissions).length || doc[DocAcl]?.length) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 6058a5a5f..e0f87f805 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -8,27 +8,32 @@ import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
 import {
-    AclAdmin,
-    AclEdit,
-    aclLevel,
-    AclPrivate,
-    AclSelfEdit,
-    AclSym,
-    DataSym,
     Doc,
+    aclLevel,
     DocListCast,
     DocListCastAsync,
-    ForceServerWrite,
-    HeightSym,
     HierarchyMapping,
-    Initializing,
-    LayoutSym,
     ReverseHierarchyMap,
     updateCachedAcls,
-    UpdatingFromServer,
-    WidthSym,
 } from './Doc';
-import { Id, OnUpdate, Parent, SelfProxy, ToValue, Update } from './FieldSymbols';
+import {
+    AclAdmin,
+    AclEdit,
+    AclPrivate,
+    AclAugment,
+    FieldKeys,
+    DocAcl,
+    DocData,
+    ForceServerWrite,
+    Height,
+    Initializing,
+    DocLayout,
+    UpdatingFromServer,
+    Width,
+    SelfProxy,
+    Update
+} from './DocSymbols';
+import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
@@ -150,12 +155,12 @@ export function denormalizeEmail(email: string) {
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
     return;
-    const dataDoc = parent[DataSym];
-    for (const key of Object.keys(dataDoc)) {
-        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
-        key.startsWith('acl') && distributeAcls(key, permission, child);
-    }
+    // const dataDoc = parent[DocData];
+    // for (const key of Object.keys(dataDoc)) {
+    //     // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+    //     const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+    //     key.startsWith('acl') && distributeAcls(key, permission, child);
+    // }
 }
 
 /**
@@ -214,7 +219,7 @@ export function SetCachedGroups(groups: string[]) {
     runInAction(() => cachedGroups.push(...groups));
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
-    const targetAcls = target[AclSym];
+    const targetAcls = target[DocAcl];
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-- 
cgit v1.2.3-70-g09d2


From d92775bffab6470dc6142e02092b7cae2c30b5ff Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 Jun 2023 12:00:12 -0400
Subject: fixes post-merge

---
 src/client/documents/tempCodeRunnerFile.ts |  1 -
 src/client/util/SharingManager.tsx         | 13 ++++++------
 src/client/views/DocComponent.tsx          |  4 ++--
 src/client/views/DocumentDecorations.tsx   | 34 +++++++++++++++++++-----------
 src/client/views/MarqueeAnnotator.tsx      |  2 +-
 src/client/views/PropertiesView.tsx        | 20 +++++++++---------
 src/fields/Doc.ts                          |  4 +---
 src/fields/util.ts                         | 31 +++------------------------
 8 files changed, 45 insertions(+), 64 deletions(-)
 delete mode 100644 src/client/documents/tempCodeRunnerFile.ts

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/documents/tempCodeRunnerFile.ts b/src/client/documents/tempCodeRunnerFile.ts
deleted file mode 100644
index 76ea17be0..000000000
--- a/src/client/documents/tempCodeRunnerFile.ts
+++ /dev/null
@@ -1 +0,0 @@
-Initializing
\ No newline at end of file
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 78e502e6e..bea870f21 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,4 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton, Size } from 'browndash-components';
 import { intersection } from 'lodash';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -6,7 +7,7 @@ import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, HierarchyMapping } from '../../fields/Doc';
-import { AclAdmin, AclPrivate, DocAcl, AclUnset, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
@@ -23,7 +24,6 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
-import { IconButton, Size } from 'browndash-components';
 
 export interface User {
     email: string;
@@ -652,7 +652,7 @@ export class SharingManager extends React.Component<{}> {
                             </div>
                         </div>
                     ) : (
-                        <br></br>
+                        <br />
                     )}
                     <div className="main-container">
                         <div className={'individual-container'}>
@@ -671,9 +671,8 @@ export class SharingManager extends React.Component<{}> {
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
                                 <div className="container">
-                                    Groups
-                                    &nbsp;
-                                    <div className="group-info" onClick={action(() => (GroupManager.Instance?.open()))}>
+                                    Groups &nbsp;
+                                    <div className="group-info" onClick={action(() => GroupManager.Instance?.open())}>
                                         <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                     </div>
                                     &nbsp;
@@ -697,4 +696,4 @@ export class SharingManager extends React.Component<{}> {
     render() {
         return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index dc9c8a0ce..db24229dc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -214,7 +214,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
+                            doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
                             Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
@@ -222,7 +222,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                    targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
+                    targetDataDoc[(annotationKey ?? this.annotationKey) + '_modificationDate'] = new DateField(new Date(Date.now()));
                 }
             }
             return true;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9bc4cb48f..744d04370 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail } from '../../fields/util';
+import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
@@ -655,7 +655,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         const rotCtr = [docwidth / 2, docheight / 2];
                         const tlRotated = Utils.rotPt(-rotCtr[0], -rotCtr[1], (NumCast(doc._rotation) / 180) * Math.PI);
 
-                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+                        const maxHeight = doc.nativeHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '_scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
                         dH && (doc._layout_autoHeight = false);
@@ -760,23 +760,23 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // sharing
         // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-'+normalizeEmail(Doc.CurrentUserEmail)];
+        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
-            case 'Admin':
+            case SharingPermissions.Admin:
                 shareSymbolIcon = '⬢ ';
                 break;
-            case 'Edit':
+            case SharingPermissions.Edit:
                 shareSymbolIcon = '⬢ ';
                 break;
-            case 'Augment':
+            case SharingPermissions.Augment:
                 shareSymbolIcon = '⬟ ';
                 break;
-            case 'View':
+            case SharingPermissions.View:
                 shareSymbolIcon = '♦ ';
                 break;
-            case 'Not-Shared':
+            case SharingPermissions.None:
                 shareSymbolIcon = '▲ ';
                 break;
             default:
@@ -805,7 +805,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             seldocview.rootDoc.hideDeleteButton ||
             SelectionManager.Views().some(docView => {
                 const collectionAcl = docView.props.docViewPath()?.lastElement() ? GetEffectiveAcl(docView.props.docViewPath().lastElement().rootDoc[DocData]) : AclEdit;
-                return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                return (docView.rootDoc.stayInCollection && !docView.rootDoc._isTimelineLabel) || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
@@ -833,7 +833,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // Radius constants
         const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
-        const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding));
+        const borderRadius = numberValue(StrCast(seldocview.rootDoc.layout_borderRounding));
         const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
@@ -858,7 +858,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             </div>
         );
         const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => SettingsManager.propertiesWidth =250))}>
+            <div
+                className="documentDecorations-share"
+                onPointerDown={e =>
+                    setupMoveUpEvents(
+                        this,
+                        e,
+                        returnFalse,
+                        returnFalse,
+                        action(() => (SettingsManager.propertiesWidth = 250))
+                    )
+                }>
                 <div className={`documentDecorations-share${shareMode}`}>
                     <span>{shareSymbolIcon + ' ' + shareMode}</span>
                 </div>
@@ -899,7 +909,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
+                                {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as new embedding, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
                                 <>
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 7edc1494b..a4a2c1df9 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,7 +1,7 @@
 import { action, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc, Opt } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData,  } from '../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast } from '../../fields/Types';
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index abbf9a22e..43ac2aa88 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -393,19 +393,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
         var dropDownText = '';
         switch (StrCast(permission)) {
-            case 'Admin':
+            case SharingPermissions.Admin:
                 dropDownText = '⬢ ';
                 break;
-            case 'Edit':
+            case SharingPermissions.Edit:
                 dropDownText = '⬢ ';
                 break;
-            case 'Augment':
+            case SharingPermissions.Augment:
                 dropDownText = '⬟ ';
                 break;
-            case 'View':
+            case SharingPermissions.View:
                 dropDownText = '♦ ';
                 break;
-            case 'Not-Shared':
+            case SharingPermissions.None:
                 dropDownText = '▲ ';
                 break;
         }
@@ -454,9 +454,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // const effectiveAcls = GetEffectiveAcl(target);
         // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)]
-        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner'
-       
+        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
+        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner';
+
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -496,11 +496,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.map(group => {
-            if (group.title != 'Public'){
+            if (group.title != 'Public') {
                 const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
                 tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
             }
-        })
+        });
 
         // shift owner to top
         tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 555e768c1..3116fd70b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1267,9 +1267,7 @@ export namespace Doc {
     }
     // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
     export function IsBrushedDegreeUnmemoized(doc: Doc) {
-        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) {
-            return DocBrushStatus.unbrushed;
-        }
+        if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
         const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index e0f87f805..9c0f4ba1f 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -7,41 +7,16 @@ import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
 import { returnZero } from '../Utils';
 import CursorField from './CursorField';
-import {
-    Doc,
-    aclLevel,
-    DocListCast,
-    DocListCastAsync,
-    HierarchyMapping,
-    ReverseHierarchyMap,
-    updateCachedAcls,
-} from './Doc';
-import {
-    AclAdmin,
-    AclEdit,
-    AclPrivate,
-    AclAugment,
-    FieldKeys,
-    DocAcl,
-    DocData,
-    ForceServerWrite,
-    Height,
-    Initializing,
-    DocLayout,
-    UpdatingFromServer,
-    Width,
-    SelfProxy,
-    Update
-} from './DocSymbols';
+import { aclLevel, Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap, updateCachedAcls } from './Doc';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, DocAcl, DocData, DocLayout, FieldKeys, ForceServerWrite, Height, Initializing, SelfProxy, Update, UpdatingFromServer, Width } from './DocSymbols';
 import { Id, OnUpdate, Parent, ToValue } from './FieldSymbols';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
 import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
-import { ComputedField, ScriptField } from './ScriptField';
+import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
-import { convertCompilerOptionsFromJson } from 'typescript';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
-- 
cgit v1.2.3-70-g09d2


From e31f6403bc62faea72767f68d3bf34ac8bd28183 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 12:43:58 -0400
Subject: acls distribute properly, documentDecorations shows user effective
 acl

---
 src/client/util/SharingManager.tsx       | 13 +++++++------
 src/client/views/DocumentDecorations.tsx | 12 ++++++++++--
 2 files changed, 17 insertions(+), 8 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 78e502e6e..01f36bd29 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -159,13 +159,14 @@ export class SharingManager extends React.Component<{}> {
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
-        // setting the same acl for a docs within the doc being shared
-        if (this.overridePrivate) {
-            var childDocs = DocListCast(target.data);
-            childDocs.map(doc => {
+        // setting the same acl for a docs within the doc being shared if they haven't been set yet
+        // or if the 'Override Private' checkbox is selected
+        var childDocs = DocListCast(target.data);
+        childDocs.map(doc => {
+            if (this.overridePrivate || doc[acl]==undefined){
                 this.setInternalSharing(recipient, permission, doc);
-            });
-        }
+            }
+        });
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9bc4cb48f..25d70b932 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -165,6 +165,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action onContainerDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
         const effectiveAcl = GetEffectiveAcl(first.rootDoc);
+        console.log(effectiveAcl)
         if (effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -198,7 +199,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
-        const dragDocView = SelectionManager.Views()[0];
+        const first = SelectionManager.Views()[0];
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
+        if (effectiveAcl != AclAdmin && effectiveAcl != AclEdit && effectiveAcl != AclAugment){
+            return false;
+        }      
+const dragDocView = SelectionManager.Views()[0];
         const containers = new Set<Doc | undefined>();
         SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer)));
         if (containers.size > 1) return false;
@@ -857,8 +863,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
             </div>
         );
+        
         const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => SettingsManager.propertiesWidth =250))}>
+            <div className='documentDecorations-share' onPointerDown={e => setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), e => {}, action(e => {}))} >
                 <div className={`documentDecorations-share${shareMode}`}>
                     <span>{shareSymbolIcon + ' ' + shareMode}</span>
                 </div>
@@ -899,6 +906,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
+                                {sharingMenu}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
-- 
cgit v1.2.3-70-g09d2


From 3899a3cb58965eb71582c9cb5cdc31d27ec91e72 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 15 Jun 2023 12:54:03 -0400
Subject: added drag lock back into document decorations.

---
 src/client/views/DocumentDecorations.tsx                  |  7 +++++++
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 14 ++++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 744d04370..79e3bd64f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -855,6 +855,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         ) : (
             <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}>
                 <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
+                {!useLock ? null : (
+                    <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
+                        <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
+                            <FontAwesomeIcon size="sm" icon="lock" />
+                        </div>
+                    </Tooltip>
+                )}
             </div>
         );
         const sharingMenu = docShareMode ? (
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2f31c9c57..8a0ac2da3 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,8 +12,8 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
 import { EditorView } from 'prosemirror-view';
 import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast,StrListCast,  Field, Opt } from '../../../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width,ForceServerWrite,  UpdatingFromServer} from '../../../../fields/DocSymbols';
+import { Doc, DocListCast, StrListCast, Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
@@ -1788,7 +1788,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         e.stopPropagation();
         for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) {
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
             }
         }
@@ -1808,11 +1808,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                 if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case ' ':
                 if (e.code !== 'Space') {
-                    [AclEdit, AclAugment, AclAdmin,].includes(GetEffectiveAcl(this.rootDoc)) &&
-                        this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
+                    [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
+                        this._editorView!.dispatch(
+                            this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))
+                        );
                 }
                 break;
-            }
+        }
         this.startUndoTypingBatch();
     };
     ondrop = (e: React.DragEvent) => {
-- 
cgit v1.2.3-70-g09d2


From f563edd4bf0dc2000564bf24afb095c36edf268a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 12:55:12 -0400
Subject: DocumentDecorations

---
 src/client/views/DocumentDecorations.tsx | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 80e2f1e65..73e293db2 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -915,6 +915,7 @@ const dragDocView = SelectionManager.Views()[0];
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
+                                {sharingMenu}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
-- 
cgit v1.2.3-70-g09d2


From a217c9261990cb751119ae68e7dcc7e32f48e529 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 11:13:52 -0400
Subject: cleaner w/ effectiveAcls

---
 src/client/util/SharingManager.tsx       | 19 ++++++-------------
 src/client/views/DocComponent.tsx        | 14 +++++++++-----
 src/client/views/DocumentDecorations.tsx |  5 ++---
 src/client/views/PropertiesView.tsx      |  1 -
 src/client/views/topbar/TopBar.tsx       |  2 +-
 src/fields/util.ts                       | 32 ++++++++++++++------------------
 6 files changed, 32 insertions(+), 41 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6cbc060fa..a161b4dc2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -80,7 +80,7 @@ export class SharingManager extends React.Component<{}> {
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-    @observable private overridePrivate: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
+    @observable private overridePrevious: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -160,10 +160,10 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Private' checkbox is selected
+        // or if the 'Override previous' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overridePrivate || doc[acl]==undefined){
+            if (this.overridePrevious || doc[acl]==undefined){
                 this.setInternalSharing(recipient, permission, doc);
             }
         });
@@ -204,7 +204,7 @@ export class SharingManager extends React.Component<{}> {
         // or if the 'Override Private' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overridePrivate || doc[acl]==undefined){
+            if (this.overridePrevious || doc[acl]==undefined){
                 this.setInternalGroupSharing(group, permission, doc);
             }
         });
@@ -279,7 +279,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overridePrivate) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data));
+                if (this.overridePrevious) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data));
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
@@ -520,13 +520,6 @@ export class SharingManager extends React.Component<{}> {
             // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
             .filter(({ user }) => docs[0]?.author !== user.email)
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
-                const dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
-                // const dashboard = dashboardList[0]
-                const dashboard = Doc.ActiveDashboard;
-                var docToUse = dashboard;
-
-                docToUse = Doc.GetProto(this.targetDoc!);
-
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
@@ -647,7 +640,7 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
-                                        <input type="checkbox" onChange={action(() => (this.overridePrivate = !this.overridePrivate))} checked={this.overridePrivate} /> <label>Override Private </label>
+                                        <input type="checkbox" onChange={action(() => (this.overridePrevious = !this.overridePrevious))} checked={this.overridePrevious} /> <label>Override previous </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index db24229dc..7e8946ca0 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,9 +1,9 @@
 import { action, computed, observable } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { DocListCast, Opt, Doc } from '../../fields/Doc';
+import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast, ScriptCast } from '../../fields/Types';
+import { Cast, ScriptCast, StrCast } from '../../fields/Types';
 import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
@@ -191,11 +191,15 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                const aclKeys = Object.keys(this.props.Document[DocAcl] ?? {});
+                const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
+
                 aclKeys.forEach(key =>
                     added.forEach(d => {
-                        if (d.author === denormalizeEmail(key.substring(4)) && !d.createdFrom) {
-                            distributeAcls(key, SharingPermissions.Admin, d);
+                        if (key != 'acl-Me'){
+                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
+                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
+                            const permission = HierarchyMapping.get(permissionSymbol)!.name
+                            distributeAcls(key, permission, d)
                         }
                     })
                 );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ff98e18d4..8e224a4b6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field } from '../../fields/Doc';
+import { Doc, DocListCast, Field, HierarchyMapping } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
@@ -765,8 +765,7 @@ const dragDocView = SelectionManager.Views()[0];
         }
 
         // sharing
-        // const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-Public'];
-        const docShareMode = Doc.GetProto(seldocview.rootDoc)['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
+        const docShareMode = HierarchyMapping.get(GetEffectiveAcl(seldocview.rootDoc))!.name
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = null;
         switch (shareMode) {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 4674bc0f4..8648d84c2 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -499,7 +499,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (Doc.ActiveDashboard[groupKey] != '' && Doc.ActiveDashboard[groupKey] != undefined) {
                     const permission = StrCast(target[groupKey]);
-                    console.log(permission)
                     tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
                 }
             }
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 71daad1a9..834156295 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -103,7 +103,7 @@ export class TopBar extends React.Component {
                     }}
                 />
                 <Button
-                    text={GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'View Original'}
+                    text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'}
                     onClick={() => {
                         SharingManager.Instance.open(undefined, Doc.ActiveDashboard);
                     }}
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 469e4d944..6c8758875 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -17,6 +17,7 @@ import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
+import { GroupManager } from '../client/util/GroupManager';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -171,7 +172,15 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
-    target = Doc.GetProto(target);
+    if (!target) return AclPrivate;
+    if (target[UpdatingFromServer]) return AclAdmin;
+    return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
+}
+
+/**
+ * Calculates the effective access layout right to a document for the current user.
+ */
+export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
     return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
@@ -195,7 +204,6 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[DocAcl];
-    console.log(target.title, targetAcls)
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
@@ -205,31 +213,20 @@ function getEffectiveAcl(target: any, user?: string): symbol {
             // 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.
             const entity = denormalizeEmail(key.substring(4)); // an individual or a group
-            if (HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
-                if (GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') {
+            if (GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') {
+                if (HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
                     effectiveAcl = value as symbol;
                 }
             }
+            console.log(targetAcls)
         }
 
-        // if there's an overriding acl set through the properties panel or sharing menu, that's what's returned if the user isn't an admin of the document
-        //const override = targetAcls['acl-Override'];
-        // if (override !== AclUnset && override !== undefined) effectiveAcl = override;
-
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
     }
     // authored documents are private until an ACL is set.
     const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
-    let acl = AclPrivate;
-    if (user) {
-        acl = target['acl-' + user];
-    } else {
-        acl = target['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
-    }
-    console.log(target['acl-' + normalizeEmail(Doc.CurrentUserEmail)]);
-    return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(acl)!.level < aclLevel.editable ? AclEdit : acl;
 }
 /**
  * Recursively distributes the access right for a user across the children of a document and its annotations.
@@ -240,11 +237,10 @@ function getEffectiveAcl(target: any, user?: string): symbol {
  * 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, visited?: Doc[], isDashboard?: boolean) {
+    console.log(key,acl,target.title)
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
-
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-        target[key] = acl;
         Doc.GetProto(target)[key] = acl;
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
-- 
cgit v1.2.3-70-g09d2


From e581928cd39e08b35eba6a2447e7a9ba75101b96 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 13:22:07 -0400
Subject: DocumentDecorations layout

---
 src/client/views/DocumentDecorations.scss |  1 +
 src/client/views/DocumentDecorations.tsx  | 39 ++++++++++++++++---------------
 src/fields/util.ts                        |  2 --
 3 files changed, 21 insertions(+), 21 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index a5ebe9d58..4d29427a6 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -179,6 +179,7 @@ $resizeHandler: 8px;
             display: flex;
             height: 20px;
             border-radius: 8px;
+            gap: 2px;
             outline: none;
             border: none;
             opacity: 0.3;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 8e224a4b6..c82257d1f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -844,6 +844,25 @@ const dragDocView = SelectionManager.Views()[0];
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
 
+        const sharingMenu = docShareMode ? (
+            <div className='documentDecorations-share' 
+                 onPointerDown={e => 
+                    setupMoveUpEvents(
+                        this, 
+                        e, 
+                        e => this.onBackgroundMove(true, e), 
+                        returnFalse, 
+                        action(() => SettingsManager.propertiesWidth =250)
+                    )
+                }>
+                <div className={`documentDecorations-share${shareMode}`}>
+                    <span>{shareSymbolIcon + ' ' + shareMode}</span>
+                </div>
+            </div>
+        ) : (
+            <div />
+        );
+
         const titleArea = this._editingTitle ? (
             <input
                 ref={this._keyinput}
@@ -860,6 +879,7 @@ const dragDocView = SelectionManager.Views()[0];
         ) : (
             <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}>
                 <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
+                {sharingMenu}
                 {!useLock ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
                         <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}>
@@ -870,24 +890,6 @@ const dragDocView = SelectionManager.Views()[0];
             </div>
         );
         
-        const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' 
-                 onPointerDown={e => 
-                    setupMoveUpEvents(
-                        this, 
-                        e, 
-                        e => this.onBackgroundMove(true, e), 
-                        returnFalse, 
-                        action(() => SettingsManager.propertiesWidth =250)
-                    )
-                }>
-                <div className={`documentDecorations-share${shareMode}`}>
-                    <span>{shareSymbolIcon + ' ' + shareMode}</span>
-                </div>
-            </div>
-        ) : (
-            <div />
-        );
         return (
             <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}>
                 <div
@@ -921,7 +923,6 @@ const dragDocView = SelectionManager.Views()[0];
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
                                 {hideTitle ? null : titleArea}
-                                {sharingMenu}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 6c8758875..5bab701ff 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -218,7 +218,6 @@ function getEffectiveAcl(target: any, user?: string): symbol {
                     effectiveAcl = value as symbol;
                 }
             }
-            console.log(targetAcls)
         }
 
         return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
@@ -237,7 +236,6 @@ function getEffectiveAcl(target: any, user?: string): symbol {
  * 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, visited?: Doc[], isDashboard?: boolean) {
-    console.log(key,acl,target.title)
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-- 
cgit v1.2.3-70-g09d2


From 116961977c69b77d7437a1c1b9719fc977060caf Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 21:53:34 -0400
Subject: PropertiesView UI updates and sharing symbol icon in heirarchy
 mapping

---
 src/client/views/DocumentDecorations.tsx | 24 ++-------------
 src/client/views/PropertiesView.scss     | 21 ++++++-------
 src/client/views/PropertiesView.tsx      | 51 +++++++-------------------------
 src/fields/Doc.ts                        | 16 +++++-----
 4 files changed, 32 insertions(+), 80 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c82257d1f..fdbe2f866 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
 import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, Field, HierarchyMapping } from '../../fields/Doc';
+import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
@@ -767,27 +767,7 @@ const dragDocView = SelectionManager.Views()[0];
         // sharing
         const docShareMode = HierarchyMapping.get(GetEffectiveAcl(seldocview.rootDoc))!.name
         const shareMode = StrCast(docShareMode);
-        var shareSymbolIcon = null;
-        switch (shareMode) {
-            case SharingPermissions.Admin:
-                shareSymbolIcon = '⬢ ';
-                break;
-            case SharingPermissions.Edit:
-                shareSymbolIcon = '⬢ ';
-                break;
-            case SharingPermissions.Augment:
-                shareSymbolIcon = '⬟ ';
-                break;
-            case SharingPermissions.View:
-                shareSymbolIcon = '♦ ';
-                break;
-            case SharingPermissions.None:
-                shareSymbolIcon = '▲ ';
-                break;
-            default:
-                shareSymbolIcon = '';
-                break;
-        }
+        var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 3cf7f2546..7e0da5520 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -162,8 +162,8 @@
 
     .propertiesView-shareDropDown{
         margin-right: 10px;
-        width: 92%;
-
+        min-width: 65px;
+        
         & .propertiesView-shareDropDownNone{
             height: 16px;
             padding: 0px;
@@ -370,13 +370,13 @@
     }
 
     .expansion-button {
-        margin-left: 0;
+        margin-left: -15px;
+        margin-right: 20px;
 
         .expansion-button-icon {
             width: 11px;
             height: 11px;
             color: black;
-            margin-left: -15px;
 
             &:hover {
                 color: rgb(131, 131, 131);
@@ -394,8 +394,7 @@
         border-radius: 6px; // remove when adding buttons
         margin-right: 10px; // remove when adding buttons
         background-color: #ececec;
-        max-height: 130px;
-        width: 92%;
+        width: 97%;
 
         .propertiesView-sharingTable-item {
             display: flex;
@@ -420,10 +419,10 @@
 
             .propertiesView-sharingTable-item-permission {
                 display: flex;
-                position: fixed;
-                right: 30px;
                 align-items: flex-end;
+                text-align: right;
                 margin-left: auto;
+                margin-right: -12px;
             }
 
             &:last-child {
@@ -437,8 +436,10 @@
         background: inherit;
         border: none;
         background: inherit;
-        text-align: justify; // for Edge
-        text-align-last: end;
+        width: max;
+        text-align: left;
+        display: flex;
+        right: 35px;
     }
 
     .propertiesView-fields {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index f5a39898e..c7db657ff 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -3,19 +3,19 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Checkbox, Tooltip } from '@material-ui/core';
-import { intersection } from 'lodash';
+import { concat, intersection } from 'lodash';
 import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
-import { Doc, Field, FieldResult, NumListCast, Opt, StrListCast } from '../../fields/Doc';
-import { DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
+import { Doc, Field, FieldResult, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { ComputedField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { SharingPermissions, normalizeEmail } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
@@ -321,7 +321,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     .filter(permission => !Doc.noviceMode || ![SharingPermissions.View].includes(permission as any))
                     .map(permission => (
                         <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {permission}
+                            {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
                         </option>
                     ))}
             </select>
@@ -391,32 +391,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns a colored dropdown bar reflective of the permission
      */
     colorACLDropDown(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
-        var dropDownText = '';
-        switch (StrCast(permission)) {
-            case SharingPermissions.Admin:
-                dropDownText = '⬢ ';
-                break;
-            case SharingPermissions.Edit:
-                dropDownText = '⬢ ';
-                break;
-            case SharingPermissions.Augment:
-                dropDownText = '⬟ ';
-                break;
-            case SharingPermissions.View:
-                dropDownText = '♦ ';
-                break;
-            case SharingPermissions.None:
-                dropDownText = '▲ ';
-                break;
-        }
+        var shareImage = ReverseHierarchyMap.get(permission)?.image;
         return (
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
                         <div className="propertiesView-shareDropDown">
-                            {' '}
-                            {dropDownText}
-                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}
                         </div>
                     </div>
                 </div>
@@ -442,21 +423,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
-        const docToUse = this.selectedDoc;
-        if (!docToUse) {
-            return null;
-        }
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
-
         const target = docs[0];
 
-        // const effectiveAcls = GetEffectiveAcl(target);
-        // const showAdmin = effectiveAcls == AclAdmin || docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)] == 'Owner';
-        const curUserAcl = docToUse!['acl-' + normalizeEmail(Doc.CurrentUserEmail)];
-        const showAdmin = curUserAcl == 'Admin' || curUserAcl == 'Owner';
-
+        const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin
         const tableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -469,12 +441,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
             }
             if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
-                // tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
                 usersAdded.push(eachUser.user.email);
             }
         });
 
-        // adds each user to the table
+        // sorts and then adds each user to the table
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]);
@@ -507,11 +478,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return (
             <div>
                 {' '}
-                Sharing Mode for Public / Guest Users
+                Public / Guest Users
                 <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
                 <div>
                     {' '}
-                    <br></br> Sharing Modes for Individual Users and Groups with Access to the Dashboard{' '}
+                    <br></br> Individual Users with Access to this Document{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {tableEntries}</div>}</div>
             </div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3116fd70b..8f9f36153 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -130,15 +130,15 @@ export enum aclLevel {
     admin = 4,
 }
 // prettier-ignore
-export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermissions }> = new Map([
-    [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None }],
-    [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View }],
-    [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment}],
-    [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit }],
-    [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin }],
-    [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset }],
+export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermissions; image: string }> = new Map([
+    [AclPrivate,  { level: aclLevel.unshared,     name: SharingPermissions.None,    image: '▲' }],
+    [AclReadonly, { level: aclLevel.viewable,     name: SharingPermissions.View,    image: '♦' }],
+    [AclAugment,  { level: aclLevel.augmentable,  name: SharingPermissions.Augment, image: '⬟' }],
+    [AclEdit,     { level: aclLevel.editable,     name: SharingPermissions.Edit,    image: '⬢' }],
+    [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin,   image: '⬢' }],
+    [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset,   image: '▲' }],
 ]);
-export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0] }]));
+export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol ; image: string}> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
 
 // caches the document access permissions for the current user.
 // this recursively updates all protos as well.
-- 
cgit v1.2.3-70-g09d2


From 90704e36738e809bfcf11f2f375e23faf838100a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 19 Jun 2023 17:37:42 -0400
Subject: Document Decorations spacing fix

---
 src/client/views/DocumentDecorations.scss | 4 +---
 src/client/views/DocumentDecorations.tsx  | 2 ++
 2 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index c81c033da..0a690f751 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -219,13 +219,11 @@ $resizeHandler: 8px;
         opacity: 1;
         grid-column: 3;
         pointer-events: auto;
-        overflow: hidden;
+        min-width: fit-content;
         text-align: center;
         width: 125px;
         display: flex;
         height: 21px;
-        font-size: small;
-        gap: 2px;
         opacity: 0.3;
         &:hover {
             opacity: 1;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index fdbe2f866..dcfaecbc1 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -836,7 +836,9 @@ const dragDocView = SelectionManager.Views()[0];
                     )
                 }>
                 <div className={`documentDecorations-share${shareMode}`}>
+                    &nbsp;
                     <span>{shareSymbolIcon + ' ' + shareMode}</span>
+                    &nbsp;
                 </div>
             </div>
         ) : (
-- 
cgit v1.2.3-70-g09d2


From 442c22e55bf4e53e57378980e7831cfea035d52a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 21 Jun 2023 16:55:45 -0400
Subject: layout acls initially unset + bug fix with public acl when creating
 tabs and dashbaords

---
 src/client/documents/Documents.ts                      | 12 +++++++-----
 src/client/views/DocComponent.tsx                      |  4 ++--
 src/client/views/DocumentDecorations.tsx               |  4 ++--
 src/client/views/collections/CollectionDockingView.tsx | 18 +++++++++++++++---
 src/fields/util.ts                                     | 17 ++++++++++-------
 5 files changed, 36 insertions(+), 19 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ffde9fe1b..7dde70a2e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -839,7 +839,9 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
-            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            if (dataProps['acl-Public'] == undefined && Doc.GetProto(dataProps)['acl-Public'] == undefined){
+                dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            }
 
             dataProps.isSystem = viewProps.isSystem;
             dataProps.isDataDoc = true;
@@ -863,7 +865,7 @@ export namespace Docs {
             }
 
             const viewFirstProps: { [id: string]: any } = {};
-            viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            // viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             // viewFirstProps['acl-Override'] = SharingPermissions.Unset;
             viewFirstProps.author = Doc.CurrentUserEmail;
             let viewDoc: Doc;
@@ -1011,7 +1013,7 @@ export namespace Docs {
             I.rotation = 0;
             I.defaultDoubleClick = 'click';
             I.author_date = new DateField();
-            I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            // I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             //I['acl-Override'] = SharingPermissions.Unset;
             I[Initializing] = false;
             return I;
@@ -1348,8 +1350,8 @@ export namespace DocUtils {
                 source,
                 target,
                 {
-                    'acl-Public': SharingPermissions.Augment,
-                    '_acl-Public': SharingPermissions.Augment,
+                    // 'acl-Public': SharingPermissions.Augment,
+                    // '_acl-Public': SharingPermissions.Augment,
                     title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
                     link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined,
                     link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined,
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 7e8946ca0..98aa9f880 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -206,7 +206,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
-                        if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
+                        // if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                         doc.embedContainer = this.props.Document;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
@@ -221,7 +221,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
 
-                            Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
+                            // Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
                         });
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index dcfaecbc1..c4c6b6488 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
+import { GetEffectiveAcl, GetEffectiveLayoutAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { DocumentType } from '../documents/DocumentTypes';
 import { Docs } from '../documents/Documents';
 import { DocumentManager } from '../util/DocumentManager';
@@ -165,7 +165,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action onContainerDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
         const effectiveAcl = GetEffectiveAcl(first.rootDoc);
-        console.log(effectiveAcl)
+        console.log(GetEffectiveLayoutAcl(first))
         if (effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index a5d7e7864..215e9c343 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -2,13 +2,13 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo
 import { observer } from 'mobx-react';
 import * as ReactDOM from 'react-dom/client';
 import * as GoldenLayout from '../../../client/goldenLayout';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { inheritParentAcls } from '../../../fields/util';
+import { distributeAcls, inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
@@ -29,6 +29,7 @@ import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
+import { DocAcl } from '../../../fields/DocSymbols';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -485,6 +486,15 @@ export class CollectionDockingView extends CollectionSubView() {
         }
     };
     tabCreated = (tab: any) => {
+        const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
+        aclKeys.forEach(key => {
+            if (key != 'acl-Me'){
+                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
+                const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
+                const permission = HierarchyMapping.get(permissionSymbol)!.name
+                distributeAcls(key, permission, Doc.GetProto(tab))
+            }
+        });
         this.tabMap.add(tab);
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
     };
@@ -545,7 +555,9 @@ export class CollectionDockingView extends CollectionSubView() {
                             _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
-                        this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                        this.props.Document.isShared && inheritParentAcls(Doc.GetProto(this.props.Document), Doc.GetProto(docToAdd));
+                        console.log(Doc.GetProto(this.props.Document))
+                        console.log(Doc.GetProto(docToAdd))
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 5bab701ff..9fb65c251 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -130,13 +130,16 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
-    return;
-    // const dataDoc = parent[DocData];
-    // for (const key of Object.keys(dataDoc)) {
-    //     // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-    //     const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
-    //     key.startsWith('acl') && distributeAcls(key, permission, child);
-    // }
+    const dataDoc = parent[DocData];
+    for (const key of Object.keys(dataDoc)) {
+        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+        // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+        const symbol = ReverseHierarchyMap.get(StrCast(dataDoc[key]))
+        if (symbol){
+            const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
+            key.startsWith('acl') && distributeAcls(key, sharePermission, child)
+        }
+    }
 }
 
 /**
-- 
cgit v1.2.3-70-g09d2


From 763b77af1127f6dd0313d4d5e13cbb6f7e6be211 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 21 Jun 2023 18:00:13 -0400
Subject: uses acl from container for layout functions

---
 src/client/views/DocumentDecorations.tsx | 13 ++++++-------
 src/fields/util.ts                       |  6 +++---
 2 files changed, 9 insertions(+), 10 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index c4c6b6488..db164a33a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -164,9 +164,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onContainerDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
-        console.log(GetEffectiveLayoutAcl(first))
-        if (effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment) {
+        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -179,8 +178,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
-        if (effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment) {
+        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -200,8 +199,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
-        if (effectiveAcl != AclAdmin && effectiveAcl != AclEdit && effectiveAcl != AclAugment){
+        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment){
             return false;
         }      
 const dragDocView = SelectionManager.Views()[0];
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 9fb65c251..f31213869 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -17,7 +17,6 @@ import { RefField } from './RefField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
-import { GroupManager } from '../client/util/GroupManager';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -181,12 +180,13 @@ export function GetEffectiveAcl(target: any, user?: string): symbol {
 }
 
 /**
- * Calculates the effective access layout right to a document for the current user.
+ * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
  */
 export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
-    return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
+    // if (getEffectiveAclCache(target, user)) return getEffectiveAclCache(target, user);
+    return getEffectiveAclCache(Doc.GetProto(target['embedContainer']), user);
 }
 
 function getPropAcl(target: any, prop: string | symbol | number) {
-- 
cgit v1.2.3-70-g09d2


From 85522d4755caa68f1aa6083756d32854ab5a3008 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 22 Jun 2023 10:17:30 -0400
Subject: fixed resize to be layout acl rather than regular

---
 src/client/views/DocumentDecorations.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index db164a33a..19ab98cea 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -495,7 +495,7 @@ const dragDocView = SelectionManager.Views()[0];
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
+        const effectiveAcl = GetEffectiveLayoutAcl(first.rootDoc);
         if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
         if (!first) return false;
         let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
-- 
cgit v1.2.3-70-g09d2


From c258f2ec493ebb84c6f8b20a723ed8df001e3927 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 10:38:14 -0400
Subject: layout acl updates - SharingManager works

---
 src/client/util/SharingManager.tsx       | 60 +++++++++++++++++++++++++++-----
 src/client/views/DocumentDecorations.tsx |  2 +-
 src/fields/util.ts                       | 51 ++++++++++++++++++++++++---
 3 files changed, 99 insertions(+), 14 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a95874fd7..59a88b404 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -6,11 +6,11 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
-import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
+import { Doc, DocCastAsync, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
-import { NumCast, StrCast } from '../../fields/Types';
+import { DocCast, NumCast, StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
@@ -24,6 +24,7 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
+import { Docs } from '../documents/Documents';
 
 export interface User {
     email: string;
@@ -199,7 +200,7 @@ export class SharingManager extends React.Component<{}> {
     setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
-        const acl = `acl-${key}`;
+        let acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         // setting the same acl for a docs within the doc being shared if they haven't been set yet
@@ -213,6 +214,9 @@ export class SharingManager extends React.Component<{}> {
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
+        if (acl == 'acl-Public' && this.layoutDocAcls){
+            acl = 'acl-Public-layout';
+        }
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
@@ -511,7 +515,7 @@ export class SharingManager extends React.Component<{}> {
             docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin);
         }
 
-        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+        const targetDoc: Doc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
         const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
@@ -527,7 +531,24 @@ export class SharingManager extends React.Component<{}> {
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
-                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]);
+                if (this.layoutDocAcls){
+                    if (targetDoc[DocAcl][userKey]){
+                        permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
+                    }
+                    else{
+                        if (targetDoc['embedContainer']){
+                            permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+                        }
+                        else{
+                            permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
+                        }
+                    }
+                }
+                else{
+                    permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                }
                 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -579,12 +600,35 @@ export class SharingManager extends React.Component<{}> {
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
-            const groupKey = `acl-${StrCast(group.title)}`;
+            let groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs
                 .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
                 .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
-            
+            // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+            let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
+                if (this.layoutDocAcls){
+                    if (groupKey == 'acl-Public'){
+                        groupKey = 'acl-Public-layout'
+                    }
+                    if (targetDoc[DocAcl][groupKey]){
+                        permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
+                    }
+                    else{
+                        if (groupKey == 'acl-Public-layout'){
+                            groupKey = 'acl-Public'
+                        }
+                        if (targetDoc['embedContainer']){
+                            permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
+                        }
+                        else{
+                            permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
+                        }
+                    }
+                }
+                else{
+                    permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
+                }
+
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
                     <div className={'padding'}>{StrCast(group.title)}</div>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 19ab98cea..b9fef5cf8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -203,7 +203,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment){
             return false;
         }      
-const dragDocView = SelectionManager.Views()[0];
+        const dragDocView = SelectionManager.Views()[0];
         const containers = new Set<Doc | undefined>();
         SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer)));
         if (containers.size > 1) return false;
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 1ac450b9a..f9090e977 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -170,6 +170,11 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
     return getEffectiveAcl(target, user);
 }, true);
 
+// return layout acl from cache or chache the acl and return.
+const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) {
+    return getEffectiveLayoutAcl(target, user);
+    }, true);
+
 /**
  * Calculates the effective access right to a document for the current user.
  */
@@ -180,14 +185,13 @@ export function GetEffectiveAcl(target: any, user?: string): symbol {
 }
 
 /**
- * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
- */
+* Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
+*/
 export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
-    // if (getEffectiveAclCache(target, user)) return getEffectiveAclCache(target, user);
-    return getEffectiveAclCache(Doc.GetProto(target['embedContainer']), user);
-}
+    return getEffectiveLayoutAclCache(target, user);
+    }
 
 function getPropAcl(target: any, prop: string | symbol | number) {
     if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
@@ -230,6 +234,43 @@ function getEffectiveAcl(target: any, user?: string): symbol {
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
     return AclAdmin;
 }
+
+/**
+* Returns the layout acl that is effective on the document passed through as the target. If no layout acls
+* have been set, it returns the regular acls for the document target is contained in.
+*/
+function getEffectiveLayoutAcl(target: any, user?: string): symbol {
+    const targetAcls = target[DocAcl];
+
+    const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
+    if (targetAcls && Object.keys(targetAcls).length) {
+        var effectiveAcl;
+        for (const [key, value] of Object.entries(targetAcls)) {
+            const entity = denormalizeEmail(key.substring(4)); // an individual or a group
+            if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') {
+                if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
+                    effectiveAcl = value as symbol;
+                }
+                else{
+                    effectiveAcl = value as symbol;
+                }
+            }
+        }
+
+        if (effectiveAcl){
+            return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
+        }
+        else{
+            return GetEffectiveAcl(Doc.GetProto(target['embedContainer']), user);
+        }
+    }
+    // authored documents are private until an ACL is set.
+    const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
+    if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
+        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)
-- 
cgit v1.2.3-70-g09d2


From 06c8f315ff25c1298ce6a6e712027cd57a73e1e3 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 18:52:47 -0400
Subject: DocumentDecorations layout acl checkbox

---
 src/client/views/DocumentDecorations.scss | 19 +++++++++++++++++++
 src/client/views/DocumentDecorations.tsx  | 26 +++++++++++++-------------
 2 files changed, 32 insertions(+), 13 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 0a690f751..d1b6c5772 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -228,6 +228,25 @@ $resizeHandler: 8px;
         &:hover {
             opacity: 1;
         }
+
+        .checkbox{
+            position: fixed;
+            display: inline;
+            margin-top: -2px;
+            margin-left: -5px;
+            font-size: 7px;
+
+            .checkbox-box{
+                transform: scale(.7);
+            }
+
+            .checkbox-text{
+                font-size: 7px;
+                margin-top: 10px;
+                margin-left: -20px;
+            }
+        }
+
         .documentDecorations-shareNone{
             width: calc(100% + 10px);
             background: grey;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b9fef5cf8..3b51f8c61 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -65,6 +65,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @observable private _isRotating: boolean = false;
     @observable private _isRounding: boolean = false;
     @observable private _isResizing: boolean = false;
+    @observable private showLayoutAcl: boolean = false;
 
     constructor(props: any) {
         super(props);
@@ -764,7 +765,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const docShareMode = HierarchyMapping.get(GetEffectiveAcl(seldocview.rootDoc))!.name
+        const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc);
+        const docShareMode = HierarchyMapping.get(acl)!.name
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
 
@@ -824,19 +826,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
 
         const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' 
-                 onPointerDown={e => 
-                    setupMoveUpEvents(
-                        this, 
-                        e, 
-                        e => this.onBackgroundMove(true, e), 
-                        returnFalse, 
-                        action(() => SettingsManager.propertiesWidth =250)
-                    )
-                }>
+            <div className='documentDecorations-share' >
                 <div className={`documentDecorations-share${shareMode}`}>
                     &nbsp;
                     <span>{shareSymbolIcon + ' ' + shareMode}</span>
+                    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
+                    <div className='checkbox'>
+                        <div className='checkbox-box'>
+                            <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> 
+                        </div>
+                        <div className='checkbox-text'> Layout </div>
+                    </div>
                     &nbsp;
                 </div>
             </div>
@@ -858,8 +858,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 onPointerDown={e => e.stopPropagation()}
             />
         ) : (
-            <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}>
-                <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
+            <div className="documentDecorations-title" key="title" onPointerDown={e => {e.stopPropagation}}>
+                <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
                 {sharingMenu}
                 {!useLock ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
-- 
cgit v1.2.3-70-g09d2


From b0fa926fdb99e9baae9a76317ef58ba9e8e52886 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Tue, 27 Jun 2023 17:54:08 -0400
Subject: DocumentDecorations UI fix for layout checkbox and when theres no
 title

---
 src/client/views/DocumentDecorations.scss | 21 +++++++++++----------
 src/client/views/DocumentDecorations.tsx  | 10 +++++-----
 2 files changed, 16 insertions(+), 15 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index d1b6c5772..4f23681ac 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -221,29 +221,30 @@ $resizeHandler: 8px;
         pointer-events: auto;
         min-width: fit-content;
         text-align: center;
-        width: 125px;
         display: flex;
         height: 21px;
         opacity: 0.3;
         &:hover {
             opacity: 1;
         }
+        
 
         .checkbox{
-            position: fixed;
             display: inline;
-            margin-top: -2px;
-            margin-left: -5px;
-            font-size: 7px;
 
             .checkbox-box{
-                transform: scale(.7);
+                display: inline;
+                position: relative;
+                top: -2.5;
+                left: 35;
+                zoom: .7;
             }
 
-            .checkbox-text{
-                font-size: 7px;
-                margin-top: 10px;
-                margin-left: -20px;
+            & .checkbox-text{
+                display: inline;
+                position: relative;
+                top: 2;
+                font-size: 8px;
             }
         }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 3b51f8c61..4da6bd697 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -829,8 +829,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             <div className='documentDecorations-share' >
                 <div className={`documentDecorations-share${shareMode}`}>
                     &nbsp;
-                    <span>{shareSymbolIcon + ' ' + shareMode}</span>
-                    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
+                    {shareSymbolIcon + ' ' + shareMode}
+                    &nbsp;
                     <div className='checkbox'>
                         <div className='checkbox-box'>
                             <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> 
@@ -843,7 +843,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         ) : (
             <div />
         );
-
+        
         const titleArea = this._editingTitle ? (
             <input
                 ref={this._keyinput}
@@ -859,7 +859,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             />
         ) : (
             <div className="documentDecorations-title" key="title" onPointerDown={e => {e.stopPropagation}}>
-                <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>{`${hideTitle ? '' : this.selectionTitle}`}</span>
+                { hideTitle ? null : <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>{this.selectionTitle}</span>}
                 {sharingMenu}
                 {!useLock ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
@@ -903,7 +903,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             <div className="documentDecorations-topbar" style={{ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined }} onPointerDown={this.onContainerDown}>
                                 {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')}
                                 {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')}
-                                {hideTitle ? null : titleArea}
+                                {titleArea}
                                 {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
                             </div>
                             {hideResizers ? null : (
-- 
cgit v1.2.3-70-g09d2


From 7567210b6e0dce1e5bc79255dc2dd4debe55ed82 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 28 Jun 2023 10:34:28 -0400
Subject: DocumentDecorations layout checkbox only in delevolper mode

---
 src/client/views/DocumentDecorations.scss |  2 +-
 src/client/views/DocumentDecorations.tsx  | 12 +++++++-----
 2 files changed, 8 insertions(+), 6 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index 4f23681ac..ca3610cc0 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -243,7 +243,7 @@ $resizeHandler: 8px;
             & .checkbox-text{
                 display: inline;
                 position: relative;
-                top: 2;
+                top: 1.5;
                 font-size: 8px;
             }
         }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4da6bd697..d306d3e45 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -831,12 +831,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                     &nbsp;
                     {shareSymbolIcon + ' ' + shareMode}
                     &nbsp;
-                    <div className='checkbox'>
-                        <div className='checkbox-box'>
-                            <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> 
+                    {!Doc.noviceMode ? 
+                        <div className='checkbox'>
+                            <div className='checkbox-box'>
+                                <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> 
+                            </div>
+                            <div className='checkbox-text'> Layout </div>
                         </div>
-                        <div className='checkbox-text'> Layout </div>
-                    </div>
+                    : null }
                     &nbsp;
                 </div>
             </div>
-- 
cgit v1.2.3-70-g09d2


From 45e36e459f34a16d8c8f7ae11685d50018e379ab Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 30 Jun 2023 12:09:04 -0400
Subject: fixed inherit parent acls to update shared docs list

---
 src/client/views/DocComponent.tsx                  | 35 +++++++++++++++++-----
 src/client/views/DocumentDecorations.tsx           |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  2 --
 src/fields/util.ts                                 |  7 ++---
 4 files changed, 31 insertions(+), 15 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 6f350e0fc..aa8b145be 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -3,8 +3,8 @@ import { DateField } from '../../fields/DateField';
 import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast, ScriptCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { Cast, DocCast, ScriptCast, StrCast } from '../../fields/Types';
+import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
@@ -12,6 +12,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 import { UndoManager } from '../util/UndoManager';
 import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
+import { SharingManager } from '../util/SharingManager';
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
@@ -206,13 +207,22 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
-                        // if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                         doc.embedContainer = this.props.Document;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
+                        const parent = DocCast(doc.embedContainer);
+                        doc.embedContainer && inheritParentAcls(parent, doc);
+                        for (const key of Object.keys(parent)) {
+                            const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
+                            if (symbol && key.startsWith('acl')){
+                                const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
+                                const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
+                                if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
+                            }
+                        }
                     });
-                } else {
-                    added
+                    } else {
+                        added
                         .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
                         .map(doc => {
                             // only make a pushpin if we have acl's to edit the document
@@ -220,9 +230,18 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             doc._stayInCollection = undefined;
                             doc.embedContainer = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
-
-                            // Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
-                        });
+                            const parent = DocCast(doc.embedContainer);
+                            doc.embedContainer && inheritParentAcls(parent, doc);
+                            for (const key of Object.keys(Doc.GetProto(parent))) {
+                                const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
+                                if (symbol && key.startsWith('acl')){
+                                    const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
+                                    const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
+                                    if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
+                                }
+                            }
+                    });
+                    
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added);
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index d306d3e45..964aada53 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -772,7 +772,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
-        const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+        const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 215e9c343..14f03044e 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -556,8 +556,6 @@ export class CollectionDockingView extends CollectionSubView() {
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
                         this.props.Document.isShared && inheritParentAcls(Doc.GetProto(this.props.Document), Doc.GetProto(docToAdd));
-                        console.log(Doc.GetProto(this.props.Document))
-                        console.log(Doc.GetProto(docToAdd))
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f9090e977..d9ccb528b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -129,11 +129,10 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
-    const dataDoc = parent[DocData];
-    for (const key of Object.keys(dataDoc)) {
+    for (const key of Object.keys(parent)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
-        const symbol = ReverseHierarchyMap.get(StrCast(dataDoc[key]))
+        // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
+        const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
         if (symbol){
             const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
             key.startsWith('acl') && distributeAcls(key, sharePermission, child)
-- 
cgit v1.2.3-70-g09d2


From 5a9af979cc293d0e3843270ee053c24bf0eb6ef5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 5 Jul 2023 16:15:27 -0400
Subject: changed acl inheritance for docking views.

---
 src/client/documents/Documents.ts                  | 10 ++--
 src/client/util/LinkFollower.ts                    |  2 +-
 src/client/util/SharingManager.tsx                 | 70 ++++++++++------------
 src/client/views/DocComponent.tsx                  | 30 +++++-----
 src/client/views/DocumentDecorations.tsx           | 40 ++++++++-----
 .../views/collections/CollectionDockingView.tsx    | 12 ++--
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.tsx          |  8 +--
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 src/client/views/nodes/ComparisonBox.tsx           |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    | 10 ++--
 src/fields/Doc.ts                                  | 11 +++-
 src/fields/util.ts                                 | 33 +++++-----
 15 files changed, 123 insertions(+), 113 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 06ff95df0..18e8b5940 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -859,7 +859,7 @@ export namespace Docs {
 
             // users placeholderDoc as proto if it exists
             const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
-            
+
             if (placeholderDoc) {
                 dataDoc.proto = proto;
             }
@@ -1053,7 +1053,7 @@ export namespace Docs {
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _type_collection: CollectionViewType.Freeform }, id);
-            documents.forEach(d => (d.embedContainer = inst));
+            documents.forEach(d => Doc.SetContainer(d, inst));
             return inst;
         }
 
@@ -1158,7 +1158,9 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
+            const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id);
+            documents.map(c => Doc.SetContainer(c, ret));
+            return ret;
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -1186,7 +1188,7 @@ export namespace Docs {
                 options,
                 id
             );
-            configs.map(c => (c.doc.embedContainer = doc));
+            configs.map(c => Doc.SetContainer(c.doc, doc));
             return doc;
         }
 
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index f74409e42..3e526c4c0 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -103,7 +103,7 @@ export class LinkFollower {
                         Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target);
                         movedTarget = true;
                     }
-                    target.embedContainer = sourceDocParent;
+                    Doc.SetContainer(target, sourceDocParent);
                     const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
                     if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
                         target.x = moveTo[0];
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 534a9cfde..64b2bb5b8 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,12 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton, Size } from 'browndash-components';
 import { concat, intersection } from 'lodash';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
-import { Doc, DocCastAsync, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
+import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
@@ -24,7 +23,6 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
-import { Docs } from '../documents/Documents';
 
 export interface User {
     email: string;
@@ -166,7 +164,7 @@ export class SharingManager extends React.Component<{}> {
         // or if the 'Override Nested' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overrideNested || doc[acl]==undefined){
+            if (this.overrideNested || doc[acl] == undefined) {
                 this.setInternalSharing(recipient, permission, doc);
             }
         });
@@ -207,7 +205,7 @@ export class SharingManager extends React.Component<{}> {
         // or if the 'Override Private' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overrideNested || doc[acl]==undefined){
+            if (this.overrideNested || doc[acl] == undefined) {
                 this.setInternalGroupSharing(group, permission, doc);
             }
         });
@@ -288,7 +286,7 @@ export class SharingManager extends React.Component<{}> {
                     this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
                 }
                 if (GetEffectiveAcl(doc) === AclAdmin) {
-                    if ( shareWith == 'Public' && layout) shareWith = 'Public-layout';
+                    if (shareWith == 'Public' && layout) shareWith = 'Public-layout';
                     distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
                 }
                 this.setDashboardBackground(doc, permission as SharingPermissions);
@@ -538,13 +536,12 @@ export class SharingManager extends React.Component<{}> {
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
                 let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]);
-                if (this.layoutDocAcls){
+                if (this.layoutDocAcls) {
                     if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-                    else if (targetDoc['embedContainer'])  permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+                    else if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
                     else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
-                }
-                else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
-                
+                } else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
                         <span className={'padding'}>{user.email}</span>
@@ -570,12 +567,11 @@ export class SharingManager extends React.Component<{}> {
         // the owner of the doc and the current user are placed at the top of the user list.
         const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`;
         var curUserPermission;
-            if (this.layoutDocAcls){
-                if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-                else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-                else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]);
-            }
-            else curUserPermission = StrCast(targetDoc[userKey]);
+        if (this.layoutDocAcls) {
+            if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
+            else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+            else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]);
+        } else curUserPermission = StrCast(targetDoc[userKey]);
         // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
@@ -609,16 +605,15 @@ export class SharingManager extends React.Component<{}> {
                 .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
             // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
             let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
-                if (this.layoutDocAcls){
-                    if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
-                    if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
-                    else{
-                        if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
-                        if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
-                        else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
-                    }
+            if (this.layoutDocAcls) {
+                if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
+                if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
+                else {
+                    if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
+                    if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
+                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
                 }
-                else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
+            } else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
@@ -629,7 +624,7 @@ export class SharingManager extends React.Component<{}> {
                             <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                         </div>
                     ) : null}
-                    <div className={"edit-actions"}>
+                    <div className={'edit-actions'}>
                         {admin || this.myDocAcls ? (
                             <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
                                 {this.sharingOptions(uniform, group.title === 'Override')}
@@ -649,16 +644,15 @@ export class SharingManager extends React.Component<{}> {
                 {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
                 <div className="sharing-contents">
                     <p className="share-title">
-                        <div className="share-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')} >
-                            <FontAwesomeIcon icon={'question-circle'} size={'sm'} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')}/>
+                        <div className="share-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')}>
+                            <FontAwesomeIcon icon={'question-circle'} size={'sm'} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')} />
                         </div>
                         <b>Share </b>
                         {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
                     </p>
                     <button className="share-copy-link" onClick={this.copyURL}>
-                        <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/>
-                        &nbsp;
-                        Copy Public URL
+                        <FontAwesomeIcon title={'Copy Public URL'} icon={'copy'} size={'sm'} onClick={this.copyURL} />
+                        &nbsp; Copy Public URL
                     </button>
                     <div className={'close-button'} onClick={this.close}>
                         <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
@@ -682,7 +676,7 @@ export class SharingManager extends React.Component<{}> {
                                         }),
                                     }}
                                 />
-                                <div className='permissions-select'>
+                                <div className="permissions-select">
                                     <select className={`permissions-dropdown-${this.permissions}`} onChange={this.handlePermissionsChange} value={this.permissions}>
                                         {this.sharingOptions(true)}
                                     </select>
@@ -706,9 +700,9 @@ export class SharingManager extends React.Component<{}> {
                             </div>
                         </div>
                     ) : (
-                        <div className='share-container'>
-                            <div className='acl-container'>
-                                <div className='layoutDoc-acls'>
+                        <div className="share-container">
+                            <div className="acl-container">
+                                <div className="layoutDoc-acls">
                                     <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                 </div>
                             </div>
@@ -717,7 +711,7 @@ export class SharingManager extends React.Component<{}> {
                     <div className="main-container">
                         <div className={'individual-container'}>
                             <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
-                                <div className='title-individual'>
+                                <div className="title-individual">
                                     Individuals &nbsp;
                                     {this.individualSort === 'ascending' ? (
                                         <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
@@ -732,7 +726,7 @@ export class SharingManager extends React.Component<{}> {
                         </div>
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
-                                <div className='title-group'>
+                                <div className="title-group">
                                     Groups &nbsp;
                                     <div className="group-info" onClick={action(() => GroupManager.Instance?.open())}>
                                         <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index f3aa8451a..1d0feec74 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -196,52 +196,52 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                 aclKeys.forEach(key =>
                     added.forEach(d => {
-                        if (key != 'acl-Me'){
-                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
-                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
-                            const permission = HierarchyMapping.get(permissionSymbol)!.name
-                            distributeAcls(key, permission, Doc.GetProto(d))
+                        if (key != 'acl-Me') {
+                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
+                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl;
+                            const permission = HierarchyMapping.get(permissionSymbol)!.name;
+                            distributeAcls(key, permission, Doc.GetProto(d));
                         }
                     })
                 );
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
-                        doc.embedContainer = this.props.Document;
+                        Doc.SetContainer(doc, this.props.Document);
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         const parent = DocCast(doc.embedContainer);
                         doc.embedContainer && inheritParentAcls(parent, doc);
                         for (const key of Object.keys(parent)) {
-                            const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
-                            if (symbol && key.startsWith('acl')){
+                            const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
+                            if (symbol && key.startsWith('acl')) {
                                 const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
                                 const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
                                 if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
                             }
                         }
                     });
-                    } else {
-                        added
+                } else {
+                    added
                         .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
                         .map(doc => {
                             // only make a pushpin if we have acl's to edit the document
                             //DocUtils.LeavePushpin(doc);
                             doc._dragOnlyWithinContainer = undefined;
-                            doc.embedContainer = this.props.Document;
+                            Doc.SetContainer(doc, this.props.Document);
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
                             const parent = DocCast(doc.embedContainer);
                             doc.embedContainer && inheritParentAcls(parent, doc);
                             for (const key of Object.keys(Doc.GetProto(parent))) {
-                                const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
-                                if (symbol && key.startsWith('acl')){
+                                const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
+                                if (symbol && key.startsWith('acl')) {
                                     const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
                                     const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
                                     if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
                                 }
                             }
-                    });
-                    
+                        });
+
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add)));
                     else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4454a3ec1..cdd9e62d8 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -201,9 +201,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
         const first = SelectionManager.Views()[0];
         const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
-        if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment){
+        if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) {
             return false;
-        }      
+        }
         const dragDocView = SelectionManager.Views()[0];
         const containers = new Set<Doc | undefined>();
         SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer)));
@@ -766,13 +766,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
         // sharing
         const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc);
-        const docShareMode = HierarchyMapping.get(acl)!.name
+        const docShareMode = HierarchyMapping.get(acl)!.name;
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
 
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
-        const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+        const hideResizers =
+            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
@@ -826,26 +827,26 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
 
         const sharingMenu = docShareMode ? (
-            <div className='documentDecorations-share' >
+            <div className="documentDecorations-share">
                 <div className={`documentDecorations-share${shareMode}`}>
                     &nbsp;
                     {shareSymbolIcon + ' ' + shareMode}
                     &nbsp;
-                    {!Doc.noviceMode ? 
-                        <div className='checkbox'>
-                            <div className='checkbox-box'>
-                                <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> 
+                    {!Doc.noviceMode ? (
+                        <div className="checkbox">
+                            <div className="checkbox-box">
+                                <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} />
                             </div>
-                            <div className='checkbox-text'> Layout </div>
+                            <div className="checkbox-text"> Layout </div>
                         </div>
-                    : null }
+                    ) : null}
                     &nbsp;
                 </div>
             </div>
         ) : (
             <div />
         );
-        
+
         const titleArea = this._editingTitle ? (
             <input
                 ref={this._keyinput}
@@ -860,8 +861,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 onPointerDown={e => e.stopPropagation()}
             />
         ) : (
-            <div className="documentDecorations-title" key="title" onPointerDown={e => {e.stopPropagation}}>
-                { hideTitle ? null : <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>{this.selectionTitle}</span>}
+            <div
+                className="documentDecorations-title"
+                key="title"
+                onPointerDown={e => {
+                    e.stopPropagation;
+                }}>
+                {hideTitle ? null : (
+                    <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>
+                        {this.selectionTitle}
+                    </span>
+                )}
                 {sharingMenu}
                 {!useLock ? null : (
                     <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top">
@@ -872,7 +882,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 )}
             </div>
         );
-        
+
         return (
             <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}>
                 <div
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 32fb4d8df..ce9eb9f17 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -461,7 +461,7 @@ export class CollectionDockingView extends CollectionSubView() {
             const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
             if (newtabdocs.length) {
                 Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
-                newtabdocs.forEach(ntab => (ntab.embedContainer = newtab));
+                newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
             }
             json = json.replace(origtab[Id], newtab[Id]);
             return newtab;
@@ -497,11 +497,11 @@ export class CollectionDockingView extends CollectionSubView() {
     tabCreated = (tab: any) => {
         const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
         aclKeys.forEach(key => {
-            if (key != 'acl-Me'){
-                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
-                const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
-                const permission = HierarchyMapping.get(permissionSymbol)!.name
-                distributeAcls(key, permission, Doc.GetProto(tab))
+            if (key != 'acl-Me') {
+                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
+                const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl;
+                const permission = HierarchyMapping.get(permissionSymbol)!.name;
+                distributeAcls(key, permission, Doc.GetProto(tab));
             }
         });
         this.tabMap.add(tab);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 4cd3885f5..d78a0e781 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -180,7 +180,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         const doAddDoc = (doc: Doc | Doc[]) =>
             (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => {
                 const res = flg && Doc.AddDocToList(this.doc[DocData], this.props.fieldKey, doc, relativeTo, before);
-                res && (doc.embedContainer = this.props.Document);
+                res && Doc.SetContainer(doc, this.props.Document);
                 return res;
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 4d780f46b..3473eee18 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -262,7 +262,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
             pinDoc.presDuration = pinDoc.presDuration ?? 1000;
             pinDoc.groupWithUp = false;
-            pinDoc.embedContainer = curPres;
+            Doc.SetContainer(pinDoc, curPres);
             // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
             pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
             pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 8d8d895c6..a2269075d 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -392,7 +392,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const innerAdd = (doc: Doc) => {
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
+                dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
                 return added;
             };
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -455,7 +455,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const innerAdd = (doc: Doc) => {
                         const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                        dataIsComputed && (doc.embedContainer = this.doc.embedContainer);
+                        dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
                         return added;
                     };
                     return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -563,7 +563,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && (doc.embedContainer = this.doc.embedContainer);
+                !dataIsComputed && added && Doc.SetContainer(doc, this.doc.embedContainer);
 
                 return added;
             };
@@ -1192,7 +1192,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                         TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
                         Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
                         newParent.treeViewOpen = true;
-                        child.embedContainer = treeView.Document;
+                        Doc.SetContainer(child, treeView.Document);
                     }
                 };
                 const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 9b0abc48b..764b1e08a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -392,7 +392,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.layout_fitWidth = true;
         newCollection['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
         selected.forEach(d => {
-            d.embedContainer = newCollection;
+            Doc.SetContainer(d, newCollection);
             d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
         });
         this.hideMarquee();
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 2290e0711..ca5ec9389 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -46,7 +46,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl
         if (dropEvent.complete.docDragData) {
             const droppedDocs = dropEvent.complete.docDragData?.droppedDocuments;
             const added = dropEvent.complete.docDragData.moveDocument?.(droppedDocs, this.rootDoc, (doc: Doc | Doc[]) => this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey));
-            droppedDocs.lastElement().embedContainer = this.dataDoc;
+            Doc.SetContainer(droppedDocs.lastElement(), this.dataDoc);
             !added && e.preventDefault();
             e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place
             return added;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 202a9f851..e5ea8e0c1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -564,7 +564,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     }
                     if (added) {
                         draggedDoc._freeform_fitContentsToBox = true;
-                        draggedDoc.embedContainer = this.rootDoc;
+                        Doc.SetContainer(draggedDoc, this.rootDoc);
                         const view = this._editorView!;
                         view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
                     }
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index d2273c91c..112a0d87e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { Schema } from 'prosemirror-model';
 import { splitListItem, wrapInList } from 'prosemirror-schema-list';
 import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
 import { liftTarget } from 'prosemirror-transform';
-import { AclAdmin, AclAugment, AclEdit} from '../../../../fields/DocSymbols';
+import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols';
 import { GetEffectiveAcl } from '../../../../fields/util';
 import { Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
@@ -50,8 +50,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         switch (GetEffectiveAcl(props.DataDoc)) {
             case AclAugment:
                 const prevNode = state.selection.$cursor.nodeBefore;
-                const prevUser = prevNode.marks[prevNode.marks.length-1].attrs.userid
-                if (prevUser != Doc.CurrentUserEmail){
+                const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid;
+                if (prevUser != Doc.CurrentUserEmail) {
                     return false;
                 }
         }
@@ -263,7 +263,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind('Backspace', (state: EditorState, dispatch: (tx: Transaction) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
-        
+
         if (
             !deleteSelection(state, (tx: Transaction) => {
                 dispatch(updateBullets(tx, schema));
@@ -334,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
-        if (GetEffectiveAcl(props.DataDoc)!=AclEdit && GetEffectiveAcl(props.DataDoc)!=AclAugment && GetEffectiveAcl(props.DataDoc)!=AclAdmin) return true;
+        if (GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 99712fb04..9c138d348 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -20,7 +20,6 @@ import {
     AclEdit,
     AclPrivate,
     AclReadonly,
-    AclSelfEdit,
     AclUnset,
     Animation,
     CachedUpdates,
@@ -138,7 +137,7 @@ export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermis
     [AclAdmin,    { level: aclLevel.admin,        name: SharingPermissions.Admin,   image: '⬢' }],
     [AclUnset,    { level: aclLevel.unset,        name: SharingPermissions.Unset,   image: '▲' }],
 ]);
-export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol ; image: string}> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
+export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol; image: string }> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
 
 // caches the document access permissions for the current user.
 // this recursively updates all protos as well.
@@ -478,6 +477,14 @@ export namespace Doc {
     //     });
     // }
 
+    export function SetContainer(doc: Doc, container: Doc) {
+        doc.embedContainer = container;
+        if (Doc.GetProto(container).author === doc.author) {
+            Object.keys(Doc.GetProto(container))
+                .filter(key => key.startsWith('acl'))
+                .forEach(key => (doc[key] = Doc.GetProto(container)[key]));
+        }
+    }
     export function RunCachedUpdate(doc: Doc, field: string) {
         const update = doc[CachedUpdates][field];
         if (update) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 0f613d926..48de36efe 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -131,10 +131,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
     for (const key of Object.keys(parent)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
         // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
-        const symbol = ReverseHierarchyMap.get(StrCast(parent[key]))
-        if (symbol){
+        const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
+        if (symbol) {
             const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-            key.startsWith('acl') && distributeAcls(key, sharePermission, child)
+            key.startsWith('acl') && distributeAcls(key, sharePermission, child);
         }
     }
 }
@@ -171,7 +171,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
 // return layout acl from cache or chache the acl and return.
 const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) {
     return getEffectiveLayoutAcl(target, user);
-    }, true);
+}, true);
 
 /**
  * Calculates the effective access right to a document for the current user.
@@ -183,13 +183,13 @@ export function GetEffectiveAcl(target: any, user?: string): symbol {
 }
 
 /**
-* Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
-*/
+ * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
+ */
 export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
     return getEffectiveLayoutAclCache(target, user);
-    }
+}
 
 function getPropAcl(target: any, prop: string | symbol | number) {
     if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
@@ -234,9 +234,9 @@ function getEffectiveAcl(target: any, user?: string): symbol {
 }
 
 /**
-* Returns the layout acl that is effective on the document passed through as the target. If no layout acls
-* have been set, it returns the regular acls for the document target is contained in.
-*/
+ * Returns the layout acl that is effective on the document passed through as the target. If no layout acls
+ * have been set, it returns the regular acls for the document target is contained in.
+ */
 function getEffectiveLayoutAcl(target: any, user?: string): symbol {
     const targetAcls = target[DocAcl];
 
@@ -248,27 +248,24 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol {
             if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') {
                 if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
                     effectiveAcl = value as symbol;
-                }
-                else{
+                } else {
                     effectiveAcl = value as symbol;
                 }
             }
         }
 
-        if (effectiveAcl){
+        if (effectiveAcl) {
             return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
-        }
-        else{
+        } else {
             return GetEffectiveAcl(Doc.GetProto(target['embedContainer']), user);
         }
     }
     // authored documents are private until an ACL is set.
     const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author
     if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
-        return AclAdmin;
+    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)
@@ -286,7 +283,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
         }
-        return;
+        //return;
     }
     visited.push(target);
 
-- 
cgit v1.2.3-70-g09d2


From fa38dbe06d6ddb5f4499b759459a24d2b3c111e8 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Jul 2023 13:36:21 -0400
Subject: a bunch of fixes to simplify collaboration and make it work better.

---
 src/client/DocServer.ts                            |  12 +-
 src/client/util/SharingManager.tsx                 | 203 +++++----------------
 src/client/views/DashboardView.scss                | 139 +++++++-------
 src/client/views/DashboardView.tsx                 |  18 +-
 src/client/views/DocComponent.tsx                  |  61 ++-----
 src/client/views/DocumentDecorations.tsx           |  29 ++-
 src/client/views/GlobalKeyHandler.ts               |  12 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/PropertiesView.tsx                |  47 ++---
 .../views/collections/CollectionDockingView.tsx    |  17 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 src/client/views/nodes/DocumentView.tsx            |   2 +-
 src/fields/Doc.ts                                  |   4 +-
 src/fields/util.ts                                 |  77 ++------
 14 files changed, 231 insertions(+), 400 deletions(-)

(limited to 'src/client/views/DocumentDecorations.tsx')

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2a7f5a09b..876f2400d 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -76,10 +76,14 @@ export namespace DocServer {
     const fieldWriteModes: { [field: string]: WriteMode } = {};
     const docsWithUpdates: { [field: string]: Set<Doc> } = {};
 
-    export var PlaygroundFields: string[];
-    export function setPlaygroundFields(livePlaygroundFields: string[]) {
-        DocServer.PlaygroundFields = livePlaygroundFields;
-        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
+    export var PlaygroundFields: string[] = [];
+    export function setLivePlaygroundFields(livePlaygroundFields: string[]) {
+        DocServer.PlaygroundFields.push(...livePlaygroundFields);
+        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+    }
+    export function setPlaygroundFields(playgroundFields: string[]) {
+        DocServer.PlaygroundFields.push(...playgroundFields);
+        playgroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
     }
     export function IsPlaygroundField(field: string) {
         return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 64b2bb5b8..d3241009a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -6,10 +6,9 @@ import * as React from 'react';
 import Select from 'react-select';
 import * as RequestPromise from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
-import { List } from '../../fields/List';
-import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
@@ -73,13 +72,11 @@ export class SharingManager extends React.Component<{}> {
     @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
-    private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute 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)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-    @observable private overrideNested: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -110,7 +107,6 @@ export class SharingManager extends React.Component<{}> {
             500
         );
         this.layoutDocAcls = false;
-        this.overrideNested = false;
     });
 
     constructor(props: {}) {
@@ -153,41 +149,19 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Shares the document with a user.
      */
-    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
+    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => {
         const { user, sharingDoc } = recipient;
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
-        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Nested' checkbox is selected
-        var childDocs = DocListCast(target.data);
-        childDocs.map(doc => {
-            if (this.overrideNested || doc[acl] == undefined) {
-                this.setInternalSharing(recipient, permission, doc);
-            }
-        });
-
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
-        // ! ensures it returns true if document has been shared successfully, false otherwise
-        return !docs
-            .map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc)))
-            .map(doc => {
-                doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+            doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined);
 
-                if (permission === SharingPermissions.None) {
-                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
-                } else {
-                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
-                }
-                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-                this.setDashboardBackground(doc, permission as SharingPermissions);
-                if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-                return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
-            })
-            .some(success => !success);
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+            if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
+            else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
+        });
     };
 
     /**
@@ -199,51 +173,24 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         let acl = `acl-${key}`;
-        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
-        // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override Private' checkbox is selected
-        var childDocs = DocListCast(target.data);
-        childDocs.map(doc => {
-            if (this.overrideNested || doc[acl] == undefined) {
-                this.setInternalGroupSharing(group, permission, doc);
-            }
-        });
-
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
-        if (acl == 'acl-Public' && this.layoutDocAcls) acl = 'acl-Public-layout';
-        // ! ensures it returns true if document has been shared successfully, false otherwise
-        return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
-            .map(doc => {
-                doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-                if (permission === SharingPermissions.None) {
-                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
-                } else {
-                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
-                }
 
-                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-                this.setDashboardBackground(doc, permission as SharingPermissions);
+        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+            doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined);
 
-                if (group instanceof Doc) {
-                    const members: string[] = JSON.parse(StrCast(group.members));
-                    const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
 
-                    // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
-                    group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc]));
+            if (group instanceof Doc) {
+                Doc.AddDocToList(group, 'docsShared', doc);
 
-                    return users
-                        .map(({ user, sharingDoc }) => {
-                            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
-                        })
-                        .some(success => !success);
-                }
-            })
-            .some(success => success);
+                this.users
+                    .filter(({ user: { email } }) => JSON.parse(StrCast(group.members)).includes(email))
+                    .forEach(({ user, sharingDoc }) => {
+                        if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+                        else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
+                    });
+            }
+        });
     };
 
     /**
@@ -279,44 +226,15 @@ export class SharingManager extends React.Component<{}> {
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
         } else {
-            const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
-                const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overrideNested) {
-                    this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
-                }
                 if (GetEffectiveAcl(doc) === AclAdmin) {
-                    if (shareWith == 'Public' && layout) shareWith = 'Public-layout';
-                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined);
                 }
-                this.setDashboardBackground(doc, permission as SharingPermissions);
             });
         }
         this.layoutDocAcls = false;
     };
 
-    /**
-     * Sets the background of the Dashboard if it has been shared as a visual indicator
-     */
-    setDashboardBackground = (doc: Doc, permission: SharingPermissions) => {
-        if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
-            if (permission !== SharingPermissions.None) {
-                doc.isShared = true;
-                doc.backgroundColor = 'green';
-            } else {
-                const acls = doc[DocData][DocAcl];
-                if (
-                    Object.keys(acls)
-                        .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && key !== 'acl-Me')
-                        .every(key => [AclUnset, AclPrivate].includes(acls[key]))
-                ) {
-                    doc.isShared = undefined;
-                    doc.backgroundColor = undefined;
-                }
-            }
-        }
-    };
-
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
      * @param group
@@ -341,12 +259,9 @@ export class SharingManager extends React.Component<{}> {
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
-            const dashboards = DocListCast(Doc.MyDashboards.data);
             DocListCast(group.docsShared).forEach(doc => {
                 const acl = `acl-${StrCast(group.title)}`;
-                const isDashboard = dashboards.indexOf(doc) !== -1;
-                distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard);
-                distributeAcls(acl, SharingPermissions.None, Doc.GetProto(doc), undefined, undefined, isDashboard);
+                distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined);
 
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -440,7 +355,7 @@ export class SharingManager extends React.Component<{}> {
         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);
+                    this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions, undefined);
                 } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
@@ -457,7 +372,6 @@ export class SharingManager extends React.Component<{}> {
             );
 
             this.layoutDocAcls = false;
-            this.overrideNested = false;
             this.selectedUsers = null;
         }
     };
@@ -509,8 +423,7 @@ export class SharingManager extends React.Component<{}> {
         const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
         const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
-        // handles the case where multiple documents are selected
-        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DocData]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DocData]));
+        let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
@@ -518,36 +431,32 @@ export class SharingManager extends React.Component<{}> {
             docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin);
         }
 
-        const targetDoc: Doc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+        const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
 
         // tslint:disable-next-line: no-unnecessary-callback-wrapper
         const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
         const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
+        const commonKeys = intersection(docs).reduce((list, doc) => (doc?.[DocAcl] ? [...list, ...Object.keys(doc[DocAcl])] : list), [] as string[]);
 
         // the list of users shared with
-        const userListContents: (JSX.Element | null)[] = users
+        const userListContents = users
             // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
             .filter(({ user }) => docs[0]?.author !== user.email)
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
                 const userKey = `acl-${normalizeEmail(user.email)}`;
-                const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
+                const uniform = docs.every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
-                let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]);
-                if (this.layoutDocAcls) {
-                    if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-                    else if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
-                } else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                let permissions = targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(targetDoc[userKey]);
+                permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
                         <span className={'padding'}>{user.email}</span>
                         <div className="edit-actions">
                             {admin || this.myDocAcls ? (
-                                <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}>
+                                <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value, undefined)}>
                                     {this.sharingOptions(uniform)}
                                 </select>
                             ) : (
@@ -566,12 +475,7 @@ export class SharingManager extends React.Component<{}> {
 
         // the owner of the doc and the current user are placed at the top of the user list.
         const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`;
-        var curUserPermission;
-        if (this.layoutDocAcls) {
-            if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
-            else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
-            else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]);
-        } else curUserPermission = StrCast(targetDoc[userKey]);
+        const curUserPermission = StrCast(targetDoc[userKey]);
         // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
@@ -600,20 +504,8 @@ export class SharingManager extends React.Component<{}> {
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             let groupKey = `acl-${StrCast(group.title)}`;
-            const uniform = docs
-                .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
-                .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
-            // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
-            let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]);
-            if (this.layoutDocAcls) {
-                if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout';
-                if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name;
-                else {
-                    if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
-                    if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
-                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-';
-                }
-            } else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
+            const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]);
+            const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
@@ -693,7 +585,6 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
-                                        <input type="checkbox" onChange={action(() => (this.overrideNested = !this.overrideNested))} checked={this.overrideNested} /> <label>Override Nested </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
@@ -713,16 +604,10 @@ export class SharingManager extends React.Component<{}> {
                             <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
                                 <div className="title-individual">
                                     Individuals &nbsp;
-                                    {this.individualSort === 'ascending' ? (
-                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                    ) : this.individualSort === 'descending' ? (
-                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                    ) : (
-                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                    )}
+                                    <FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
                                 </div>
                             </div>
-                            <div className={'users-list'}>{userListContents}</div>
+                            <div className="users-list">{userListContents}</div>
                         </div>
                         <div className={'group-container'}>
                             <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
@@ -732,13 +617,7 @@ export class SharingManager extends React.Component<{}> {
                                         <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                     </div>
                                     &nbsp;
-                                    {this.groupSort === 'ascending' ? (
-                                        <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                    ) : this.groupSort === 'descending' ? (
-                                        <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                    ) : (
-                                        <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                    )}
+                                    <FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
                                 </div>
                             </div>
                             <div className={'groups-list'}>{groupListContents}</div>
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index b8a6f6c05..a37df106b 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -1,39 +1,38 @@
-@import "./global/globalCssVariables";
-
+@import './global/globalCssVariables';
 
 .dashboard-view {
-      padding: 50px;
-      display: flex;
-      flex-direction: row;
-      width: 100%;
-      position: absolute;
-
-      .left-menu {
-            display: flex;
-            justify-content: flex-start;
-            flex-direction: column;
-            width: 250px;
-            min-width: 250px;
-      }
-
-      .all-dashboards {
-            display: flex;
-            flex-direction: row;
-            flex-wrap: wrap;
-            overflow-y: scroll;
-      }
+    padding: 50px;
+    display: flex;
+    flex-direction: row;
+    width: 100%;
+    position: absolute;
+
+    .left-menu {
+        display: flex;
+        justify-content: flex-start;
+        flex-direction: column;
+        width: 250px;
+        min-width: 250px;
+    }
+
+    .all-dashboards {
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        overflow-y: scroll;
+    }
 }
 
 .text-button {
     cursor: pointer;
-      padding: 3px 0;
-      &:hover {
-            font-weight: 500;
-      }
-
-      &.selected {
-            font-weight: 700;
-      }
+    padding: 3px 0;
+    &:hover {
+        font-weight: 500;
+    }
+
+    &.selected {
+        font-weight: 700;
+    }
 }
 
 .new-dashboard-button {
@@ -64,41 +63,51 @@
 }
 
 .dashboard-container {
-      border-radius: 10px;
-      cursor: pointer;
-      width: 250px;
-      height: 200px;
-      outline: solid 2px $light-gray;
-      display: flex;
-      flex-direction: column;
-      margin: 0 0px 30px 30px;
-      overflow: hidden;
-
-      &:hover{
+    border-radius: 10px;
+    cursor: pointer;
+    width: 250px;
+    height: 200px;
+    outline: solid 2px $light-gray;
+    display: flex;
+    flex-direction: column;
+    margin: 0 0px 30px 30px;
+    overflow: hidden;
+
+    &:hover {
         outline: solid 2px $light-blue;
-      }
-
-      .title {
-            margin: 10px;
-            font-weight: 500;
-      }
-
-      img {
-            width: auto;
-            height: 80%;
-      }
-
-      .info {
-            display: flex;
-            flex-direction: row;
-            justify-content: space-between;
-            align-items: center;
-            padding: 0px 10px;
-      }
-
-      .more {
-            z-index: 100;
-      }
+    }
+
+    .title {
+        margin: 10px;
+        font-weight: 500;
+    }
+
+    img {
+        width: auto;
+        height: 80%;
+    }
+
+    .info {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        padding: 0px 10px;
+    }
+    .dashboard-status,
+    .dashboard-status-shared {
+        font-size: 9;
+        left: 10%;
+        position: relative;
+        top: -5;
+    }
+    .dashboard-status-shared {
+        background: 'lightgreen';
+    }
+
+    .more {
+        z-index: 100;
+    }
 }
 
 .new-dashboard {
@@ -136,4 +145,4 @@
         flex-direction: row;
         justify-content: flex-end;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index b60b84015..6feeb8846 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
+import { AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
@@ -67,7 +67,7 @@ export class DashboardView extends React.Component {
         if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
             return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
         } else {
-            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig)
+            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig);
             // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._type_collection === CollectionViewType.Docking);
             return sharedDashboards;
         }
@@ -168,15 +168,24 @@ export class DashboardView extends React.Component {
                     <div className="all-dashboards">
                         {this.getDashboards().map(dashboard => {
                             const href = ImageCast(dashboard.thumb)?.url.href;
+                            const shared = Object.keys(dashboard[DocAcl])
+                                .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Public'].includes(key))
+                                .some(key => ![AclUnset, AclPrivate].includes(dashboard[DocAcl][key]));
                             return (
-                                <div className="dashboard-container" key={dashboard[Id]} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
+                                <div className="dashboard-container" key={dashboard[Id]} style={{ background: shared ? 'lightgreen' : '' }} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
                                         src={
                                             href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
                                         }
                                     />
                                     <div className="info">
-                                        <input style={{ border: 'unset' }} className="input" onClick={e => e.stopPropagation()} defaultValue={StrCast(dashboard.title)} onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)} />
+                                        <input
+                                            style={{ border: 'unset', borderRadius: '5px' }}
+                                            className="input"
+                                            onClick={e => e.stopPropagation()}
+                                            defaultValue={StrCast(dashboard.title)}
+                                            onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)}
+                                        />
                                         {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
                                         <div
                                             className="more"
@@ -192,6 +201,7 @@ export class DashboardView extends React.Component {
                                             <Button size={Size.SMALL} icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
                                         </div>
                                     </div>
+                                    <div className={'dashboard-status' + (shared ? '-shared' : '')}>{shared ? 'shared' : ''}</div>
                                 </div>
                             );
                         })}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 1d0feec74..44af51341 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,18 +1,16 @@
 import { action, computed, observable } from 'mobx';
 import { DateField } from '../../fields/DateField';
-import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
+import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
 import { List } from '../../fields/List';
-import { Cast, DocCast, ScriptCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
+import { Cast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { InteractionUtils } from '../util/InteractionUtils';
-import { UndoManager } from '../util/UndoManager';
 import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
-import { SharingManager } from '../util/SharingManager';
 
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
@@ -184,7 +182,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
-            const targetDataDoc = this.props.Document[DocData];
+            const targetDataDoc = this.rootDoc[DocData];
             const effectiveAcl = GetEffectiveAcl(targetDataDoc);
 
             if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
@@ -194,52 +192,33 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (added.length) {
                 const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
 
-                aclKeys.forEach(key =>
-                    added.forEach(d => {
-                        if (key != 'acl-Me') {
-                            const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
-                            const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl;
-                            const permission = HierarchyMapping.get(permissionSymbol)!.name;
-                            distributeAcls(key, permission, Doc.GetProto(d));
-                        }
-                    })
-                );
+                GetEffectiveAcl(this.rootDoc) === AclAdmin &&
+                    aclKeys.forEach(key =>
+                        added.forEach(d => {
+                            if (key != 'acl-Me') {
+                                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
+                                const permissionSymbol = ReverseHierarchyMap.get(permissionString)?.acl;
+                                const permission = permissionSymbol && HierarchyMapping.get(permissionSymbol)?.name;
+                                distributeAcls(key, permission ?? SharingPermissions.Augment, Doc.GetProto(d));
+                            }
+                        })
+                    );
 
                 if (effectiveAcl === AclAugment) {
                     added.map(doc => {
-                        Doc.SetContainer(doc, this.props.Document);
-                        if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
+                        if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
                         Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
-                        const parent = DocCast(doc.embedContainer);
-                        doc.embedContainer && inheritParentAcls(parent, doc);
-                        for (const key of Object.keys(parent)) {
-                            const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-                            if (symbol && key.startsWith('acl')) {
-                                const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-                                const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
-                                if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
-                            }
-                        }
+                        Doc.SetContainer(doc, targetDataDoc);
+                        inheritParentAcls(targetDataDoc, doc);
                     });
                 } else {
                     added
                         .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
                         .map(doc => {
-                            // only make a pushpin if we have acl's to edit the document
-                            //DocUtils.LeavePushpin(doc);
                             doc._dragOnlyWithinContainer = undefined;
-                            Doc.SetContainer(doc, this.props.Document);
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
-                            const parent = DocCast(doc.embedContainer);
-                            doc.embedContainer && inheritParentAcls(parent, doc);
-                            for (const key of Object.keys(Doc.GetProto(parent))) {
-                                const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-                                if (symbol && key.startsWith('acl')) {
-                                    const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-                                    const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0];
-                                    if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc);
-                                }
-                            }
+                            Doc.SetContainer(doc, this.rootDoc);
+                            inheritParentAcls(targetDataDoc, doc);
                         });
 
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cdd9e62d8..3522830e5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,7 +5,6 @@ import { IconButton } from 'browndash-components';
 import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import { FaUndo } from 'react-icons/fa';
-import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { DateField } from '../../fields/DateField';
 import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
 import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
@@ -13,27 +12,27 @@ import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { ScriptField } from '../../fields/ScriptField';
 import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, GetEffectiveLayoutAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { DocumentType } from '../documents/DocumentTypes';
+import { GetEffectiveAcl } from '../../fields/util';
+import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
 import { Docs } from '../documents/Documents';
+import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { DragManager } from '../util/DragManager';
 import { LinkFollower } from '../util/LinkFollower';
 import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
 import { SnappingManager } from '../util/SnappingManager';
 import { UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { InkStrokeProperties } from './InkStrokeProperties';
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
+import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { Colors } from './global/globalEnums';
 import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
-import { ImageBox } from './nodes/ImageBox';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
 import _ = require('lodash');
 
@@ -165,7 +164,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onContainerDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -179,7 +178,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
             setupMoveUpEvents(
                 this,
@@ -200,7 +199,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     @action
     onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
         if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) {
             return false;
         }
@@ -496,7 +495,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
         const first = SelectionManager.Views()[0];
-        const effectiveAcl = GetEffectiveLayoutAcl(first.rootDoc);
+        const effectiveAcl = GetEffectiveAcl(first.rootDoc);
         if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
         if (!first) return false;
         let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
@@ -765,7 +764,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         }
 
         // sharing
-        const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc);
+        const acl = GetEffectiveAcl(!this.showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc);
         const docShareMode = HierarchyMapping.get(acl)!.name;
         const shareMode = StrCast(docShareMode);
         var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
@@ -773,7 +772,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations;
         const hideResizers =
-            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
+            ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating;
         const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating;
         const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 47dcdd2e4..347c40c18 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -285,13 +285,17 @@ export class KeyManager {
                 preventDefault = false;
                 break;
             case 'y':
-                SelectionManager.DeselectAll();
-                UndoManager.Redo();
+                if (Doc.ActivePage !== 'home') {
+                    SelectionManager.DeselectAll();
+                    UndoManager.Redo();
+                }
                 stopPropagation = false;
                 break;
             case 'z':
-                SelectionManager.DeselectAll();
-                UndoManager.Undo();
+                if (Doc.ActivePage !== 'home') {
+                    SelectionManager.DeselectAll();
+                    UndoManager.Undo();
+                }
                 stopPropagation = false;
                 break;
             case 'a':
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 258674d53..b708e2587 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -153,10 +153,12 @@ export class MainView extends React.Component {
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields([
+            DocServer.setPlaygroundFields(['dockingConfig']);
+            DocServer.setLivePlaygroundFields([
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
+                'treeViewExpandedView',
                 'carousel_index',
                 'itemIndex', // for changing slides in presentations
                 'layout_sidebarWidthPercent',
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 14291b537..633401d58 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -395,9 +395,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <div>
                 <div className={'propertiesView-shareDropDown'}>
                     <div className={`propertiesView-shareDropDown${permission}`}>
-                        <div className="propertiesView-shareDropDown">
-                            {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}
-                        </div>
+                        <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}</div>
                     </div>
                 </div>
             </div>
@@ -425,11 +423,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         // all selected docs
-        const docs =
-            SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
+        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
         const target = docs[0];
 
-        const showAdmin = GetEffectiveAcl(target) == AclAdmin
+        const showAdmin = GetEffectiveAcl(target) == AclAdmin;
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
@@ -450,13 +447,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
-            var permission;
-            if (this.layoutDocAcls){
-                if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
-                else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
-                else permission = StrCast(Doc.GetProto(target)?.[userKey]);
-            }
-            else permission = StrCast(target[userKey]);
+            var permission = StrCast(target[userKey]);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
@@ -466,12 +457,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
             var permission;
-            if (this.layoutDocAcls){
+            if (this.layoutDocAcls) {
                 if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
                 else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
                 else permission = StrCast(Doc.GetProto(target)?.[userKey]);
-            }
-            else permission = StrCast(target[userKey]);
+            } else permission = StrCast(target[userKey]);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         }
 
@@ -481,36 +471,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds groups
         const groupTableEntries: JSX.Element[] = [];
         const groupList = GroupManager.Instance?.allGroups || [];
-        groupList.sort(this.sortGroups)
+        groupList.sort(this.sortGroups);
         groupList.map(group => {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     var permission;
-                    if (this.layoutDocAcls){
-                        if (target[DocAcl][groupKey]){
+                    if (this.layoutDocAcls) {
+                        if (target[DocAcl][groupKey]) {
                             permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
-                        }
-                        else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
+                        } else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
                         else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
-                    }
-                    else permission = StrCast(target[groupKey]);
+                    } else permission = StrCast(target[groupKey]);
                     groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
                 }
             }
         });
 
         // public permission
-        let publicPermission = StrCast(target['acl-Public']);
-        if (this.layoutDocAcls){
-            if (target['acl-Public-layout']) publicPermission = StrCast(target['acl-Public-layout']);
-            else if (target['embedContainer']) publicPermission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))['acl-Public']);
-            else StrCast(Doc.GetProto(target)['acl-Public']);
-        }
+        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Public']);
 
         return (
             <div>
-                <br/>
+                <br />
                 Public / Guest Users
                 <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
                 <div>
@@ -518,7 +501,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <br></br> Individual Users with Access to this Document{' '}
                 </div>
                 <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
-                {groupTableEntries.length>0 ? 
+                {groupTableEntries.length > 0 ? (
                     <div>
                         <div>
                             {' '}
@@ -526,7 +509,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         </div>
                         <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
                     </div>
-                : null}
+                ) : null}
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ce9eb9f17..0daa3dd92 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
-import { distributeAcls, inheritParentAcls } from '../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, GetPropAcl, inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
@@ -30,7 +30,7 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
 import { DocumentManager } from '../../util/DocumentManager';
-import { DocAcl } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocAcl } from '../../../fields/DocSymbols';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
@@ -386,8 +386,13 @@ export class CollectionDockingView extends CollectionSubView() {
                   .map(f => f as Doc);
         const changesMade = this.props.Document.dockingConfig !== json;
         if (changesMade) {
-            this.props.Document.dockingConfig = json;
-            this.props.Document.data = new List<Doc>(docs);
+            if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
+                this.layoutDoc.dockingConfig = json;
+                this.layoutDoc.data = new List<Doc>(docs);
+            } else {
+                Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
+                Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+            }
         }
         this._flush?.end();
         this._flush = undefined;
@@ -521,7 +526,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
-                this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                inheritParentAcls(this.rootDoc, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
         });
@@ -564,7 +569,7 @@ export class CollectionDockingView extends CollectionSubView() {
                             _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
-                        this.props.Document.isShared && inheritParentAcls(Doc.GetProto(this.props.Document), Doc.GetProto(docToAdd));
+                        inheritParentAcls(this.dataDoc, Doc.GetProto(docToAdd));
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a2269075d..7767c5b79 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -392,7 +392,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const innerAdd = (doc: Doc) => {
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
-                dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
+                dataIsComputed && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
                 return added;
             };
             return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -455,7 +455,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const innerAdd = (doc: Doc) => {
                         const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                         const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                        dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer);
+                        dataIsComputed && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
                         return added;
                     };
                     return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -563,7 +563,7 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
                 const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
                 const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
-                !dataIsComputed && added && Doc.SetContainer(doc, this.doc.embedContainer);
+                !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
 
                 return added;
             };
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dab269474..a4f5eb62b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -535,7 +535,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             // if this is part of a template, let the event go up to the template root unless right/ctrl clicking
             if (
                 // prettier-ignore
-                this.props.isDocumentActive?.() &&
+                (this.props.isDocumentActive?.() || this.props.isContentActive?.()) &&
                 !this.props.onBrowseClick?.() &&
                 !this.Document.ignoreClick &&
                 e.button === 0 &&
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9c138d348..28fbdc192 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -481,7 +481,7 @@ export namespace Doc {
         doc.embedContainer = container;
         if (Doc.GetProto(container).author === doc.author) {
             Object.keys(Doc.GetProto(container))
-                .filter(key => key.startsWith('acl'))
+                .filter(key => key.startsWith('acl') && !key.includes(Doc.CurrentUserEmailNormalized))
                 .forEach(key => (doc[key] = Doc.GetProto(container)[key]));
         }
     }
@@ -696,7 +696,7 @@ export namespace Doc {
             Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length + 1;
         embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
         embedding.author = Doc.CurrentUserEmail;
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index fc0057827..9230d9df0 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -14,6 +14,7 @@ import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
 import { RefField } from './RefField';
+import { RichTextField } from './RichTextField';
 import { SchemaHeaderField } from './SchemaHeaderField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
@@ -62,8 +63,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     const fromServer = target[UpdatingFromServer];
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
-    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin) && !DocServer.Control.isReadOnly();
+    const writeToDoc =
+        sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField);
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclAugment && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -104,7 +106,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             );
         return true;
     }
-    return false;
+    return true;
 });
 
 let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
@@ -128,6 +130,7 @@ export function denormalizeEmail(email: string) {
  * Copies parent's acl fields to the child
  */
 export function inheritParentAcls(parent: Doc, child: Doc) {
+    if (GetEffectiveAcl(parent) !== AclAdmin) return;
     for (const key of Object.keys(parent)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
         // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
@@ -168,30 +171,16 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
     return getEffectiveAcl(target, user);
 }, true);
 
-// return layout acl from cache or chache the acl and return.
-const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) {
-    return getEffectiveLayoutAcl(target, user);
-}, true);
-
 /**
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
     if (!target) return AclPrivate;
     if (target[UpdatingFromServer]) return AclAdmin;
-    return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
-}
-
-/**
- * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set.
- */
-export function GetEffectiveLayoutAcl(target: any, user?: string): symbol {
-    if (!target) return AclPrivate;
-    if (target[UpdatingFromServer]) return AclAdmin;
-    return getEffectiveLayoutAclCache(target, user);
+    return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
 }
 
-function getPropAcl(target: any, prop: string | symbol | number) {
+export function GetPropAcl(target: any, prop: string | symbol | number) {
     if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
     if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
     return GetEffectiveAcl(target);
@@ -233,37 +222,6 @@ function getEffectiveAcl(target: any, user?: string): symbol {
     return AclAdmin;
 }
 
-/**
- * Returns the layout acl that is effective on the document passed through as the target. If no layout acls
- * have been set, it returns the regular acls for the document target is contained in.
- */
-function getEffectiveLayoutAcl(target: any, user?: string): symbol {
-    const targetAcls = target[DocAcl];
-
-    const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-    if (targetAcls && Object.keys(targetAcls).length) {
-        var effectiveAcl;
-        for (const [key, value] of Object.entries(targetAcls)) {
-            const entity = denormalizeEmail(key.substring(4)); // an individual or a group
-            if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') {
-                if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) {
-                    effectiveAcl = value as symbol;
-                } else {
-                    effectiveAcl = value as symbol;
-                }
-            }
-        }
-
-        if (effectiveAcl) {
-            return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
-        }
-    }
-    // authored documents are private until an ACL is set.
-    const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc or Proxy, so check __fieldTuples.author and .author
-    if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
-    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)
@@ -272,11 +230,13 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol {
  * @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, visited?: Doc[], isDashboard?: boolean) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[]) {
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target)) return;
     if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
-        target[key] = acl;
+        if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+            target[key] = acl;
+        }
         if (target !== Doc.GetProto(target)) {
             //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
             updateCachedAcls(target);
@@ -288,19 +248,16 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
     // 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 (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
-        target[key] = acl;
-        layoutDocChanged = true;
-        if (isDashboard) {
-            DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => {
-                docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited));
-            });
+        if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+            target[key] = acl;
+            layoutDocChanged = true;
         }
     }
 
     let dataDocChanged = false;
     const dataDoc = target[DocData];
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
-        if (GetEffectiveAcl(dataDoc) === AclAdmin) {
+        if (GetEffectiveAcl(dataDoc) === AclAdmin && (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`)) {
             dataDoc[key] = acl;
             dataDocChanged = true;
         }
@@ -327,7 +284,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
 export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
     let prop = in_prop;
-    const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop);
+    const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
     if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && 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;
-- 
cgit v1.2.3-70-g09d2

