From 1a9ecbb274e7b95a5b1dca6c45c329d9a32432c7 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 22 Jun 2023 12:27:57 -0400
Subject: playground fields edit

---
 src/client/views/MainView.tsx | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 1a9d15c1a..c827bc92a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -157,33 +157,28 @@ export class MainView extends React.Component {
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
-                'layout_showSidebar',
                 'carousel_index',
                 'itemIndex', // for changing slides in presentations
                 'layout_sidebarWidthPercent',
                 'layout_currentTimecode',
                 'layout_timelineHeightPercent',
+                'layout_hideMinimap',
+                'layout_showSidebar',
+                'layout_scrollTop',
+                'layout_fitWidth',
+                'layout_curPage',
                 'presStatus',
                 'freeform_panX',
                 'freeform_panY',
+                'freeform_scale',
                 'overlayX',
                 'overlayY',
-                'layout_fitWidth',
-                'nativeWidth',
-                'nativeHeight',
                 'text_scrollHeight',
                 'text_height',
-                'layout_hideMinimap',
-                'freeform_scale',
-                'layout_scrollTop',
                 'hidden',
-                'layout_curPage',
-                'type_collection',
+                //'type_collection',
                 'chromeHidden',
                 'currentFrame',
-                'width',
-                'height',
-                'nativeWidth',
             ]); // can play with these fields on someone else's
         }
         DocServer.GetRefField('rtfProto').then(
-- 
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/MainView.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


From 0007d043f7f27ce7d1103198ecb0e95e81362619 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Sat, 8 Jul 2023 09:19:16 -0400
Subject: fixed adding tabs to not try to distribute acls (this is done through
 adding a Doc to the docking view's Doc list).

---
 src/client/views/MainView.tsx                          | 2 +-
 src/client/views/collections/CollectionDockingView.tsx | 9 ---------
 src/fields/util.ts                                     | 4 +++-
 3 files changed, 4 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b708e2587..59636ccbe 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -153,8 +153,8 @@ export class MainView extends React.Component {
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(['dockingConfig']);
             DocServer.setLivePlaygroundFields([
+                'dockingConfig',
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 30bc8cbec..77b719d53 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -500,15 +500,6 @@ export class CollectionDockingView extends CollectionSubView() {
         }
     };
     tabCreated = (tab: any) => {
-        const aclKeys = Object.keys(Doc.GetProto(this.rootDoc)[DocAcl] ?? {});
-        aclKeys
-            .filter(key => key !== 'acl-Me')
-            .forEach(key => {
-                const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]);
-                const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl;
-                const permission = HierarchyMapping.get(permissionSymbol)!.name;
-                distributeAcls(key, permission, 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)
     };
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 815f3b186..36f619120 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -240,7 +240,9 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
 
     let dataDocChanged = false;
     const dataDoc = target[DocData];
-    if (dataDoc && (allowUpgrade || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
+    const curVal = ReverseHierarchyMap.get(StrCast(dataDoc[key]))?.level ?? 0;
+    const aclVal = ReverseHierarchyMap.get(acl)?.level ?? 0;
+    if (dataDoc && (allowUpgrade || !dataDoc[key] || curVal > aclVal)) {
         // propagate ACLs to links, children, and annotations
 
         LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade));
-- 
cgit v1.2.3-70-g09d2


From 044f451de061373aa8865082ee9c02f567d17264 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Wed, 12 Jul 2023 14:55:33 -0400
Subject: fixing dashboard view of shared dashboards to open dashbarods the
 same as sharing panel - as embeddings.

---
 src/client/util/CurrentUserUtils.ts                    |  1 +
 src/client/views/DashboardView.tsx                     | 17 ++++++++++++++---
 src/client/views/MainView.tsx                          |  1 -
 src/client/views/collections/CollectionDockingView.tsx |  4 ++++
 src/client/views/collections/TreeView.tsx              | 10 ++++------
 5 files changed, 23 insertions(+), 10 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 2cee37380..f7de6b41f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -849,6 +849,7 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.treeViewFreezeChildren = "remove|add";
+        doc.activePage = doc.activeDashboard === undefined ? 'home': doc.activePage;
         this.setupLinkDocs(doc, linkDatabaseId);
         this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 9b16ecfa7..0c871b2b2 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -57,8 +57,11 @@ export class DashboardView extends React.Component {
     };
 
     clickDashboard = (e: React.MouseEvent, dashboard: Doc) => {
-        Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
-        Doc.ActiveDashboard = dashboard;
+        if (this.selectedDashboardGroup === DashboardGroup.SharedDashboards) {   
+            DashboardView.openSharedDashboard(dashboard);
+        } else {
+            Doc.ActiveDashboard = dashboard;
+        }
         Doc.ActivePage = 'dashboard';
     };
 
@@ -170,7 +173,10 @@ export class DashboardView extends React.Component {
                                 .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Guest'].includes(key))
                                 .some(key => dashboard[DocAcl][key] !== AclPrivate);
                             return (
-                                <div className="dashboard-container" key={dashboard[Id]} style={{ background: shared ? 'lightgreen' : '' }} 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='
@@ -230,6 +236,11 @@ export class DashboardView extends React.Component {
         return CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
     }
 
+    public static openSharedDashboard = (dashboard:Doc) => {
+        Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
+        DashboardView.openDashboard(Doc.BestEmbedding(dashboard));
+    }
+
     /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
     /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
     public static openDashboard = (doc: Doc | undefined, fromHistory = false) => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 59636ccbe..2f877d74d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -154,7 +154,6 @@ export class MainView extends React.Component {
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
             DocServer.setLivePlaygroundFields([
-                'dockingConfig',
                 'dataTransition',
                 'viewTransition',
                 'treeViewOpen',
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8d1b46ebb..b2eaa964c 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -386,6 +386,10 @@ export class CollectionDockingView extends CollectionSubView() {
                   .map(f => f as Doc);
         const changesMade = this.props.Document.dockingConfig !== json;
         if (changesMade) {
+            if (json.startsWith('{"settings"')) {
+                alert("Porblems looming")
+                debugger;
+            }
             if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
                 this.layoutDoc.dockingConfig = json;
                 this.layoutDoc.data = new List<Doc>(docs);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 7767c5b79..91d1ff11e 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -221,12 +221,10 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns,  (2) has no context field ... otherwise make a new embedding
-            const bestEmbedding =
-                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document)
-                    ? docView.props.Document
-                    : DocListCast(this.props.document.proto_embeddings).find(doc => !doc.embedContainer && doc.author === Doc.CurrentUserEmail);
-            const nextBestEmbedding = DocListCast(this.props.document.proto_embeddings).find(doc => doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestEmbedding ?? nextBestEmbedding ?? Doc.MakeEmbedding(this.props.document), OpenWhere.lightbox);
+            const bestEmbedding =  docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document)
+                    ? docView.rootDoc
+                    : Doc.BestEmbedding(docView.rootDoc);
+              this.props.addDocTab(bestEmbedding, OpenWhere.lightbox);
         }
     };
 
-- 
cgit v1.2.3-70-g09d2

