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

properties view, document decorations, and sharing manager show + use correct acl
---
 src/client/util/SharingManager.tsx | 126 ++++++++++++++++++++++++++++++++-----
 1 file changed, 109 insertions(+), 17 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3c05af4bb..5e61f6d3c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -12,6 +12,7 @@ import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types';
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
 import { Utils } from '../../Utils';
 import { DocServer } from '../DocServer';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
 import { CollectionView } from '../views/collections/CollectionView';
 import { DictationOverlay } from '../views/DictationOverlay';
 import { MainViewModal } from '../views/MainViewModal';
@@ -157,21 +158,42 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
+        
+        // var docs: Doc[] = [];
+        // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+        // const dashboard = dashboardList[0]
+        // docs.push(dashboard!)
+        // const tabs = DocListCast(dashboard?.data)
+        // tabs.forEach(tab => {
+        //     docs.push(tab)
+        //     var newDocs = DocListCast(tab.data)
+        //     newDocs.forEach(newDoc => 
+        //         docs.push(newDoc))
+        // })
+
+        // if (!docs){
+        //     return null;
+        // }
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+
+        //var docs: Doc[] = [];
+        docs.push(targetDoc!)
+
+        // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
+                
                 if (permission === SharingPermissions.None) {
                     if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
                 } else {
                     if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
                 }
+                doc = targetDoc
 
                 distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-
                 this.setDashboardBackground(doc, permission as SharingPermissions);
                 if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
                 else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
@@ -190,8 +212,18 @@ export class SharingManager extends React.Component<{}> {
         const acl = `acl-${key}`;
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
-        const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
+        var docs: Doc[] = [];
+        const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+        const dashboard = dashboardList[0]
+        const tabs = DocListCast(dashboard?.data)
+        tabs.forEach(tab => {
+            var newDocs = DocListCast(tab.data)
+            newDocs.forEach(newDoc => docs.push(newDoc))
+        })
+        
+        if (!docs){
+            return null;
+        }
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
             .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -334,13 +366,20 @@ export class SharingManager extends React.Component<{}> {
     //     targetDoc["acl-" + PublicKey] = permission;
     // }
 
-    // private get sharingUrl() {
-    //     if (!this.targetDoc) {
-    //         return undefined;
-    //     }
-    //     const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
-    //     return `${baseUrl}?sharing=true`;
-    // }
+    private get sharingUrl() {
+        if (!this.targetDoc) {
+            return undefined;
+        }
+        const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+        return `${baseUrl}?sharing=true`;
+    }
+
+    /**
+     * Copies the Public sharing url to the user's clipboard.
+     */
+    private copyURL = (e: any) => {
+        Utils.CopyText(this.sharingUrl!)
+    }
 
     // copy = action(() => {
     //     if (this.sharingUrl) {
@@ -357,7 +396,7 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift('-multiple-');
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues
-            .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any))
+            .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
             .map(permission => (
                 <option key={permission} value={permission}>
                     {permission}
@@ -422,6 +461,7 @@ export class SharingManager extends React.Component<{}> {
             this.selectedUsers.forEach(user => {
                 if (user.value.includes(indType)) {
                     this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+
                 } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
@@ -466,6 +506,7 @@ export class SharingManager extends React.Component<{}> {
         if (!this.targetDoc) return null;
         TraceMobx();
         const groupList = GroupManager.Instance?.allGroups || [];
+
         const sortedUsers = this.users
             .slice()
             .sort(this.sortUsers)
@@ -505,13 +546,48 @@ export class SharingManager extends React.Component<{}> {
         // users in common between all docs
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
 
+        // including all users on dashboard
+        // SharingManager.Instance.users.forEach(eachUser => {
+        //     var userOnDashboard = true;
+        //     if(Doc.ActiveDashboard){
+        //         if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+        //             userOnDashboard = false;
+        //         }
+        //     }
+        //     if (userOnDashboard && !commonKeys.includes(`acl-${normalizeEmail(eachUser.user.email)}`)){
+        //         users.push(eachUser.user);
+        //     }
+        // });
+
+
         // the list of users shared with
         const userListContents: (JSX.Element | null)[] = users
-            .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            // .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            .filter(({ user }) => (docs[0]?.author !== user.email))
             .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+                const dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+                // const dashboard = dashboardList[0]
+                const dashboard = Doc.ActiveDashboard
+                var docToUse = dashboard
+
+                // const tabs = DocListCast(dashboard?.data)
+                // tabs.forEach(tab => {
+                //     if (tab.title == targetDoc.title){
+                //         docToUse = tab
+                //     }
+                //     var newdocsList = DocListCast(tab.data)
+                //     newdocsList.forEach(newDoc => {
+                //         if (newDoc.title == targetDoc.title){
+                //             docToUse = newDoc
+                //         }
+                //     })
+                // })
+
+                docToUse = Doc.GetProto(this.targetDoc!)
+
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
-                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+                const permissions = uniform ? StrCast(docToUse?.[userKey]) : '-multiple-';
 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -551,7 +627,7 @@ export class SharingManager extends React.Component<{}> {
                 </div>
             ) : null
         );
-
+    
         // the list of groups shared with
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
@@ -560,11 +636,27 @@ export class SharingManager extends React.Component<{}> {
             const uniform = docs
                 .map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
                 .every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+            const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
 
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
-                    <div className={'padding'}>{StrCast(group.title)}</div>
+                    <div className={'padding'} >{StrCast(group.title)}</div>
+                    <div>
+                        {StrCast(group.title)==='Public' ? (
+                            <div title={"Copy Public URL"} onClick={this.copyURL}>
+                                {/* <IconButton
+                                    size={Size.SMALL}
+                                    isCircle={true}
+                                    hoverStyle="gray"
+                                    onClick={() => this.copyURL}
+                                    icon={<FontAwesomeIcon icon="copy" />}
+                                /> */}
+                                <FontAwesomeIcon icon={'copy'} size={'sm'} />
+                            </div>
+                        ) : (
+                            <div/>
+                        )}
+                    </div>
                     {group instanceof Doc ? (
                         <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
                             <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From abcf1167340f9f411e7712d11f2110625b0938d8 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 4 May 2023 15:45:50 -0400
Subject: simple checkbox so that users can choose to override more private acl
 docs

---
 src/client/util/SharingManager.scss |  2 +-
 src/client/util/SharingManager.tsx  | 24 +++++++++++++++---------
 src/client/views/PropertiesView.tsx |  9 +--------
 3 files changed, 17 insertions(+), 18 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 3cab77816..aa1e11ef5 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -85,11 +85,11 @@
                 .layoutDoc-acls,
                 .myDocs-acls {
                     flex-direction: column;
-                    margin-right: 12;
 
                     label {
                         font-weight: normal;
                         font-style: italic;
+                        padding-right: 12;
                     }
 
                     input {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 40a282db0..aa9fc4433 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -82,6 +82,7 @@ export class SharingManager extends React.Component<{}> {
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
+    @observable private overridePrivate: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
     @observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
 
     // private get linkVisible() {
@@ -161,10 +162,12 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
         // setting the same acl for a docs within the doc being shared
-        var childDocs = DocListCast(target.data)
-        childDocs.map(doc => {
-            this.setInternalSharing(recipient, permission, doc)
-        })
+        if (this.overridePrivate){
+            var childDocs = DocListCast(target.data)
+            childDocs.map(doc => {
+                this.setInternalSharing(recipient, permission, doc)
+            })
+        }
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
@@ -200,10 +203,12 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
         
         // setting the same acl for a docs within the doc being shared
-        var childDocs = DocListCast(target.data)
-        childDocs.map(doc => {
-            this.setInternalGroupSharing(group, permission, doc)
-        })
+        if (this.overridePrivate){
+            var childDocs = DocListCast(target.data)
+            childDocs.map(doc => {
+                this.setInternalGroupSharing(group, permission, doc)
+            })
+        }
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
@@ -275,7 +280,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data))
+                if (this.overridePrivate) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data))
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
@@ -648,6 +653,7 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
+                                        <input type="checkbox" onChange={action(() => (this.overridePrivate = !this.overridePrivate))} checked={this.overridePrivate} /> <label>Override Private </label>
                                         <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
                                     </div>
                                 )}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 984c2dc04..94a6d5639 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -368,14 +368,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     changePermissions = (e: any, user: string) => {
         const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
-        if (user=="Public"){
-            // docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
-            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
-        }
-        else{
-
-            SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
-        }
+        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     };
 
     /**
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


From a366394a1db42628b7299947e6c1be2845c9a77d Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Thu, 15 Jun 2023 13:41:48 -0400
Subject: fixed group sharing distribution bug and group sharing in the
 properties menu

---
 src/client/util/SharingManager.tsx  | 13 +++++++------
 src/client/util/UndoManager.ts      |  2 +-
 src/client/views/PropertiesView.tsx | 11 +++++++----
 src/fields/util.ts                  |  1 +
 4 files changed, 16 insertions(+), 11 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a51a71268..6cbc060fa 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -200,13 +200,14 @@ export class SharingManager extends React.Component<{}> {
         const 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 (this.overridePrivate) {
-            var childDocs = DocListCast(target.data);
-            childDocs.map(doc => {
+        // setting the same acl for a docs within the doc being shared if they haven't been set yet
+        // or if the 'Override Private' checkbox is selected
+        var childDocs = DocListCast(target.data);
+        childDocs.map(doc => {
+            if (this.overridePrivate || doc[acl]==undefined){
                 this.setInternalGroupSharing(group, permission, doc);
-            });
-        }
+            }
+        });
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 6fef9d660..d4532c8ef 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -95,7 +95,7 @@ export namespace UndoManager {
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
-            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
+            // console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + value);
             currentBatch.push(event);
             tempEvents?.push(event);
         }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 43ac2aa88..4674bc0f4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -463,7 +463,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // adds each user to usersAdded
         SharingManager.Instance.users.forEach(eachUser => {
             var userOnDashboard = true;
-            var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`]);
             if (Doc.ActiveDashboard) {
                 if (Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == '' || Doc.ActiveDashboard['acl-' + normalizeEmail(eachUser.user.email)] == undefined) {
                     userOnDashboard = false;
@@ -496,9 +495,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
         const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
         groupListMap.map(group => {
-            if (group.title != 'Public') {
-                const permission = StrCast(target[`acl-${StrCast(group.title)}`]);
-                tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+            if (group.title != 'Public' && Doc.ActiveDashboard) {
+                const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
+                if (Doc.ActiveDashboard[groupKey] != '' && Doc.ActiveDashboard[groupKey] != undefined) {
+                    const permission = StrCast(target[groupKey]);
+                    console.log(permission)
+                    tableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+                }
             }
         });
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 9c0f4ba1f..469e4d944 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -195,6 +195,7 @@ export function SetCachedGroups(groups: string[]) {
 }
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[DocAcl];
+    console.log(target.title, targetAcls)
     if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
 
     const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From f101fd2a3ba25c9fc417d8097f1f10ca622f3dd0 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 16 Jun 2023 13:26:39 -0400
Subject: SharingManager checkboxes don't stay selected

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

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a161b4dc2..da075632b 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -110,6 +110,8 @@ export class SharingManager extends React.Component<{}> {
             }),
             500
         );
+        this.layoutDocAcls = false;
+        this.overridePrevious = false;
     });
 
     constructor(props: {}) {
@@ -446,6 +448,8 @@ export class SharingManager extends React.Component<{}> {
                 2000
             );
 
+            this.layoutDocAcls = false;
+            this.overridePrevious = false;
             this.selectedUsers = null;
         }
     };
-- 
cgit v1.2.3-70-g09d2


From 47f3a2b6d14b71b69ba30c1f5e0b99590dd8c863 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 19 Jun 2023 19:58:17 -0400
Subject: SharingManager UI

---
 src/client/util/SharingManager.scss | 96 ++++++++++++++++++++++++++++++++-----
 src/client/util/SharingManager.tsx  | 32 ++++++++-----
 2 files changed, 105 insertions(+), 23 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 80d5807b4..d942550ca 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -206,7 +206,7 @@
         -moz-user-select: none;
         -ms-user-select: none;
         user-select: none;
-        width: 100%;
+        width: 265px;
         text-align: left;
         font-style: normal;
         font-size: 14;
@@ -214,33 +214,105 @@
         padding: 0;
         align-items: center;
 
+        margin-left: -5px;
+
+        display: flex;
+        padding: 3px;
+        align-items: right;
+        border-bottom: 0.5px solid grey;
+
         .group-info {
             cursor: pointer;
         }
 
-        // &:hover .padding {
-        //     white-space: unset;
-        // }
+        &:hover .padding {
+            overflow-x: unset;
+            white-space: unset;
+            overflow-wrap: break-word;
+        }
 
         .padding {
-            padding: 0 10px 0 0;
-            color: black;
+            // padding: 0 10px 0 0;
+            // color: black;
+            // text-overflow: ellipsis;
+            // overflow: hidden;
+            // white-space: nowrap;
+            // max-width: 40%;
+
+            width: 150px;
+            overflow-x: hidden;
+            display: inline-block;
             text-overflow: ellipsis;
-            overflow: hidden;
             white-space: nowrap;
-            max-width: 40%;
         }
 
         .permissions-dropdown {
-            border: none;
-            height: 25;
-            background-color: #e8e8e8;
+            display: flex;
+            align-items: flex-end;
+            text-align: right;
+            margin-left: auto;
+            margin-right: -12px;
+
         }
 
         .edit-actions {
             display: flex;
             position: absolute;
-            right: -10;
+            align-items: flex-end;
+            right: -10;            
+        }
+
+        .permissions-dropdown-None{
+            align-items: flex-end;
+            margin-right: -12px;
+            padding: 0px;
+            padding-left: 3px;
+            background: grey;
+            color: rgb(71, 71, 71);
+            border-radius: 6px;
+            border: 1px solid rgb(71, 71, 71);
+        }
+        .permissions-dropdown-Edit,
+        .permissions-dropdown-Admin {
+            align-items: flex-end;
+            margin-right: -12px;
+            padding: 0px;
+            padding-left: 3px;
+            background: rgb(254, 254, 199);
+            color: rgb(75, 75, 5);
+            border-radius: 6px;
+            border: 1px solid rgb(75, 75, 5);
+        }
+        .permissions-dropdown-Augment{
+            align-items: flex-end;
+            margin-right: -12px;
+            padding: 0px;
+            padding-left: 3px;
+            background: rgb(208, 255, 208);
+            color:rgb(19, 80, 19);
+            border-radius: 6px;
+            border: 1px solid rgb(19, 80, 19);
+
+        }
+        .permissions-dropdown-View{
+            align-items: flex-end;
+            margin-right: -12px;
+            padding: 0px;
+            padding-left: 3px;
+            background: rgb(213, 213, 255);
+            color: rgb(25, 25, 101);
+            border-radius: 6px;
+            border: 1px solid rgb(25, 25, 101);
+        }
+        .permissions-dropdown-Not-Shared{
+            align-items: flex-end;
+            margin-right: -12px;
+            padding: 0px;
+            padding-left: 3px;
+            background: rgb(255, 207, 207);
+            color: rgb(138, 47, 47);
+            border-radius: 6px;
+            border: 1px solid rgb(138, 47, 47);
         }
 
     }
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index da075632b..8b829158e 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { IconButton, Size } from 'browndash-components';
-import { intersection } from 'lodash';
+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, DocListCast, DocListCastAsync, HierarchyMapping } 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';
@@ -372,7 +372,7 @@ export class SharingManager extends React.Component<{}> {
         if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
         return dropdownValues.map(permission => (
             <option key={permission} value={permission}>
-                {permission}
+                {concat(ReverseHierarchyMap.get(permission)?.image, ' ', permission)}
             </option>
         ));
     }
@@ -527,17 +527,20 @@ export class SharingManager extends React.Component<{}> {
                 const userKey = `acl-${normalizeEmail(user.email)}`;
                 const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
                 const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
-
+                
                 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'} 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)}>
                                     {this.sharingOptions(uniform)}
                                 </select>
                             ) : (
-                                <div className={'permissions-dropdown'}>{permissions}</div>
+                                <div className={`permissions-dropdown-${permissions}`}>
+                                    {concat(ReverseHierarchyMap.get(permissions)?.image, ' ', permissions)}
+                                    &nbsp;
+                                </div>
                             )}
                         </div>
                     </div>
@@ -548,6 +551,7 @@ export class SharingManager extends React.Component<{}> {
         const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
 
         // the owner of the doc and the current user are placed at the top of the user list.
+        const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
                 <div key={'owner'} className={'container'}>
@@ -561,7 +565,10 @@ export class SharingManager extends React.Component<{}> {
                 <div key={'me'} className={'container'}>
                     <span className={'padding'}>Me</span>
                     <div className="edit-actions">
-                        <div className={'permissions-dropdown'}>{effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-'}</div>
+                        <div className={`permissions-dropdown-${curUserPermission}`}>
+                            {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission)?.image, ' ', curUserPermission) : '-multiple-'}
+                            &nbsp;
+                        </div>
                     </div>
                 </div>
             ) : null
@@ -576,7 +583,7 @@ export class SharingManager extends React.Component<{}> {
                 .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
                 .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey]));
             const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
-
+            
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
                     <div className={'padding'}>{StrCast(group.title)}</div>
@@ -585,13 +592,16 @@ 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'} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
+                            <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
                                 {this.sharingOptions(uniform, group.title === 'Override')}
                             </select>
                         ) : (
-                            <div className={'permissions-dropdown'}>{permissions}</div>
+                            <div className={`permissions-dropdown-${permissions}`}>
+                                {concat(ReverseHierarchyMap.get(permissions)?.image, ' ', permissions)}
+                                &nbsp;
+                            </div>
                         )}
                     </div>
                 </div>
-- 
cgit v1.2.3-70-g09d2


From ed3bb0dc180e9fd70311936fa1a60fc10b69f5ad Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Tue, 20 Jun 2023 17:03:59 -0400
Subject: SharingManager UI

---
 src/client/util/SharingManager.scss | 174 +++++++++++++++++++++---------------
 src/client/util/SharingManager.tsx  |  29 +++---
 2 files changed, 119 insertions(+), 84 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index d942550ca..98fea0dcb 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -29,6 +29,7 @@
         }
 
         .share-container {
+
             .share-setup {
                 display: flex;
                 margin-bottom: 20px;
@@ -49,11 +50,15 @@
                     outline: none;
                     text-align: justify; // for Edge
                     text-align-last: end;
+                    font-size: 13px;
+                    min-width: 90px;
+                    height: 36;
+                    margin-left: 2px;
                 }
 
                 .share-button {
-                    height: 105%;
-                    margin-left: 2%;
+                    height: 36;
+                    margin-left: 3%;
                     background-color: black;
                 }
             }
@@ -126,9 +131,10 @@
                 .users-list {
                     font-style: italic;
                     background: #e8e8e8;
+                    border: 2px solid gray;
                     padding-left: 10px;
                     padding-right: 10px;
-                    width: 100%;
+                    width: 97%;
                     overflow-y: scroll;
                     overflow-x: hidden;
                     text-align: left;
@@ -196,29 +202,51 @@
         }
     }
 
+    .title-individual{
+        height: 25px;
+        padding-left: 2;
+        width: 97%;
+        margin-top: 10px;
+        margin-left: -8px;
+        font-size: 14;
+        margin-bottom: -4;
+        border: 2px solid gray;
+        border-bottom: none;
+        align-items: center;
+        display: flex;
+    }
+
+    .title-group{
+        height: 25px;
+        padding-left: 2;
+        width: 97%;
+        margin-top: 10px;
+        margin-left: -.5px;
+        font-size: 14;
+        margin-bottom: -4;
+        border: 2px solid gray;
+        border-bottom: none;
+        align-items: center;
+        display: flex;
+    }
+
     .container {
         display: flex;
         position: relative;
         margin-top: 5px;
-        // margin-bottom: 10px;
+        margin-left: -5px;
         font-size: 22px;
         -webkit-user-select: none;
         -moz-user-select: none;
         -ms-user-select: none;
         user-select: none;
-        width: 265px;
+        width: 97%;
         text-align: left;
         font-style: normal;
-        font-size: 14;
+        font-size: 12.5;
         font-weight: normal;
-        padding: 0;
-        align-items: center;
-
-        margin-left: -5px;
 
-        display: flex;
         padding: 3px;
-        align-items: right;
         border-bottom: 0.5px solid grey;
 
         .group-info {
@@ -232,14 +260,7 @@
         }
 
         .padding {
-            // padding: 0 10px 0 0;
-            // color: black;
-            // text-overflow: ellipsis;
-            // overflow: hidden;
-            // white-space: nowrap;
-            // max-width: 40%;
-
-            width: 150px;
+            max-width: 150px;
             overflow-x: hidden;
             display: inline-block;
             text-overflow: ellipsis;
@@ -261,61 +282,70 @@
             align-items: flex-end;
             right: -10;            
         }
+    }
 
-        .permissions-dropdown-None{
-            align-items: flex-end;
-            margin-right: -12px;
-            padding: 0px;
-            padding-left: 3px;
-            background: grey;
-            color: rgb(71, 71, 71);
-            border-radius: 6px;
-            border: 1px solid rgb(71, 71, 71);
-        }
-        .permissions-dropdown-Edit,
-        .permissions-dropdown-Admin {
-            align-items: flex-end;
-            margin-right: -12px;
-            padding: 0px;
-            padding-left: 3px;
-            background: rgb(254, 254, 199);
-            color: rgb(75, 75, 5);
-            border-radius: 6px;
-            border: 1px solid rgb(75, 75, 5);
-        }
-        .permissions-dropdown-Augment{
-            align-items: flex-end;
-            margin-right: -12px;
-            padding: 0px;
-            padding-left: 3px;
-            background: rgb(208, 255, 208);
-            color:rgb(19, 80, 19);
-            border-radius: 6px;
-            border: 1px solid rgb(19, 80, 19);
-
-        }
-        .permissions-dropdown-View{
-            align-items: flex-end;
-            margin-right: -12px;
-            padding: 0px;
-            padding-left: 3px;
-            background: rgb(213, 213, 255);
-            color: rgb(25, 25, 101);
-            border-radius: 6px;
-            border: 1px solid rgb(25, 25, 101);
-        }
-        .permissions-dropdown-Not-Shared{
-            align-items: flex-end;
-            margin-right: -12px;
-            padding: 0px;
-            padding-left: 3px;
-            background: rgb(255, 207, 207);
-            color: rgb(138, 47, 47);
-            border-radius: 6px;
-            border: 1px solid rgb(138, 47, 47);
-        }
+    .permissions-dropdown-None{
+        height: 100%;
+        min-width: 85px;
+        text-align: right;
+        margin-right: -12px;
+        padding: 0px;
+        padding-left: 3px;
+        background: grey;
+        color: rgb(71, 71, 71);
+        border-radius: 6px;
+        border: 1px solid rgb(71, 71, 71);
+    }
+    .permissions-dropdown-Edit,
+    .permissions-dropdown-Admin {
+        height: 100%;
+        min-width: 85px;
+        text-align: right;
+        margin-right: -12px;
+        padding: 0px;
+        padding-left: 3px;
+        background: rgb(254, 254, 199);
+        color: rgb(75, 75, 5);
+        border-radius: 6px;
+        border: 1px solid rgb(75, 75, 5);
+    }
+    .permissions-dropdown-Augment{
+        height: 100%;
+        min-width: 85px;
+        text-align: right;
+        margin-right: -12px;
+        padding: 0px;
+        padding-left: 3px;
+        background: rgb(208, 255, 208);
+        color:rgb(19, 80, 19);
+        border-radius: 6px;
+        border: 1px solid rgb(19, 80, 19);
 
     }
+    .permissions-dropdown-View{
+        height: 100%;
+        min-width: 85px;
+        text-align: right;
+        margin-right: -12px;
+        padding: 0px;
+        padding-left: 3px;
+        background: rgb(213, 213, 255);
+        color: rgb(25, 25, 101);
+        border-radius: 6px;
+        border: 1px solid rgb(25, 25, 101);
+    }
+    .permissions-dropdown-Not-Shared{
+        height: 100%;
+        min-width: 85px;
+        text-align: right;
+        margin-right: -12px;
+        padding: 0px;
+        padding-left: 3px;
+        background: rgb(255, 207, 207);
+        color: rgb(138, 47, 47);
+        border-radius: 6px;
+        border: 1px solid rgb(138, 47, 47);
+    }
 
     .no-users {
         margin-top: 20px;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 8b829158e..3a3d393a0 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -587,6 +587,7 @@ export class SharingManager extends React.Component<{}> {
             return !permissions ? null : (
                 <div key={groupKey} className={'container'}>
                     <div className={'padding'}>{StrCast(group.title)}</div>
+                    &nbsp;
                     {group instanceof Doc ? (
                         <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
                             <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
@@ -639,9 +640,11 @@ export class SharingManager extends React.Component<{}> {
                                         }),
                                     }}
                                 />
-                                <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
-                                    {this.sharingOptions(true)}
-                                </select>
+                                <div className='permissions-select'>
+                                    <select className={`permissions-dropdown-${this.permissions}`} onChange={this.handlePermissionsChange} value={this.permissions}>
+                                        {this.sharingOptions(true)}
+                                    </select>
+                                </div>
                                 <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
                                     Share
                                 </button>
@@ -666,20 +669,22 @@ 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'))}>
-                                Individuals{' '}
-                                {this.individualSort === 'ascending' ? (
-                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
-                                ) : this.individualSort === 'descending' ? (
-                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
-                                ) : (
-                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
-                                )}
+                                <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'} />
+                                    )}
+                                </div>
                             </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'))}>
-                                <div className="container">
+                                <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' }} />
-- 
cgit v1.2.3-70-g09d2


From 32239432b79664f577bf0c84d4688c4e5cf7f14f Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Fri, 23 Jun 2023 11:11:07 -0400
Subject: acls in proto so that layout acls start cleared

---
 src/client/documents/Documents.ts   | 6 ++----
 src/client/util/SharingManager.tsx  | 3 ++-
 src/client/views/DocComponent.tsx   | 2 +-
 src/client/views/PropertiesView.tsx | 4 +++-
 src/fields/util.ts                  | 2 +-
 5 files changed, 9 insertions(+), 8 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7dde70a2e..05994ebbd 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -839,9 +839,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
             // dataProps['acl-Override'] = SharingPermissions.Unset;
-            if (dataProps['acl-Public'] == undefined && Doc.GetProto(dataProps)['acl-Public'] == undefined){
-                dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            }
+            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.isSystem = viewProps.isSystem;
             dataProps.isDataDoc = true;
@@ -859,7 +857,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;
             }
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3a3d393a0..a95874fd7 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -174,7 +174,7 @@ export class SharingManager extends React.Component<{}> {
 
         // ! ensures it returns true if document has been shared successfully, false otherwise
         return !docs
-            .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
+            .map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc)))
             .map(doc => {
                 doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
 
@@ -339,6 +339,7 @@ export class SharingManager extends React.Component<{}> {
                 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);
 
                 const members: string[] = JSON.parse(StrCast(group.members));
                 const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 98aa9f880..6f350e0fc 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             const permissionString = StrCast(Doc.GetProto(this.props.Document)[key])
                             const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl
                             const permission = HierarchyMapping.get(permissionSymbol)!.name
-                            distributeAcls(key, permission, d)
+                            distributeAcls(key, permission, Doc.GetProto(d))
                         }
                     })
                 );
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 825a802cd..6bca08d23 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -425,12 +425,14 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * @returns the sharing and permissions panel.
      */
     @computed get sharingTable() {
+        console.log('doc', this.selectedDoc)
+        console.log('proto', Doc.GetProto(this.selectedDoc!))
         // all selected docs
         const docs =
             SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
         const target = docs[0];
 
-        const showAdmin = GetEffectiveAcl(this.selectedDoc!) == AclAdmin
+        const showAdmin = GetEffectiveAcl(target) == AclAdmin
         const individualTableEntries = [];
         const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index f31213869..1ac450b9a 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -242,7 +242,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: 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)) {
-        Doc.GetProto(target)[key] = acl;
+        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);
-- 
cgit v1.2.3-70-g09d2


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

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

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

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


From 777381bc14e825a81f7bcb8b8ab305e1ac39ce00 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 11:18:35 -0400
Subject: PropertiesView layout acls work

---
 src/client/util/SharingManager.tsx  | 25 ++++++++++---------
 src/client/views/PropertiesView.tsx | 49 +++++++++++++++++++++++++++----------
 2 files changed, 49 insertions(+), 25 deletions(-)

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 59a88b404..0f7ce26d2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -214,9 +214,7 @@ export class SharingManager extends React.Component<{}> {
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
-        if (acl == 'acl-Public' && this.layoutDocAcls){
-            acl = 'acl-Public-layout';
-        }
+        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]))
@@ -274,7 +272,8 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Called from the properties sidebar to change permissions of a user.
      */
-    shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
+    shareFromPropertiesSidebar = (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 => {
@@ -285,11 +284,17 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overridePrevious) this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data));
-                if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+                if (this.overridePrevious) {
+                    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);
+                }
                 this.setDashboardBackground(doc, permission as SharingPermissions);
             });
         }
+        this.layoutDocAcls = false;
     };
 
     /**
@@ -607,16 +612,12 @@ export class SharingManager extends React.Component<{}> {
             // 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 (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 (groupKey == 'acl-Public-layout') groupKey = 'acl-Public';
                         if (targetDoc['embedContainer']){
                             permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]);
                         }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index d5d09389f..0a8eabdb1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -8,7 +8,7 @@ import { Lambda, action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
-import { Doc, Field, FieldResult, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
+import { Doc, Field, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc';
 import { AclAdmin, DocAcl, DocData, Height, Width } from '../../fields/DocSymbols';
 import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
@@ -303,7 +303,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @undoBatch
     changePermissions = (e: any, user: string) => {
         const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc! : DocCast(doc)[DocData]));
-        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
+        SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls);
     };
 
     /**
@@ -450,8 +450,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // sorts and then adds each user to the table
         usersAdded.sort(this.sortUsers);
         usersAdded.map(userEmail => {
-            const permission = StrCast(target[`acl-${normalizeEmail(userEmail)}`]);
-            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission, false)); // adds each user
+            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]);
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         });
 
         // adds current user
@@ -473,17 +482,33 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             if (group.title != 'Public' && this.selectedDoc) {
                 const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
                 if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
-                    const permission = StrCast(target[groupKey]);
-                    groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission, false));
+                    var permission;
+                    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 permission = StrCast(Doc.GetProto(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']);
+        }
+
         return (
             <div>
                 {' '}
                 Public / Guest Users
-                <div>{this.colorACLDropDown('Public', showAdmin, StrCast(target['acl-Public']), false)}</div>
+                <div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
                 <div>
                     {' '}
                     <br></br> Individual Users with Access to this Document{' '}
@@ -994,12 +1019,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 {!this.openSharing ? null : (
                     <div className="propertiesView-sharing-content">
                         <div className="propertiesView-buttonContainer">
-                            {/* {!Doc.noviceMode ? ( // what is the layout checkbox for?
-                                <div className="propertiesView-acls-checkbox">
-                                    <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
-                                    <div className="propertiesView-acls-checkbox-text">Layout</div>
-                                </div>
-                            ) : null} */}
+                            <div className="propertiesView-acls-checkbox">
+                                <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                                <div className="propertiesView-acls-checkbox-text">Layout</div>
+                            </div>
 
                             {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
-- 
cgit v1.2.3-70-g09d2


From 68e791512551261fd805f841fd88aba373df845a Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Mon, 26 Jun 2023 15:02:12 -0400
Subject: layout always an option in SharingManager, current user layout acl
 bug fix in PropertiesView + SharingManager

---
 src/client/util/SharingManager.scss |  1 +
 src/client/util/SharingManager.tsx  | 54 +++++++++++++++++--------------------
 src/client/views/PropertiesView.tsx | 15 +++++++----
 3 files changed, 36 insertions(+), 34 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 98fea0dcb..86de5ac44 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -86,6 +86,7 @@
                 float: right;
                 align-items: baseline;
                 margin-top: -12;
+                margin-bottom: 10;
 
                 .layoutDoc-acls,
                 .myDocs-acls {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 0f7ce26d2..7593cc025 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -539,21 +539,11 @@ export class SharingManager extends React.Component<{}> {
                 // 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-';
+                    if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
+                    else if (targetDoc['embedContainer'])  permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]);
+                    else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-';
                 }
+                else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
                 
                 return !permissions ? null : (
                     <div key={userKey} className={'container'}>
@@ -578,7 +568,15 @@ export class SharingManager extends React.Component<{}> {
         const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
 
         // the owner of the doc and the current user are placed at the top of the user list.
-        const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
+        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 = HierarchyMapping.get(effectiveAcls[0])!.name
         userListContents.unshift(
             sameAuthor ? (
                 <div key={'owner'} className={'container'}>
@@ -593,7 +591,7 @@ export class SharingManager extends React.Component<{}> {
                     <span className={'padding'}>Me</span>
                     <div className="edit-actions">
                         <div className={`permissions-dropdown-${curUserPermission}`}>
-                            {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission)?.image, ' ', curUserPermission) : '-multiple-'}
+                            {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? concat(ReverseHierarchyMap.get(curUserPermission!)?.image, ' ', curUserPermission) : '-multiple-'}
                             &nbsp;
                         </div>
                     </div>
@@ -613,22 +611,14 @@ export class SharingManager extends React.Component<{}> {
             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;
-                    }
+                    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 (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'}>
@@ -710,7 +700,13 @@ export class SharingManager extends React.Component<{}> {
                             </div>
                         </div>
                     ) : (
-                        <br />
+                        <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>
+                        </div>
                     )}
                     <div className="main-container">
                         <div className={'individual-container'}>
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ca357f830..904755132 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -453,9 +453,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const userKey = `acl-${normalizeEmail(userEmail)}`;
             var permission;
             if (this.layoutDocAcls){
-                if (target[DocAcl][userKey]){
-                    permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
-                }
+                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]);
             }
@@ -465,10 +463,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // adds current user
         var userEmail = Doc.CurrentUserEmail;
+        const userKey = `acl-${normalizeEmail(userEmail)}`;
         if (userEmail == 'guest') userEmail = 'Public';
         if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
-            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, StrCast(target[`acl-${normalizeEmail(userEmail)}`]), false)); // adds each user
-            usersAdded.push(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]);
+            individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
         }
 
         // shift owner to top
-- 
cgit v1.2.3-70-g09d2


From 77b3e319233f05de64276d4d77ff4328aa850a28 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Tue, 4 Jul 2023 16:37:27 -0400
Subject: SharingManager added documentation + clearer copy url button

---
 src/client/util/SharingManager.scss | 12 ++++++++++++
 src/client/util/SharingManager.tsx  | 14 ++++++++++++--
 2 files changed, 24 insertions(+), 2 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 86de5ac44..6dd38ef30 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -28,6 +28,18 @@
             gap: 5px;
         }
 
+        .share-copy-link {
+            display: inline;
+            border-radius: 4px;
+            border: solid gray 1px;
+            font-size: x-small;
+            background: #E8E8E8;
+            color: black;
+            margin-top: -15px;
+            margin-bottom: 15px;
+            width: fit-content;
+        }
+
         .share-container {
 
             .share-setup {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 7593cc025..b557dd5d6 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -649,11 +649,21 @@ 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">
+                        <IconButton size={Size.SMALL} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
                         <b>Share </b>
                         {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
-                        {/* <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/> */}
-                        <IconButton size={Size.SMALL} tooltip="Copy Public URL" onClick={this.copyURL} icon={<FontAwesomeIcon icon="copy" />} />
+                        {/* <button className="share-copy-link" onClick={this.copyURL}>
+                            <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/>
+                            &nbsp;
+                            Copy Public URL
+                        </button> */}
+                        {/* <IconButton size={Size.SMALL} tooltip="Copy Public URL" onClick={this.copyURL} icon={<FontAwesomeIcon icon="copy" />} /> */}
                     </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
+                    </button>
                     <div className={'close-button'} onClick={this.close}>
                         <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
-- 
cgit v1.2.3-70-g09d2


From 79edf434c4925a02ca40b6e96bb98f2098823170 Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 5 Jul 2023 10:22:50 -0400
Subject: SharingManager UI fix from merge with master + Sharing Dashboards
 appear

---
 src/client/util/SharingManager.scss |  4 ++++
 src/client/util/SharingManager.tsx  | 10 +++-------
 src/client/views/DashboardView.tsx  |  3 ++-
 3 files changed, 9 insertions(+), 8 deletions(-)

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

diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 6dd38ef30..065d38d2d 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -26,6 +26,10 @@
         .share-title {
             display: inline-flex;
             gap: 5px;
+
+            .share-info {
+                align-self: center;
+            }
         }
 
         .share-copy-link {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index b557dd5d6..a0c29d6af 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -649,15 +649,11 @@ 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">
-                        <IconButton size={Size.SMALL} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/properties/sharing-and-permissions/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} />
+                        <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-')}
-                        {/* <button className="share-copy-link" onClick={this.copyURL}>
-                            <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/>
-                            &nbsp;
-                            Copy Public URL
-                        </button> */}
-                        {/* <IconButton size={Size.SMALL} tooltip="Copy Public URL" onClick={this.copyURL} icon={<FontAwesomeIcon icon="copy" />} /> */}
                     </p>
                     <button className="share-copy-link" onClick={this.copyURL}>
                         <FontAwesomeIcon title={"Copy Public URL"} icon={'copy'} size={'sm'} onClick={this.copyURL}/>
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index f6d843201..b60b84015 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -67,7 +67,8 @@ 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._type_collection === CollectionViewType.Docking);
+            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;
         }
     };
-- 
cgit v1.2.3-70-g09d2


From 1bc41c42c2070589d9b3f0aef935bec97fef916b Mon Sep 17 00:00:00 2001
From: srichman333 <sarah_n_richman@brown.edu>
Date: Wed, 5 Jul 2023 10:33:18 -0400
Subject: override previous -> override nested

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

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a0c29d6af..534a9cfde 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -81,7 +81,7 @@ export class SharingManager extends React.Component<{}> {
     @observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
     private populating: boolean = false; // whether the list of users is populating or not
     @observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
-    @observable private overridePrevious: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private
+    @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() {
@@ -112,7 +112,7 @@ export class SharingManager extends React.Component<{}> {
             500
         );
         this.layoutDocAcls = false;
-        this.overridePrevious = false;
+        this.overrideNested = false;
     });
 
     constructor(props: {}) {
@@ -163,10 +163,10 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         // setting the same acl for a docs within the doc being shared if they haven't been set yet
-        // or if the 'Override previous' checkbox is selected
+        // or if the 'Override Nested' checkbox is selected
         var childDocs = DocListCast(target.data);
         childDocs.map(doc => {
-            if (this.overridePrevious || doc[acl]==undefined){
+            if (this.overrideNested || doc[acl]==undefined){
                 this.setInternalSharing(recipient, permission, doc);
             }
         });
@@ -207,7 +207,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.overridePrevious || doc[acl]==undefined){
+            if (this.overrideNested || doc[acl]==undefined){
                 this.setInternalGroupSharing(group, permission, doc);
             }
         });
@@ -284,7 +284,7 @@ export class SharingManager extends React.Component<{}> {
             const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
-                if (this.overridePrevious) {
+                if (this.overrideNested) {
                     this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
                 }
                 if (GetEffectiveAcl(doc) === AclAdmin) {
@@ -459,7 +459,7 @@ export class SharingManager extends React.Component<{}> {
             );
 
             this.layoutDocAcls = false;
-            this.overridePrevious = false;
+            this.overrideNested = false;
             this.selectedUsers = null;
         }
     };
@@ -699,7 +699,7 @@ export class SharingManager extends React.Component<{}> {
                             <div className="acl-container">
                                 {Doc.noviceMode ? null : (
                                     <div className="layoutDoc-acls">
-                                        <input type="checkbox" onChange={action(() => (this.overridePrevious = !this.overridePrevious))} checked={this.overridePrevious} /> <label>Override previous </label>
+                                        <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>
                                 )}
-- 
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/util/SharingManager.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/util/SharingManager.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/util/SharingManager.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 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/util/SharingManager.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/util/SharingManager.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 b54443ecb79345ce0be802bd53d9f60ae996b55d Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 11 Jul 2023 16:46:44 -0400
Subject: only show view/not shared for guest options.

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

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

diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 407396cb4..32b0e729c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -288,8 +288,8 @@ export class SharingManager extends React.Component<{}> {
     /**
      * Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share
      */
-    private sharingOptions(uniform: boolean, override?: boolean) {
-        const dropdownValues: string[] = Object.values(SharingPermissions);
+    private sharingOptions(uniform: boolean, showGuestOptions?: boolean) {
+        const dropdownValues: string[] = showGuestOptions ? [SharingPermissions.None, SharingPermissions.View] : Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift('-multiple-');
         return dropdownValues.map(permission => (
             <option key={permission} value={permission}>
@@ -521,7 +521,7 @@ export class SharingManager extends React.Component<{}> {
                     <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')}
+                                {this.sharingOptions(uniform, group.title === 'Guest')}
                             </select>
                         ) : (
                             <div className={`permissions-dropdown-${permissions}`}>
-- 
cgit v1.2.3-70-g09d2


From 51d464e67b22939e7fd1422a7175ec9b58f390a7 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Jul 2023 19:44:16 -0400
Subject: getting shared links to show up when they are made remotely.  fixing
 text to not generate network events when it's not selected

---
 src/client/util/LinkManager.ts                     | 65 +++++++++++-----------
 src/client/util/SharingManager.tsx                 |  3 +-
 src/client/util/UndoManager.ts                     |  9 ++-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 48 ++++++++--------
 src/fields/Doc.ts                                  |  2 +-
 5 files changed, 71 insertions(+), 56 deletions(-)

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

diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index ce422f849..c7f092565 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -46,38 +46,41 @@ export class LinkManager {
         LinkManager._instance = this;
         this.createlink_relationshipLists();
         LinkManager.userLinkDBs = [];
-        const addLinkToDoc = (link: Doc) => {
-            Promise.all([link]).then(linkdoc => {
-                const link = DocCast(linkdoc[0]);
-                Promise.all([link.proto]).then(linkproto => {
-                    Promise.all([link.link_anchor_1, link.link_anchor_2]).then(linkdata => {
-                        const a1 = DocCast(linkdata[0]);
-                        const a2 = DocCast(linkdata[1]);
-                        a1 &&
-                            a2 &&
-                            Promise.all([Doc.GetProto(a1), Doc.GetProto(a2)]).then(
-                                action(protos => {
-                                    (protos[0] as Doc)?.[DirectLinks].add(link);
-                                    (protos[1] as Doc)?.[DirectLinks].add(link);
+        // since this is an action, not a reaction, we get only one shot to add this link to the Anchor docs
+        // Thus make sure all promised values are resolved from link -> link.proto -> link.link_anchor_[1,2] -> link.link_anchor_[1,2].proto
+        // Then add the link to the anchor protos.
+        const addLinkToDoc = (lprom: Doc) =>
+            PromiseValue(lprom).then((link: Opt<Doc>) =>
+                PromiseValue(link?.proto as Doc).then((lproto: Opt<Doc>) =>
+                    Promise.all([lproto?.link_anchor_1 as Doc, lproto?.link_anchor_2 as Doc].map(PromiseValue)).then((lAnchs: Opt<Doc>[]) =>
+                        Promise.all(lAnchs.map(lAnch => PromiseValue(lAnch?.proto as Doc))).then((lAnchProtos: Opt<Doc>[]) =>
+                            Promise.all(lAnchProtos.map(lAnchProto => PromiseValue(lAnchProto?.proto as Doc))).then(
+                                action(lAnchProtoProtos => {
+                                    link && lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].add(link);
+                                    link && lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].add(link);
                                 })
-                            );
-                    });
-                });
-            });
-        };
-        const remLinkFromDoc = (link: Doc) => {
-            const a1 = link?.link_anchor_1;
-            const a2 = link?.link_anchor_2;
-            Promise.all([a1, a2]).then(
-                action(() => {
-                    if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                        Doc.GetProto(a1)[DirectLinks].delete(link);
-                        Doc.GetProto(a2)[DirectLinks].delete(link);
-                        Doc.GetProto(link)[DirectLinks].delete(link);
-                    }
-                })
+                            )
+                        )
+                    )
+                )
             );
-        };
+
+        const remLinkFromDoc = (lprom: Doc) =>
+            PromiseValue(lprom).then((link: Opt<Doc>) =>
+                PromiseValue(link?.proto as Doc).then((lproto: Opt<Doc>) =>
+                    Promise.all([lproto?.link_anchor_1 as Doc, lproto?.link_anchor_2 as Doc].map(PromiseValue)).then((lAnchs: Opt<Doc>[]) =>
+                        Promise.all(lAnchs.map(lAnch => PromiseValue(lAnch?.proto as Doc))).then((lAnchProtos: Opt<Doc>[]) =>
+                            Promise.all(lAnchProtos.map(lAnchProto => PromiseValue(lAnchProto?.proto as Doc))).then(
+                                action(lAnchProtoProtos => {
+                                    link && lAnchs[0] && Doc.GetProto(lAnchs[0])[DirectLinks].delete(link);
+                                    link && lAnchs[1] && Doc.GetProto(lAnchs[1])[DirectLinks].delete(link);
+                                })
+                            )
+                        )
+                    )
+                )
+            );
+
         const watchUserLinkDB = (userLinkDBDoc: Doc) => {
             LinkManager._links.push(...DocListCast(userLinkDBDoc.data));
             const toRealField = (field: Field) => (field instanceof ProxyField ? field.value : field); // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
@@ -156,7 +159,7 @@ export class LinkManager {
         return this.relatedLinker(anchor);
     } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
-        return Array.from(Doc.GetProto(anchor)[DirectLinks] ?? []);
+        return Array.from(Doc.GetProto(anchor)[DirectLinks]);
     } // finds all links that contain the given anchor
 
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 32b0e729c..807e812f2 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -21,6 +21,7 @@ import { SearchBox } from '../views/search/SearchBox';
 import { DocumentManager } from './DocumentManager';
 import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
+import { LinkManager } from './LinkManager';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
 import { undoable } from './UndoManager';
@@ -142,7 +143,7 @@ export class SharingManager extends React.Component<{}> {
                     if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
                         if (!this.users.find(user => user.user.email === newUser.email)) {
                             this.users.push({ user: newUser, sharingDoc, linkDatabase, userColor: StrCast(sharingDoc.userColor) });
-                            // LinkManager.addLinkDB(sharer.linkDatabase);
+                            LinkManager.addLinkDB(linkDatabase);
                         }
                     }
                 })
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index b59af6656..9a6719ea5 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,5 +1,6 @@
 import { observable, action, runInAction } from 'mobx';
 import { Field } from '../../fields/Doc';
+import { RichTextField } from '../../fields/RichTextField';
 import { Without } from '../../Utils';
 
 function getBatchName(target: any, key: string | symbol): string {
@@ -96,7 +97,13 @@ export namespace UndoManager {
 
     export function AddEvent(event: UndoEvent, value?: any): void {
         if (currentBatch && batchCounter.get() && !undoing) {
-            console.log('                         '.slice(0, batchCounter.get()) + 'UndoEvent : ' + event.prop + ' = ' + (value instanceof Array ? value.map(val => Field.toScriptString(val)).join(',') : Field.toScriptString(value)));
+            console.log(
+                '                         '.slice(0, batchCounter.get()) +
+                    'UndoEvent : ' +
+                    event.prop +
+                    ' = ' +
+                    (value instanceof RichTextField ? value.Text : value instanceof Array ? value.map(val => Field.toScriptString(val)).join(',') : Field.toScriptString(value))
+            );
             currentBatch.push(event);
             tempEvents?.push(event);
         }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e5ea8e0c1..a0eb328a1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -34,7 +34,6 @@ import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { MakeTemplate } from '../../../util/DropConverter';
-import { IsFollowLinkScript } from '../../../util/LinkFollower';
 import { LinkManager } from '../../../util/LinkManager';
 import { RTFMarkup } from '../../../util/RTFMarkup';
 import { SelectionManager } from '../../../util/SelectionManager';
@@ -305,11 +304,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             const newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
             const newJson = JSON.stringify(state.toJSON());
             const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
-            const prevLayoutData = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
+            const templateData = this.rootDoc !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template
             const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
             const effectiveAcl = GetEffectiveAcl(dataDoc);
 
-            const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
+            const removeSelection = (json: string | undefined) => json?.replace(/"selection":.*/, '');
 
             if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
@@ -325,12 +324,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     this._applyingChange = this.fieldKey;
                     const textChange = newText !== prevData?.Text;
                     textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
-                    if ((!prevData && !protoData) || newText || (!newText && !protoData)) {
+                    if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
                         // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
-                        if (removeSelection(newJson) !== removeSelection(prevLayoutData?.Data)) {
+                        if (this.props.isContentActive() && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
                             const numstring = NumCast(dataDoc[this.fieldKey], null);
                             dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
-                            dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
+                            dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
                             textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
                             unchanged = false;
                         }
@@ -415,6 +414,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
             tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
+            this.prepareForTyping();
         }
         oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
     };
@@ -1230,6 +1230,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this._disposers.selected = reaction(
             () => this.props.isSelected(),
             action(selected => {
+                selected && this.prepareForTyping();
                 if (FormattedTextBox._globalHighlights.has('Bold Text')) {
                     this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
                 }
@@ -1524,24 +1525,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             }
         }
         selectOnLoad && this._editorView!.focus();
-        // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
+        if (this.props.isContentActive()) this.prepareForTyping();
         if (this._editorView) {
             const tr = this._editorView.state.tr;
             const { from, to } = tr.selection;
             // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated.
             if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
-            this._editorView.dispatch(
-                this._editorView.state.tr.setStoredMarks([
-                    ...(this._editorView.state.storedMarks ?? []),
-                    ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
-                    ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
-                    ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
-                    ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
-                    ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
-                    ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
-                    ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
-                ])
-            );
+
             if (FormattedTextBox.PasteOnLoad) {
                 const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
                 FormattedTextBox.PasteOnLoad = undefined;
@@ -1551,6 +1541,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         FormattedTextBox.DontSelectInitialText = false;
     }
 
+    // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
+    prepareForTyping = () => {
+        this._editorView?.dispatch(
+            this._editorView?.state.tr.setStoredMarks([
+                ...(this._editorView.state.storedMarks?.filter(mark => ![schema.marks.em, schema.marks.underline, schema.marks.pFontFamily, schema.marks.pFontSize, schema.marks.strong, schema.marks.pFontColor].includes(mark.type)) ?? []),
+                ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
+                ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+                ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
+                ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
+                ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
+                ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+                ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
+            ])
+        );
+    };
+
     componentWillUnmount() {
         Object.values(this._disposers).forEach(disposer => disposer?.());
         this.endUndoTypingBatch();
@@ -1831,9 +1837,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             case ' ':
                 if (e.code !== 'Space') {
                     [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
-                        this._editorView!.dispatch(
-                            this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))
-                        );
+                        this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
                 }
                 break;
         }
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index a1aea1563..5a8a6e4b6 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -346,7 +346,7 @@ export class Doc extends RefField {
     /// all of the raw acl's that have been set on this document.  Use GetEffectiveAcl to determine the actual ACL of the doc for editing
     @observable public [DocAcl]: { [key: string]: symbol } = {};
     @observable public [DocCss]: number = 0; // incrementer denoting a change to CSS layout
-    @observable public [DirectLinks]: Set<Doc> = new Set();
+    @observable public [DirectLinks] = new ObservableSet<Doc>();
     @observable public [Animation]: Opt<Doc>;
     @observable public [Highlight]: boolean = false;
     static __Anim(Doc: Doc) {
-- 
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/util/SharingManager.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


From 6503f182a4534119a9ae27a9decf884db70775c3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 13 Jul 2023 21:05:23 -0400
Subject: from last - fixed one problem with group sharing, others exist
 (remove/adding meber from group after sharing has been done doesn't take
 effect until browser refresh)

---
 src/client/util/CurrentUserUtils.ts | 2 +-
 src/client/util/SharingManager.tsx  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 7832b1c87..7d0b477e2 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -783,7 +783,6 @@ 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!,]),
@@ -797,6 +796,7 @@ export class CurrentUserUtils {
         };
         
         DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
+        if (!DocCast(doc.mySharedDocs).data_dashboards) DocCast(doc.mySharedDocs).data_dashboards = new List<Doc>();
     }
 
     /// Import option on the left side button panel 
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 587e1e585..8e5ae7bc0 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -251,7 +251,7 @@ export class SharingManager extends React.Component<{}> {
     removeMember = (group: Doc, emailId: string) => {
         const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
 
-        if (group.docsShared) {
+        if (group.docsShared && user) {
             DocListCastAsync(user.sharingDoc[storage]).then(userdocs =>
                 DocListCastAsync(group.docsShared).then(dl => {
                     const remaining = userdocs?.filter(doc => !dl?.includes(doc)) || [];
-- 
cgit v1.2.3-70-g09d2

