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/views/collections/CollectionDockingView.tsx | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

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);
                     }
                 })
-- 
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/collections/CollectionDockingView.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/collections/CollectionDockingView.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/collections/CollectionDockingView.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 cf88809ea2299395db70ce608c193df7f24f0fb2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Jul 2023 15:57:51 -0400
Subject: fixed self-ownership of copied Docs created by someone else. prevent
 dash from being pasted into itself.  fixed inheritance of acls

---
 src/client/DocServer.ts                            |   6 +-
 src/client/util/SharingManager.tsx                 | 109 +++++++++++----------
 src/client/views/DocComponent.tsx                  |  32 ++----
 src/client/views/PreviewCursor.tsx                 |  26 ++---
 .../views/collections/CollectionDockingView.tsx    |  12 +--
 src/fields/Doc.ts                                  |   4 +-
 src/fields/util.ts                                 |  60 +++++-------
 7 files changed, 120 insertions(+), 129 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 8b8a9a618..67be96d13 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -198,7 +198,7 @@ export namespace DocServer {
     export namespace Control {
         let _isReadOnly = false;
         export function makeReadOnly() {
-            if (!_isReadOnly) {
+            if (!Control.isReadOnly()) {
                 _isReadOnly = true;
                 _CreateField = field => (_cache[field[Id]] = field);
                 _UpdateField = emptyFunction;
@@ -207,7 +207,7 @@ export namespace DocServer {
         }
 
         export function makeEditable() {
-            if (_isReadOnly) {
+            if (Control.isReadOnly() && Doc.CurrentUserEmail !== 'guest') {
                 location.reload();
                 // _isReadOnly = false;
                 // _CreateField = _CreateFieldImpl;
@@ -218,7 +218,7 @@ export namespace DocServer {
         }
 
         export function isReadOnly() {
-            return _isReadOnly;
+            return _isReadOnly || Doc.CurrentUserEmail === 'guest';
         }
     }
 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d3241009a..828271270 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,4 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Colors } from 'browndash-components';
 import { concat, intersection } from 'lodash';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
@@ -22,6 +23,7 @@ import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
+import { undoable } from './UndoManager';
 
 export interface User {
     email: string;
@@ -76,8 +78,10 @@ export class SharingManager extends React.Component<{}> {
     @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 overrideNested: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private - initially selected so default is override
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
+    @observable private _buttonDown = false;
 
     // private get linkVisible() {
     //     return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
@@ -91,6 +95,7 @@ export class SharingManager extends React.Component<{}> {
             DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = this.targetDoc !== undefined;
             this.permissions = SharingPermissions.Augment;
+            this.overrideNested = true;
         });
     };
 
@@ -149,36 +154,30 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Shares the document with a user.
      */
-    setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => {
+    setInternalSharing = undoable((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 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);
-
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+            distributeAcls(acl, permission as SharingPermissions, doc);
             if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
             else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
         });
-    };
+    }, 'set Doc permissions');
 
     /**
      * Sets the permission on the target for the group.
      * @param group
      * @param permission
      */
-    setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
+    setInternalGroupSharing = undoable((group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
         const target = targetDoc || this.targetDoc!;
-        const key = normalizeEmail(StrCast(group.title));
-        let acl = `acl-${key}`;
+        const acl = `acl-${normalizeEmail(StrCast(group.title))}`;
 
         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);
-
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested);
 
             if (group instanceof Doc) {
                 Doc.AddDocToList(group, 'docsShared', doc);
@@ -191,7 +190,7 @@ export class SharingManager extends React.Component<{}> {
                     });
             }
         });
-    };
+    }, 'set group permissions');
 
     /**
      * Shares the documents shared with a group with a new user who has been added to that group.
@@ -217,23 +216,23 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Called from the properties sidebar to change permissions of a user.
      */
-    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
+    shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
         if (layout) this.layoutDocAcls = true;
         if (shareWith !== 'Public' && shareWith !== 'Override') {
             const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
                 if (user) this.setInternalSharing(user, permission, doc);
-                else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
+                else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc, undefined, true);
             });
         } else {
             docs.forEach(doc => {
                 if (GetEffectiveAcl(doc) === AclAdmin) {
-                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined);
+                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, true);
                 }
             });
         }
         this.layoutDocAcls = false;
-    };
+    }, 'sidebar set permissions');
 
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
@@ -261,7 +260,7 @@ export class SharingManager extends React.Component<{}> {
         if (group.docsShared) {
             DocListCast(group.docsShared).forEach(doc => {
                 const acl = `acl-${StrCast(group.title)}`;
-                distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined);
+                distributeAcls(acl, SharingPermissions.None, doc);
 
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -342,39 +341,43 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Handles changes in the permission chosen to share with someone with
      */
-    @action
-    handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
-        this.permissions = event.currentTarget.value as SharingPermissions;
-    };
+    handlePermissionsChange = undoable(
+        action((event: React.ChangeEvent<HTMLSelectElement>) => {
+            this.permissions = event.currentTarget.value as SharingPermissions;
+        }),
+        'permission change'
+    );
 
     /**
      * Calls the relevant method for sharing, displays the popup, and resets the relevant variables.
      */
-    @action
-    share = () => {
-        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, undefined);
-                } else {
-                    this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
-                }
-            });
-
-            const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
-            TaskCompletionBox.popupX = left - 1.5 * width;
-            TaskCompletionBox.popupY = top - 1.5 * height;
-            TaskCompletionBox.textDisplayed = 'Document shared!';
-            TaskCompletionBox.taskCompleted = true;
-            setTimeout(
-                action(() => (TaskCompletionBox.taskCompleted = false)),
-                2000
-            );
+    share = undoable(
+        action(() => {
+            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, undefined);
+                    } else {
+                        this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
+                    }
+                });
+
+                const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
+                TaskCompletionBox.popupX = left - 1.5 * width;
+                TaskCompletionBox.popupY = top - 1.5 * height;
+                TaskCompletionBox.textDisplayed = 'Document shared!';
+                TaskCompletionBox.taskCompleted = true;
+                setTimeout(
+                    action(() => (TaskCompletionBox.taskCompleted = false)),
+                    2000
+                );
 
-            this.layoutDocAcls = false;
-            this.selectedUsers = null;
-        }
-    };
+                this.layoutDocAcls = false;
+                this.selectedUsers = null;
+            }
+        }),
+        'share Doc'
+    );
 
     /**
      * Sorting algorithm to sort users.
@@ -542,12 +545,17 @@ export class SharingManager extends React.Component<{}> {
                         <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} />
+                    <button
+                        className="share-copy-link"
+                        style={{ background: this._buttonDown ? Colors.LIGHT_BLUE : undefined }}
+                        onPointerDown={action(e => (this._buttonDown = true))}
+                        onPointerUp={action(e => (this._buttonDown = false))}
+                        onClick={this.copyURL}>
+                        <FontAwesomeIcon title="Copy Public URL" icon="copy" size="sm" />
                         &nbsp; Copy Public URL
                     </button>
-                    <div className={'close-button'} onClick={this.close}>
-                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
+                    <div className="close-button" onClick={this.close}>
+                        <FontAwesomeIcon icon="times" color="black" size="lg" />
                     </div>
                     {admin ? (
                         <div className="share-container">
@@ -585,6 +593,7 @@ 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>
                                 )}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 44af51341..422d2d6d7 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -190,36 +190,24 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
-
-                GetEffectiveAcl(this.rootDoc) === AclAdmin &&
-                    aclKeys.forEach(key =>
+                Object.keys(Doc.GetProto(this.rootDoc)[DocAcl]) // apply all collection acls (except pseudo-acl 'Me') to each added doc
+                    .filter(key => key !== 'acl-Me')
+                    .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));
-                            }
+                            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, d);
                         })
                     );
 
-                if (effectiveAcl === AclAugment) {
+                if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
                     added.map(doc => {
+                        doc._dragOnlyWithinContainer = undefined;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
-                        Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
-                        Doc.SetContainer(doc, targetDataDoc);
+                        Doc.SetContainer(doc, this.rootDoc);
                         inheritParentAcls(targetDataDoc, doc);
                     });
-                } else {
-                    added
-                        .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
-                        .map(doc => {
-                            doc._dragOnlyWithinContainer = undefined;
-                            if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
-                            Doc.SetContainer(doc, this.rootDoc);
-                            inheritParentAcls(targetDataDoc, doc);
-                        });
 
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                     if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add)));
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index b513fe245..82d2bff56 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -50,18 +50,20 @@ export class PreviewCursor extends React.Component<{}> {
                     PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, PreviewCursor._addDocument).then(batch.end);
                 } else if (re.test(plain)) {
                     const url = plain;
-                    undoBatch(() =>
-                        PreviewCursor._addDocument(
-                            Docs.Create.WebDocument(url, {
-                                title: url,
-                                _width: 500,
-                                _height: 300,
-                                data_useCors: true,
-                                x: newPoint[0],
-                                y: newPoint[1],
-                            })
-                        )
-                    )();
+                    if (url.startsWith(window.location.href)) {
+                        undoBatch(() =>
+                            PreviewCursor._addDocument(
+                                Docs.Create.WebDocument(url, {
+                                    title: url,
+                                    _width: 500,
+                                    _height: 300,
+                                    data_useCors: true,
+                                    x: newPoint[0],
+                                    y: newPoint[1],
+                                })
+                            )
+                        )();
+                    } else alert('cannot paste dash into itself');
                 } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
                     const clone = plain.startsWith('__DashCloneId(');
                     const docids = plain.split(':');
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0daa3dd92..30bc8cbec 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -500,15 +500,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 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, Doc.GetProto(tab));
-            }
-        });
+                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/Doc.ts b/src/fields/Doc.ts
index 28fbdc192..8be295810 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -751,7 +751,9 @@ export namespace Doc {
                     }
                 };
                 const docAtKey = doc[key];
-                if (docAtKey instanceof Doc) {
+                if (key === 'author') {
+                    assignKey(Doc.CurrentUserEmail);
+                } else if (docAtKey instanceof Doc) {
                     if (pruneDocs.includes(docAtKey)) {
                         // prune doc and do nothing
                     } else if (!Doc.IsSystem(docAtKey) && (key.startsWith('layout') || ['embedContainer', 'annotationOn', 'proto'].includes(key) || ((key === 'link_anchor_1' || key === 'link_anchor_2') && doc.author === Doc.CurrentUserEmail))) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 0e9940ced..815f3b186 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -65,7 +65,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
     const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
     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 && Doc.CurrentUserEmail !== 'guest' && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+    const writeToServer =
+        !DocServer.Control.isReadOnly() && //
+        (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclAugment && value instanceof RichTextField));
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -230,52 +232,40 @@ function getEffectiveAcl(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[]) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean) {
+    const selfKey = `acl-${Doc.CurrentUserEmailNormalized}`;
     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)) {
-        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);
-        }
-        //return;
-    }
+    if (!target || visited.includes(target) || key === selfKey) return;
     visited.push(target);
 
-    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)) {
-        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 && (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`)) {
-            dataDoc[key] = acl;
-            dataDocChanged = true;
-        }
+    if (dataDoc && (allowUpgrade || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
+        // propagate ACLs to links, children, and annotations
 
-        // maps over the links of the document
-        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited));
+        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade));
 
-        // maps over the children of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
-            distributeAcls(key, acl, d, inheritingFromCollection, visited);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
+            distributeAcls(key, acl, d, visited, allowUpgrade);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade);
         });
 
-        // maps over the annotations of the document
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
-            distributeAcls(key, acl, d, inheritingFromCollection, visited);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
+            distributeAcls(key, acl, d, visited, allowUpgrade);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade);
         });
+
+        if (GetEffectiveAcl(dataDoc) === AclAdmin) {
+            dataDoc[key] = acl;
+            dataDocChanged = true;
+        }
+    }
+
+    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) allowUpgrade is set B) the key doesn't already exist or c) the right being inherited is more restrictive
+    if (GetEffectiveAcl(target) === AclAdmin && (allowUpgrade || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
+        target[key] = acl;
+        layoutDocChanged = true;
     }
 
     layoutDocChanged && updateCachedAcls(target); // updates target[AclSym] when changes to acls have been made
-- 
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/collections/CollectionDockingView.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 2f4c55bf026a249910e90451a173234934ebca01 Mon Sep 17 00:00:00 2001
From: geireann <geireann.lindfield@gmail.com>
Date: Mon, 10 Jul 2023 12:00:31 -0400
Subject: fixed sharing to a user to allow upgrading credentials.

---
 src/client/util/SharingManager.tsx                     |  6 +++---
 src/client/views/DashboardView.scss                    |  5 ++++-
 src/client/views/DocComponent.tsx                      |  2 +-
 src/client/views/collections/CollectionDockingView.tsx |  8 ++++----
 src/fields/util.ts                                     | 12 ++++++------
 5 files changed, 18 insertions(+), 15 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 828271270..37c813279 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -160,7 +160,7 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(user.email)}`;
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
         docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
-            distributeAcls(acl, permission as SharingPermissions, doc);
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : 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);
         });
@@ -177,7 +177,7 @@ export class SharingManager extends React.Component<{}> {
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
         docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested);
+            distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : undefined);
 
             if (group instanceof Doc) {
                 Doc.AddDocToList(group, 'docsShared', doc);
@@ -227,7 +227,7 @@ export class SharingManager extends React.Component<{}> {
         } else {
             docs.forEach(doc => {
                 if (GetEffectiveAcl(doc) === AclAdmin) {
-                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined, true);
+                    distributeAcls(`acl-${shareWith}`, permission, doc, undefined);
                 }
             });
         }
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index 583edac08..4551fb4f4 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -7,6 +7,8 @@
     width: 100%;
     position: absolute;
     height: 100%;
+    width:100%;
+    padding-right: 0px;
     overflow: auto;
 
     .left-menu {
@@ -21,7 +23,8 @@
         display: flex;
         flex-direction: row;
         flex-wrap: wrap;
-        overflow-y: scroll;
+        overflow-y: auto;
+        width: 100%;
     }
 }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 422d2d6d7..10985c3f1 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -197,7 +197,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             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, d);
+                            distributeAcls(key, permission ?? SharingPermissions.Augment, d, undefined, false);
                         })
                     );
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 77b719d53..13d3b2cdc 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, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } 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 { distributeAcls, GetEffectiveAcl, GetPropAcl, inheritParentAcls } from '../../../fields/util';
+import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
@@ -28,9 +28,9 @@ import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
 import { TabDocView } from './TabDocView';
-import React = require('react');
 import { DocumentManager } from '../../util/DocumentManager';
-import { AclAdmin, AclEdit, DocAcl } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
+import React = require('react');
 const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 4dcbf1fbe..034229319 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -243,19 +243,19 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     const dataDoc = target[DocData];
     const curVal = ReverseHierarchyMap.get(StrCast(dataDoc[key]))?.level ?? 0;
     const aclVal = ReverseHierarchyMap.get(acl)?.level ?? 0;
-    if (dataDoc && (allowUpgrade || !dataDoc[key] || curVal > aclVal)) {
+    if (dataDoc && (allowUpgrade !== false|| !dataDoc[key] || curVal > aclVal)) {
         // propagate ACLs to links, children, and annotations
 
-        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade));
+        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade? true: false));
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade);
+            distributeAcls(key, acl, d, visited, allowUpgrade ? true: false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true: false);
         });
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade);
+            distributeAcls(key, acl, d, visited, allowUpgrade? true: false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade? true: false);
         });
 
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
-- 
cgit v1.2.3-70-g09d2


From 06cfe3cbba127d865e788b00561f8a591af1bd81 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 11 Jul 2023 15:32:08 -0400
Subject: more fixes to simplify sharing

---
 src/client/documents/Documents.ts                  | 31 ++++++------
 src/client/util/CurrentUserUtils.ts                |  6 +--
 src/client/util/DocumentManager.ts                 |  2 +-
 src/client/util/SharingManager.tsx                 |  9 ++--
 src/client/views/DashboardView.tsx                 | 15 +++---
 src/client/views/DocComponent.tsx                  | 15 +-----
 src/client/views/PropertiesView.tsx                | 55 ++++++++++------------
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  8 +---
 src/client/views/nodes/button/FontIconBox.tsx      |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  | 16 ++-----
 src/fields/DocSymbols.ts                           |  1 -
 src/fields/util.ts                                 | 39 +++++++--------
 14 files changed, 88 insertions(+), 117 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index f1ba852f9..4ebbfbd1c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -169,8 +169,8 @@ export class DocumentOptions {
     _nativeDimModifiable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers');
     _nativeHeightUnfrozen?: BOOLt = new BoolInfo('native height can be changed independent of width by dragging decoration resizers');
 
-    'acl-Public'?: string; // public permissions
-    '_acl-Public'?: string; // public permissions
+    'acl-Guest'?: string; // public permissions
+    '_acl-Guest'?: string; // public permissions
     type?: DTYPEt = new DTypeInfo('type of document', true);
     type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
     _type_collection?: COLLt = new CTypeInfo('how collection is rendered'); // sub type of a collection
@@ -825,8 +825,7 @@ 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;
-
+            dataProps['acl-Guest'] = options['acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
             dataProps.isSystem = viewProps.isSystem;
             dataProps.isDataDoc = true;
             dataProps.author = Doc.CurrentUserEmail;
@@ -848,16 +847,17 @@ export namespace Docs {
                 dataDoc.proto = proto;
             }
 
-            const viewFirstProps: { [id: string]: any } = {};
-            // viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            // viewFirstProps['acl-Override'] = SharingPermissions.Unset;
-            viewFirstProps.author = Doc.CurrentUserEmail;
+            const viewFirstProps: { [id: string]: any } = { author: Doc.CurrentUserEmail };
+            viewFirstProps['acl-Guest'] = options['_acl-Guest'] ?? (Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View);
             let viewDoc: Doc;
             // determines whether viewDoc should be created using placeholder Doc or default
             if (placeholderDoc) {
                 placeholderDoc._height = options._height !== undefined ? Number(options._height) : undefined;
                 placeholderDoc._width = options._width !== undefined ? Number(options._width) : undefined;
                 viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true);
+                Array.from(Object.keys(placeholderDoc))
+                    .filter(key => key.startsWith('acl'))
+                    .forEach(key => (dataDoc[key] = viewDoc[key] = placeholderDoc[key]));
             } else {
                 viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             }
@@ -997,7 +997,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-Guest'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View;
             //I['acl-Override'] = SharingPermissions.Unset;
             I[Initializing] = false;
 
@@ -1169,10 +1169,13 @@ export namespace Docs {
             const doc = DockDocument(
                 configs.map(c => c.doc),
                 JSON.stringify(layoutConfig),
-                options,
+                Doc.CurrentUserEmail === 'guest' ? options : { 'acl-Guest': SharingPermissions.View, ...options },
                 id
             );
-            configs.map(c => Doc.SetContainer(c.doc, doc));
+            configs.map(c => {
+                Doc.SetContainer(c.doc, doc);
+                inheritParentAcls(doc, c.doc, false);
+            });
             return doc;
         }
 
@@ -1332,8 +1335,8 @@ export namespace DocUtils {
                 source,
                 target,
                 {
-                    'acl-Public': SharingPermissions.Augment,
-                    '_acl-Public': SharingPermissions.Augment,
+                    'acl-Guest': SharingPermissions.Augment,
+                    '_acl-Guest': 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,
@@ -1854,8 +1857,6 @@ export namespace DocUtils {
             Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory_count']).toString(), true);
         }
 
-        if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
-
         return ndoc;
     }
     export function delegateDragFactory(dragFactory: Doc) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 11a8dcaf6..2cee37380 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -765,7 +765,7 @@ export class CurrentUserUtils {
             linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail;
             linkDocs.author = Doc.CurrentUserEmail;
             linkDocs.data = new List<Doc>([]);
-            linkDocs["acl-Public"] = SharingPermissions.Augment;
+            linkDocs["acl-Guest"] = SharingPermissions.Augment;
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
     }
@@ -788,7 +788,7 @@ export class CurrentUserUtils {
             childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
             childContextMenuLabels: new List<string>(["Add to Dashboards",]),
             childContextMenuIcons: new List<string>(["user-plus",]),
-            "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+            "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
             childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
             _layout_showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, layout_boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
@@ -823,7 +823,7 @@ export class CurrentUserUtils {
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
-                SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
+                SetCachedGroups(["Guest", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         doc.isSystem ?? (doc.isSystem = true);
         doc.title ?? (doc.title = Doc.CurrentUserEmail);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 3f0848d00..b921b3116 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -194,7 +194,7 @@ export class DocumentManager {
         var containerDocContext = srcContext ? [srcContext, doc] : [doc];
         while (
             containerDocContext.length &&
-            containerDocContext[0]?.embedContainer &&
+            DocCast(containerDocContext[0]?.embedContainer) &&
             DocCast(containerDocContext[0].embedContainer)?._type_collection !== CollectionViewType.Docking &&
             (includeExistingViews || !DocumentManager.Instance.getDocumentView(containerDocContext[0]))
         ) {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 37c813279..407396cb4 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -159,7 +159,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
-        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+        docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : 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);
@@ -176,7 +176,7 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(StrCast(group.title))}`;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
-        docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+        docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : undefined);
 
             if (group instanceof Doc) {
@@ -218,7 +218,7 @@ export class SharingManager extends React.Component<{}> {
      */
     shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
         if (layout) this.layoutDocAcls = true;
-        if (shareWith !== 'Public' && shareWith !== 'Override') {
+        if (shareWith !== 'Guest') {
             const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
                 if (user) this.setInternalSharing(user, permission, doc);
@@ -291,7 +291,6 @@ export class SharingManager extends React.Component<{}> {
     private sharingOptions(uniform: boolean, override?: boolean) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift('-multiple-');
-        if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues.map(permission => (
             <option key={permission} value={permission}>
                 {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
@@ -504,7 +503,7 @@ export class SharingManager extends React.Component<{}> {
 
         // 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" });
+        groupListMap.unshift({ title: 'Guest' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             let groupKey = `acl-${StrCast(group.title)}`;
             const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 123090fcf..9b16ecfa7 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,16 +1,17 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Button, ColorPicker, FontSize, IconButton, Size } from 'browndash-components';
+import { Button, ColorPicker, Size } from 'browndash-components';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { FaPlus } from 'react-icons/fa';
 import { Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
-import { AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclPrivate, DocAcl } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { listSpec } from '../../fields/Schema';
 import { ScriptField } from '../../fields/ScriptField';
-import { Cast, DocCast, ImageCast, StrCast } from '../../fields/Types';
+import { Cast, ImageCast, StrCast } from '../../fields/Types';
 import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions, DocUtils } from '../documents/Documents';
 import { CollectionViewType } from '../documents/DocumentTypes';
@@ -25,7 +26,6 @@ import './DashboardView.scss';
 import { Colors } from './global/globalEnums';
 import { MainViewModal } from './MainViewModal';
 import { ButtonType } from './nodes/button/FontIconBox';
-import { FaPlus } from 'react-icons/fa';
 
 enum DashboardGroup {
     MyDashboards,
@@ -167,8 +167,8 @@ export class DashboardView extends React.Component {
                         {this.getDashboards(this.selectedDashboardGroup).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]));
+                                .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)}>
                                     <img
@@ -378,9 +378,6 @@ export class DashboardView extends React.Component {
         const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
 
-        // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = DocListCast(dashboardDoc[DocData].data);
-        dashboardDoc.data = new List<Doc>(dashboardTabs);
         dashboardDoc['pane-count'] = 1;
 
         Doc.AddDocToList(dashboards, 'data', dashboardDoc);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 10985c3f1..a41fc8ded 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -190,23 +190,12 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             }
             const added = docs;
             if (added.length) {
-                Object.keys(Doc.GetProto(this.rootDoc)[DocAcl]) // apply all collection acls (except pseudo-acl 'Me') to each added doc
-                    .filter(key => key !== 'acl-Me')
-                    .forEach(key =>
-                        added.forEach(d => {
-                            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, d, undefined, false);
-                        })
-                    );
-
                 if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
-                    added.map(doc => {
+                    added.forEach(doc => {
                         doc._dragOnlyWithinContainer = undefined;
                         if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
                         Doc.SetContainer(doc, this.rootDoc);
-                        inheritParentAcls(targetDataDoc, doc);
+                        inheritParentAcls(targetDataDoc, doc, true);
                     });
 
                     const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 633401d58..2e10bf346 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -308,21 +308,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     /**
      * @returns the options for the permissions dropdown.
      */
-    getPermissionsSelect(user: string, permission: string) {
-        const dropdownValues: string[] = Object.values(SharingPermissions);
+    getPermissionsSelect(user: string, permission: string, showGuestOptions: boolean) {
+        const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions);
         if (permission === '-multiple-') dropdownValues.unshift(permission);
-        if (user !== 'Override') {
-            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].includes(permission as any))
-                    .map(permission => (
-                        <option className="propertiesView-permisssions-select" key={permission} value={permission}>
-                            {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
-                        </option>
-                    ))}
+                {dropdownValues.map(permission => (
+                    <option className="propertiesView-permisssions-select" key={permission} value={permission}>
+                        {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
+                    </option>
+                ))}
             </select>
         );
     }
@@ -379,7 +374,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 </div>
                 {/* {name !== "Me" ? this.notifyIcon : null} */}
                 <div className="propertiesView-sharingTable-item-permission">
-                    {this.colorACLDropDown(name, admin, permission, showExpansionIcon)}
+                    {this.colorACLDropDown(name, admin, permission, false)}
                     {(permission === 'Owner' && name == 'Me') || showExpansionIcon ? this.expansionIcon : null}
                 </div>
             </div>
@@ -389,13 +384,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) {
+    colorACLDropDown(name: string, admin: boolean, permission: string, showGuestOptions: boolean) {
         var shareImage = ReverseHierarchyMap.get(permission)?.image;
         return (
             <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, showGuestOptions) : concat(shareImage, ' ', permission)}</div>
                     </div>
                 </div>
             </div>
@@ -430,15 +425,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
+        const seldoc = this.layoutDocAcls || !this.selectedDoc ? this.selectedDoc : Doc.GetProto(this.selectedDoc);
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDoc = true;
-            if (this.selectedDoc) {
-                if (this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == '' || this.selectedDoc['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
+            if (seldoc) {
+                if (Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === '' || Doc.GetT(seldoc, 'acl-' + normalizeEmail(eachUser.user.email), 'string', true) === undefined) {
                     userOnDoc = false;
                 }
             }
-            if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email != 'Public' && eachUser.user.email != target.author) {
+            if (userOnDoc && !usersAdded.includes(eachUser.user.email) && eachUser.user.email !== 'guest' && eachUser.user.email != target.author) {
                 usersAdded.push(eachUser.user.email);
             }
         });
@@ -447,15 +443,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
-            var permission = StrCast(target[userKey]);
+            var aclField = Doc.GetT(this.layoutDocAcls ? target : Doc.GetProto(target), userKey, 'string', true);
+            var permission = StrCast(aclField);
             individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
         // adds current user
         var userEmail = Doc.CurrentUserEmail;
+        if (userEmail == 'guest') userEmail = 'Guest';
         const userKey = `acl-${normalizeEmail(userEmail)}`;
-        if (userEmail == 'guest') userEmail = 'Public';
-        if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
+        if (!usersAdded.includes(userEmail) && userEmail !== 'Guest' && userEmail != target.author) {
             var permission;
             if (this.layoutDocAcls) {
                 if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
@@ -473,7 +470,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const groupList = GroupManager.Instance?.allGroups || [];
         groupList.sort(this.sortGroups);
         groupList.map(group => {
-            if (group.title != 'Public' && this.selectedDoc) {
+            if (group.title != 'Guest' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
                     var permission;
@@ -489,17 +486,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         });
 
         // public permission
-        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Public']);
+        const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Guest']);
 
         return (
             <div>
                 <br />
-                Public / Guest Users
-                <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
-                <div>
-                    {' '}
-                    <br></br> Individual Users with Access to this Document{' '}
-                </div>
                 <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
                 {groupTableEntries.length > 0 ? (
                     <div>
@@ -510,6 +501,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                         <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
                     </div>
                 ) : null}
+                Guest
+                <div>{this.colorACLDropDown('Guest', true, publicPermission!, true)}</div>
+                <div>
+                    {' '}
+                    <br></br> Individual Users with Access to this Document{' '}
+                </div>
             </div>
         );
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 13d3b2cdc..8d1b46ebb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -517,7 +517,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     _layout_fitWidth: true,
                     title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                 });
-                inheritParentAcls(this.rootDoc, docToAdd);
+                inheritParentAcls(this.rootDoc, docToAdd, false);
                 CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
             }
         });
@@ -560,7 +560,7 @@ export class CollectionDockingView extends CollectionSubView() {
                             _freeform_backgroundGrid: true,
                             title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
                         });
-                        inheritParentAcls(this.dataDoc, Doc.GetProto(docToAdd));
+                        inheritParentAcls(this.dataDoc, docToAdd, false);
                         CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
                     }
                 })
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 764b1e08a..e2718b52d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -9,7 +9,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
 import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
 import { ImageField } from '../../../../fields/URLField';
-import { GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, SharingPermissions } from '../../../../fields/util';
 import { intersectRect, returnFalse, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
@@ -390,11 +390,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
         newCollection.layout_fitWidth = true;
-        newCollection['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-        selected.forEach(d => {
-            Doc.SetContainer(d, newCollection);
-            d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-        });
+        selected.forEach(d => Doc.SetContainer(d, newCollection));
         this.hideMarquee();
         return newCollection;
     });
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 5bba51ec8..a0b0caf98 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -597,7 +597,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
             if (contentFrameNumber !== undefined) {
                 CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color });
             } else {
-                dv.rootDoc['_' + fieldKey] = color;
+                dv.dataDoc[fieldKey] = color;
             }
         });
     } else {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index d13c09443..ab9eebd78 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -222,7 +222,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             'width',
             'layout_autoHeight',
             'acl-Override',
-            'acl-Public',
+            'acl-Guest',
             'embedContainer',
             'zIndex',
             'height',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 698e09915..6121668e3 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -20,7 +20,6 @@ import {
     AclEdit,
     AclPrivate,
     AclReadonly,
-    AclUnset,
     Animation,
     CachedUpdates,
     DirectLinks,
@@ -134,7 +133,6 @@ export const HierarchyMapping: Map<symbol, { level:aclLevel; name: SharingPermis
     [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; image: string }> = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }]));
 
@@ -380,7 +378,8 @@ export class Doc extends RefField {
         return self.resolvedDataDoc && !self.isTemplateForField ? self : Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
     }
     @computed get __LAYOUT__(): Doc | undefined {
-        const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
+        const self = this[SelfProxy];
+        const templateLayoutDoc = Cast(Doc.LayoutField(self), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
             const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layout_fieldKey, 'layout')];
@@ -389,7 +388,7 @@ export class Doc extends RefField {
             } else {
                 return Cast(layoutField, Doc, null);
             }
-            return Cast(this[SelfProxy][renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
+            return Cast(self[renderFieldKey + '-layout[' + templateLayoutDoc[Id] + ']'], Doc, null) || templateLayoutDoc;
         }
         return undefined;
     }
@@ -480,11 +479,6 @@ 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') && !key.includes(Doc.CurrentUserEmailNormalized))
-                .forEach(key => (doc[key] = Doc.GetProto(container)[key]));
-        }
     }
     export function RunCachedUpdate(doc: Doc, field: string) {
         const update = doc[CachedUpdates][field];
@@ -697,7 +691,7 @@ export namespace Doc {
             Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
         }
         embedding.createdFrom = doc;
-        embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length + 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;
 
@@ -1027,7 +1021,6 @@ export namespace Doc {
             Doc.AddDocToList(Doc.GetProto(copy)[DocData], 'proto_embeddings', copy);
         }
         copy.embedContainer = undefined;
-        Doc.defaultAclPrivate && (copy['acl-Public'] = 'Not Shared');
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
@@ -1087,7 +1080,6 @@ export namespace Doc {
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + '(...' + _applyCount++ + ')');
             target.layout_fieldKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.defaultAclPrivate && (applied['acl-Public'] = 'Not Shared');
             return applied;
         }
         return undefined;
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index eab26ed10..66d1ab094 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -13,7 +13,6 @@ export const DocFields = Symbol('DocFields');
 export const DocCss = Symbol('DocCss');
 export const DocAcl = Symbol('DocAcl');
 export const DirectLinks = Symbol('DocDirectLinks');
-export const AclUnset = Symbol('DocAclUnset');
 export const AclPrivate = Symbol('DocAclOwnerOnly');
 export const AclReadonly = Symbol('DocAclReadOnly');
 export const AclAugment = Symbol('DocAclAugment');
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 034229319..e89cb1fb1 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -131,17 +131,19 @@ export function denormalizeEmail(email: string) {
 /**
  * Copies parent's acl fields to the child
  */
-export function inheritParentAcls(parent: Doc, child: Doc) {
+export function inheritParentAcls(parent: Doc, child: Doc, layoutOnly: boolean) {
     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];
-        const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
-        if (symbol) {
-            const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
-            key.startsWith('acl') && distributeAcls(key, sharePermission, child);
-        }
-    }
+    Object.keys(parent)
+        .filter(key => key.startsWith('acl'))
+        .forEach(key => {
+            // if the default acl mode is private, then don't inherit the acl-guest permission, but set it to private.
+            // const permission: string = key === 'acl-guest' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
+            const parAcl = ReverseHierarchyMap.get(StrCast(parent[key]))?.acl;
+            if (parAcl) {
+                const sharePermission = HierarchyMapping.get(parAcl)?.name;
+                sharePermission && distributeAcls(key, sharePermission, child, undefined, false, layoutOnly);
+            }
+        });
 }
 
 /**
@@ -160,7 +162,6 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
  * Unset: Remove a sharing permission (eg., used )
  */
 export enum SharingPermissions {
-    Unset = 'None',
     Admin = 'Admin',
     Edit = 'Edit',
     Augment = 'Augment',
@@ -233,7 +234,7 @@ function getEffectiveAcl(target: any, user?: string): symbol {
  * @param allowUpgrade whether permissions can be made less restrictive
  * inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
  */
-export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, visited?: Doc[], allowUpgrade?: boolean, layoutOnly = false) {
     const selfKey = `acl-${Doc.CurrentUserEmailNormalized}`;
     if (!visited) visited = [] as Doc[];
     if (!target || visited.includes(target) || key === selfKey) return;
@@ -243,19 +244,19 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     const dataDoc = target[DocData];
     const curVal = ReverseHierarchyMap.get(StrCast(dataDoc[key]))?.level ?? 0;
     const aclVal = ReverseHierarchyMap.get(acl)?.level ?? 0;
-    if (dataDoc && (allowUpgrade !== false|| !dataDoc[key] || curVal > aclVal)) {
+    if (!layoutOnly && dataDoc && (allowUpgrade !== false || !dataDoc[key] || curVal > aclVal)) {
         // propagate ACLs to links, children, and annotations
 
-        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade? true: false));
+        LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, visited, allowUpgrade ? true : false));
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade ? true: false);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true: false);
+            distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
         });
 
         DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
-            distributeAcls(key, acl, d, visited, allowUpgrade? true: false);
-            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade? true: false);
+            distributeAcls(key, acl, d, visited, allowUpgrade ? true : false);
+            d !== d[DocData] && distributeAcls(key, acl, d[DocData], visited, allowUpgrade ? true : false);
         });
 
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
@@ -266,7 +267,7 @@ 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) allowUpgrade is set B) the key doesn't already exist or c) the right being inherited is more restrictive
-    if (GetEffectiveAcl(target) === AclAdmin && (allowUpgrade || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
+    if (GetEffectiveAcl(target) === AclAdmin && (allowUpgrade || !Doc.GetT(target, key, 'boolean', true) || ReverseHierarchyMap.get(StrCast(target[key]))!.level > aclVal)) {
         target[key] = acl;
         layoutDocChanged = true;
     }
-- 
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/collections/CollectionDockingView.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


From f96fb8dc3e430f93b7a7f3a57fa3fb3d83c813c9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Jul 2023 20:51:20 -0400
Subject: removed shared dashboards from SharedDocs -- only available through
 the Dashbaord view now.  Need to change hgihlight of Dashboard button to show
 badge w/ new dashboards the same as is done is shard docs.

---
 src/client/documents/Documents.ts                  |  1 +
 src/client/util/CurrentUserUtils.ts                |  9 +++---
 src/client/util/SharingManager.tsx                 | 24 +++++++++++---
 src/client/views/DashboardView.tsx                 | 37 ++++++++++++----------
 src/client/views/StyleProvider.tsx                 |  2 +-
 .../views/collections/CollectionDockingView.tsx    |  4 ---
 src/client/views/nodes/button/FontIconBadge.scss   |  5 +--
 src/client/views/topbar/TopBar.tsx                 |  8 +++--
 8 files changed, 55 insertions(+), 35 deletions(-)

(limited to 'src/client/views/collections/CollectionDockingView.tsx')

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a39145151..a16742623 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -395,6 +395,7 @@ export class DocumentOptions {
     sidebar_color?: string; // background color of text sidebar
     sidebar_collectionType?: string; // collection type of text sidebar
 
+    data_dashboards?: List<any>; // list of dashboards used in shareddocs;
     text?: string;
     textTransform?: string;
     letterSpacing?: string;
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 0c53caaee..7832b1c87 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -783,11 +783,12 @@ export class CurrentUserUtils {
         const sharedDocOpts:DocumentOptions = {
             title: "My Shared Docs",
             userColor: "rgb(202, 202, 202)",
+            data_dashboards: new List<Doc>(),
             isFolder:true, undoIgnoreFields:new List<string>(['treeViewSortCriterion']),
-            childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
-            childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
-            childContextMenuLabels: new List<string>(["Add to Dashboards",]),
-            childContextMenuIcons: new List<string>(["user-plus",]),
+            // childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
+            // childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
+            // childContextMenuLabels: new List<string>(["Add to Dashboards",]),
+            // childContextMenuIcons: new List<string>(["user-plus",]),
             "acl-Guest": SharingPermissions.Augment, "_acl-Guest": SharingPermissions.Augment,
             childDragAction: "embed", isSystem: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 0, _gridGap: 15, childDontRegisterViews:true,
             // NOTE: treeViewHideTitle & _layout_showTitle is for a TreeView's editable title, _layout_showTitle is for DocumentViews title bar
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 807e812f2..587e1e585 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -49,6 +49,7 @@ const indType = '!indType/';
 const groupType = '!groupType/';
 
 const storage = 'data';
+const dashStorage = 'data_dashboards';
 
 /**
  * A user who also has a sharing doc.
@@ -162,8 +163,9 @@ export class SharingManager extends React.Component<{}> {
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
         docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => {
             distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested ? true : 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);
+            if (permission !== SharingPermissions.None) {
+                Doc.AddDocToList(sharingDoc, doc.dockingConfig ? dashStorage : storage, doc);
+            } else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, ((doc.createdFrom as Doc) || doc).dockingConfig ? dashStorage : storage, (doc.createdFrom as Doc) || doc);
         });
     }, 'set Doc permissions');
 
@@ -186,8 +188,8 @@ export class SharingManager extends React.Component<{}> {
                 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
+                        if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, doc.dockingConfig ? dashStorage : storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+                        else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, ((doc.createdFrom as Doc) || doc).dockingConfig ? dashStorage : storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
                     });
             }
         });
@@ -206,7 +208,13 @@ export class SharingManager extends React.Component<{}> {
             else {
                 DocListCastAsync(user.sharingDoc[storage]).then(userdocs =>
                     DocListCastAsync(group.docsShared).then(dl => {
-                        const filtered = dl?.filter(doc => !userdocs?.includes(doc));
+                        const filtered = dl?.filter(doc => !doc.dockingConfig && !userdocs?.includes(doc));
+                        filtered && userdocs?.push(...filtered);
+                    })
+                );
+                DocListCastAsync(user.sharingDoc[dashStorage]).then(userdocs =>
+                    DocListCastAsync(group.docsShared).then(dl => {
+                        const filtered = dl?.filter(doc => doc.dockingConfig && !userdocs?.includes(doc));
                         filtered && userdocs?.push(...filtered);
                     })
                 );
@@ -250,6 +258,12 @@ export class SharingManager extends React.Component<{}> {
                     userdocs?.splice(0, userdocs.length, ...remaining);
                 })
             );
+            DocListCastAsync(user.sharingDoc[dashStorage]).then(userdocs =>
+                DocListCastAsync(group.docsShared).then(dl => {
+                    const remaining = userdocs?.filter(doc => !dl?.includes(doc)) || [];
+                    userdocs?.splice(0, userdocs.length, ...remaining);
+                })
+            );
         }
     };
 
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 0c871b2b2..b8c89d2ff 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -57,7 +57,7 @@ export class DashboardView extends React.Component {
     };
 
     clickDashboard = (e: React.MouseEvent, dashboard: Doc) => {
-        if (this.selectedDashboardGroup === DashboardGroup.SharedDashboards) {   
+        if (this.selectedDashboardGroup === DashboardGroup.SharedDashboards) {
             DashboardView.openSharedDashboard(dashboard);
         } else {
             Doc.ActiveDashboard = dashboard;
@@ -70,17 +70,17 @@ export class DashboardView extends React.Component {
         if (whichGroup === DashboardGroup.MyDashboards) {
             return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
         }
-        const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig);
+        const sharedDashboards = DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc.dockingConfig);
         return sharedDashboards;
     };
 
     isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
-        // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._type_collection === CollectionViewType.Docking);
+        // const sharedDashboards = DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc._type_collection === CollectionViewType.Docking);
         return !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard);
     };
 
     getSharedDashboards = () => {
-        const sharedDashs = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._type_collection === CollectionViewType.Docking);
+        const sharedDashs = DocListCast(Doc.MySharedDocs.data_dashboards).filter(doc => doc._type_collection === CollectionViewType.Docking);
         return sharedDashs.filter(dashboard => !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard));
     };
 
@@ -163,7 +163,13 @@ export class DashboardView extends React.Component {
                             {'My Dashboards (' + this.getDashboards(DashboardGroup.MyDashboards).length + ')'}
                         </div>
                         <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>
-                            {'Shared Dashboards (' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
+                            Shared Dashboards{' '}
+                            <span
+                                style={{
+                                    background: this.getDashboards(DashboardGroup.SharedDashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'lightgreen' : 'undefined',
+                                }}>
+                                {'(' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
+                            </span>
                         </div>
                     </div>
                     <div className="all-dashboards">
@@ -173,9 +179,11 @@ 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)} 
+                                <div
+                                    className="dashboard-container"
+                                    key={dashboard[Id]} //
+                                    style={{ background: this.isUnviewedSharedDashboard(dashboard) && this.selectedDashboardGroup === DashboardGroup.SharedDashboards ? 'lightgreen' : shared ? 'lightblue' : '' }}
+                                    onContextMenu={e => this.onContextMenu(dashboard, e)}
                                     onClick={e => this.clickDashboard(e, dashboard)}>
                                     <img
                                         src={
@@ -236,10 +244,10 @@ export class DashboardView extends React.Component {
         return CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
     }
 
-    public static openSharedDashboard = (dashboard:Doc) => {
+    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)
@@ -373,8 +381,7 @@ export class DashboardView extends React.Component {
     };
 
     public static createNewDashboard = (id?: string, name?: string, background?: string) => {
-        const dashboards = Doc.MyDashboards;
-        const dashboardCount = DocListCast(dashboards.data).length + 1;
+        const dashboardCount = DocListCast(Doc.MyDashboards.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -391,7 +398,7 @@ export class DashboardView extends React.Component {
 
         dashboardDoc['pane-count'] = 1;
 
-        Doc.AddDocToList(dashboards, 'data', dashboardDoc);
+        Doc.AddDocToList(Doc.MyDashboards, 'data', dashboardDoc);
 
         DashboardView.SetupDashboardTrails(dashboardDoc);
 
@@ -453,10 +460,6 @@ export class DashboardView extends React.Component {
     }
 }
 
-export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) {
-    throw new Error('Function not implemented.');
-}
-
 ScriptingGlobals.add(function createNewDashboard() {
     return DashboardView.createNewDashboard();
 }, 'creates a new dashboard when called');
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 9ba0e5e26..f3471c350 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -280,7 +280,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 if (props?.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform) {
                     return doc?.pointerEvents !== 'none' ? null : (
                         <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
-                            <FontAwesomeIcon icon={'lock'} style={{ color: 'red' }} size="lg" />
+                            <FontAwesomeIcon icon='lock' style={{ color: 'red' }} size="lg" />
                         </div>
                     );
                 }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index b2eaa964c..8d1b46ebb 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -386,10 +386,6 @@ 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/nodes/button/FontIconBadge.scss b/src/client/views/nodes/button/FontIconBadge.scss
index 78f506e57..2ff5c651f 100644
--- a/src/client/views/nodes/button/FontIconBadge.scss
+++ b/src/client/views/nodes/button/FontIconBadge.scss
@@ -1,11 +1,12 @@
 .fontIconBadge {
-    background: red;
+    background: lightgreen;
     width: 15px;
     height: 15px;
     top: 8px;
+    color: black;
     display: block;
     position: absolute;
     right: 5;
     border-radius: 50%;
     text-align: center;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 9f2eafcee..0a0bac998 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,7 +5,7 @@ import { action, computed, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { FaBug, FaCamera, FaStamp } from 'react-icons/fa';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
 import { AclAdmin } from '../../../fields/DocSymbols';
 import { StrCast } from '../../../fields/Types';
 import { GetEffectiveAcl } from '../../../fields/util';
@@ -59,7 +59,11 @@ export class TopBar extends React.Component {
         return (
             <div className="topbar-left">
                 {Doc.ActiveDashboard ? (
-                    <IconButton onClick={this.navigateToHome} icon={<FontAwesomeIcon icon="home" />} color={this.textColor} />
+                    <IconButton
+                        onClick={this.navigateToHome}
+                        icon={<FontAwesomeIcon icon={DocListCast(Doc.MySharedDocs.data_dashboards).some(dash => !DocListCast(Doc.MySharedDocs.viewed).includes(dash)) ? 'portrait' : 'home'} />}
+                        color={this.textColor}
+                    />
                 ) : (
                     <div className="logo-container">
                         <img className="logo" src="/assets/medium-blue-light-blue-circle.png" alt="dash logo"></img>
-- 
cgit v1.2.3-70-g09d2

