From 5550d58f16a0df090cde5a3605f6307b17e70758 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 5 Aug 2021 17:22:42 -0400
Subject: fixed so that links to sidebar annos show the sidebar in a
 LinkPreview

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

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d308345ec..e5c36ea71 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1480,6 +1480,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
+                    nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                     showSidebar={this.SidebarShown}
                     PanelWidth={this.sidebarWidth}
                     setHeight={this.setSidebarHeight}
-- 
cgit v1.2.3-70-g09d2


From d756ffd3daa24270361648454062e98f190ae964 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 Aug 2021 10:34:57 -0400
Subject: fixed webboxes so that you can link to an annotation and it will
 restore the url as well.   a bunch of warnings cleanup.

---
 src/client/util/DocumentManager.ts                        |  6 ++----
 src/client/views/DocumentButtonBar.tsx                    |  2 +-
 src/client/views/InkStrokeProperties.ts                   |  2 +-
 src/client/views/collections/CollectionSubView.tsx        |  2 +-
 src/client/views/collections/CollectionView.tsx           |  2 +-
 src/client/views/collections/TabDocView.tsx               |  4 ++--
 src/client/views/linking/LinkPopup.tsx                    |  1 -
 src/client/views/nodes/DocumentLinksButton.tsx            |  4 ++--
 src/client/views/nodes/PDFBox.tsx                         |  2 +-
 src/client/views/nodes/WebBox.tsx                         | 15 ++++++++++-----
 src/client/views/nodes/formattedText/FormattedTextBox.tsx |  1 -
 src/fields/Doc.ts                                         |  2 +-
 12 files changed, 22 insertions(+), 21 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 27ae0447a..cb0ee411c 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -163,7 +163,8 @@ export class DocumentManager {
         const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
-        var targetDocContextView = targetDocContext && getFirstDocView(targetDocContext);
+        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
+            (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (!docView && annoContainerView) {
             annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
@@ -181,9 +182,6 @@ export class DocumentManager {
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
             } else {  // otherwise try to get a view of the context of the target
-                if (annoContainerView && wasHidden) { // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
-                    targetDocContextView = annoContainerView;
-                }
                 if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
                     targetDocContext._viewTransition = "transform 500ms";
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index df1e6899d..5f09a322c 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -355,7 +355,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
             </div>
-            {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink != doc ? <div className="documentButtonBar-button">
+            {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">
                 <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
             </div> : (null)}
             {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 76ca5b5ec..6444e4451 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -283,7 +283,7 @@ export class InkStrokeProperties {
      */
     @action
     rotatePoint = (target: PointData, origin: PointData, angle: number) => {
-        let rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y };
+        const rotatedTarget = { X: target.X - origin.X, Y: target.Y - origin.Y };
         const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y;
         const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y;
         rotatedTarget.X = newX + origin.X;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a9b5ce465..227635c9b 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -93,7 +93,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
         childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
         IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined;
+            this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ae06d2f4..e65ebf075 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -236,7 +236,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     * Shows the filter icon if it's a user-created collection which isn't a dashboard and has some docFilters applied on it or on the current dashboard.
     */
     @computed get showFilterIcon() {
-        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered()
+        return this.props.Document.viewType !== CollectionViewType.Docking && !Doc.IsSystem(this.props.Document) && this._subView?.IsFiltered();
     }
 
     @observable _subView: any = undefined;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 1f73793b6..1969d728c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -172,8 +172,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const stack: HTMLDivElement = tab.contentItem.parent;
             const header: HTMLDivElement = tab;
             stack.onscroll = action((e: any) => {
-                console.log('scrolling...')
-            })
+                console.log('scrolling...');
+            });
             const moreInfoDrag = document.createElement("div");
             moreInfoDrag.className = "lm_iconWrap";
             tab._disposers.buttonDisposer = reaction(() => this.view, view =>
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 2c4b718f4..df469c53b 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -54,7 +54,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
     @action
     onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.linkURL = e.target.value;
-        console.log(this.linkURL)
     }
 
 
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index b63174e54..7648e866e 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -114,7 +114,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                     DocumentLinksButton.StartLink = this.props.View.props.Document;
                     DocumentLinksButton.StartLinkView = this.props.View;
                 }
-            };
+            }
         }));
     }
 
@@ -291,7 +291,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
                         (null)
                 }
             </div>
-        )
+        );
     }
 
     render() {
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b1f2070f8..23236cf20 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -217,7 +217,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
     sidebarWidth = () => !this.SidebarShown ? 0 :
         this._previewWidth ? PDFBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth);
+            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth)
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 751d63711..ca281d68f 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        if (StrCast(doc.data) !== this._url) this.submitURL(StrCast(doc.data));
+        if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl));
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
         }
@@ -191,7 +191,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 annotationOn: this.rootDoc,
                 y: NumCast(this.layoutDoc._scrollTop)
             });
-        this.addDocument(anchor);
+        this.addDocumentWrapper(anchor);
         return anchor;
     }
 
@@ -444,9 +444,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return view;
     }
 
+    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => doc.webUrl = this._url);
+        return this.addDocument(doc, annotationKey);
+    }
+
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
-        return this.addDocument(doc, sidebarKey);
+        return this.addDocumentWrapper(doc, sidebarKey);
     }
     sidebarBtnDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, (e, down, delta) => {
@@ -482,7 +487,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     sidebarWidth = () => !this.SidebarShown ? 0 :
         this._previewWidth ? WebBox.openSidebarWidth :
             (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() /
-            NumCast(this.layoutDoc.nativeWidth);
+            NumCast(this.layoutDoc.nativeWidth)
 
     @computed get content() {
         return <div className={"webBox-cont" + (!this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.isContentActive() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")}
@@ -561,7 +566,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             anchorMenuClick={this.anchorMenuClick}
                             scrollTop={0}
                             down={this._marqueeing} scaling={returnOne}
-                            addDocument={this.addDocument}
+                            addDocument={this.addDocumentWrapper}
                             docView={this.props.docViewPath().lastElement()}
                             finishMarquee={this.finishMarquee}
                             savedAnnotations={this._savedAnnotations}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e5c36ea71..edd7177de 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -215,7 +215,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         AnchorMenu.Instance.Status = "marquee";
         AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => {
             this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch);
-            console.log("highlight")
             return undefined;
         });
         /**
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6dcf34a3a..0cbfaf067 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1214,7 +1214,7 @@ export namespace Doc {
             case DocumentType.RTF: return "sticky-note";
             case DocumentType.COL:
                 const folder: IconProp = isOpen ? "folder-open" : "folder";
-                const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right"
+                const chevron: IconProp = isOpen ? "chevron-down" : "chevron-right";
                 return !doc?.isFolder ? folder : chevron;
             case DocumentType.WEB: return "globe-asia";
             case DocumentType.SCREENSHOT: return "photo-video";
-- 
cgit v1.2.3-70-g09d2


From e7bbdd3b489fea1c508af53345cd0d1f31685cb9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 Aug 2021 18:03:38 -0400
Subject: collabortion fixes: added new acl for allowing people to edit their
 own text within the same note, fixed playground fields to write to the server
 without updating other clients.

---
 src/client/DocServer.ts                            |  5 +-
 src/client/documents/Documents.ts                  | 10 ++--
 src/client/util/CurrentUserUtils.ts                |  6 +-
 src/client/util/SharingManager.tsx                 | 13 ++--
 src/client/views/DocComponent.tsx                  |  6 +-
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/MarqueeAnnotator.tsx              |  4 +-
 src/client/views/PropertiesView.tsx                | 14 ++---
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++-
 .../formattedText/ProsemirrorExampleTransfer.ts    | 70 ++++++++++++++--------
 src/fields/Doc.ts                                  |  6 +-
 src/fields/util.ts                                 | 22 ++++---
 13 files changed, 105 insertions(+), 69 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 59278d2af..d9ae7d64c 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -59,7 +59,10 @@ export namespace DocServer {
     export var PlaygroundFields: string[];
     export function setPlaygroundFields(livePlaygroundFields: string[]) {
         DocServer.PlaygroundFields = livePlaygroundFields;
-        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
+        livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+    }
+    export function IsPlaygroundField(field: string) {
+        return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ""));
     }
 
     export function setFieldWriteMode(field: string, writeMode: WriteMode) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 24f777e88..93f0880a4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -577,7 +577,7 @@ export namespace Docs {
             dataProps.creationDate = new DateField;
             dataProps[`${fieldKey}-lastModified`] = new DateField;
             dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+            dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps[fieldKey] = data;
 
@@ -588,7 +588,7 @@ export namespace Docs {
 
             viewProps.author = Doc.CurrentUserEmail;
             viewProps["acl-Override"] = "None";
-            viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+            viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true);
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
@@ -699,7 +699,7 @@ export namespace Docs {
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
-            I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+            I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
             I[Initializing] = false;
             return I;
@@ -1038,8 +1038,8 @@ export namespace DocUtils {
             title: ComputedField.MakeFunction("generateLinkTitle(self)") as any,
             "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined,
             "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined,
-            "acl-Public": SharingPermissions.Add,
-            "_acl-Public": SharingPermissions.Add,
+            "acl-Public": SharingPermissions.Augment,
+            "_acl-Public": SharingPermissions.Augment,
             layout_linkView: Cast(Cast(Doc.UserDoc()["template-button-link"], Doc, null).dragFactory, Doc, null),
             linkDisplay: true, hidden: true,
             linkRelationship,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1af6607a7..14c43fb1c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -917,7 +917,7 @@ export class CurrentUserUtils {
                 linkDocs = new Doc(linkDatabaseId, true);
                 (linkDocs as Doc).author = Doc.CurrentUserEmail;
                 (linkDocs as Doc).data = new List<Doc>([]);
-                (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add;
+                (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment;
             }
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
@@ -926,10 +926,10 @@ export class CurrentUserUtils {
             if (!sharedDocs) {
                 sharedDocs = Docs.Create.TreeDocument([], {
                     title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
-                    _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add,
+                    _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
                     _chromeHidden: true, boxShadow: "0 0",
                 }, sharingDocumentId + "outer", sharingDocumentId);
-                (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add;
+                (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
             }
             if (sharedDocs instanceof Doc) {
                 Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d283510b7..a8972b988 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import Select from "react-select";
 import * as RequestPromise from "request-promise";
-import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
+import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc";
 import { List } from "../../fields/List";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
@@ -85,7 +85,8 @@ export class SharingManager extends React.Component<{}> {
     private AclMap = new Map<symbol, string>([
         [AclPrivate, SharingPermissions.None],
         [AclReadonly, SharingPermissions.View],
-        [AclAddonly, SharingPermissions.Add],
+        [AclAugment, SharingPermissions.Augment],
+        [AclSelfEdit, SharingPermissions.SelfEdit],
         [AclEdit, SharingPermissions.Edit],
         [AclAdmin, SharingPermissions.Admin]
     ]);
@@ -101,7 +102,7 @@ export class SharingManager extends React.Component<{}> {
             this.targetDoc = target_doc || target?.props.Document;
             DictationOverlay.Instance.hasActiveModal = true;
             this.isOpen = this.targetDoc !== undefined;
-            this.permissions = SharingPermissions.Add;
+            this.permissions = SharingPermissions.Augment;
         });
     }
 
@@ -366,10 +367,10 @@ export class SharingManager extends React.Component<{}> {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission =>
+        return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
             (
                 <option key={permission} value={permission}>
-                    {permission === SharingPermissions.Add ? "Can Augment" : permission}
+                    {permission}
                 </option>
             )
         );
@@ -546,7 +547,7 @@ export class SharingManager extends React.Component<{}> {
                             </select>
                         ) : (
                                 <div className={"permissions-dropdown"}>
-                                    {permissions === SharingPermissions.Add ? "Can Augment" : permissions}
+                                    {permissions}
                                 </div>
                             )}
                     </div>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index fc36c7e43..99c695a4a 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,4 +1,4 @@
-import { Doc, Opt, DataSym, AclReadonly, AclAddonly, AclPrivate, AclEdit, AclSym, DocListCastAsync, DocListCast, AclAdmin } from '../../fields/Doc';
+import { Doc, Opt, DataSym, AclReadonly, AclAugment, AclPrivate, AclEdit, AclSym, DocListCast, AclAdmin, AclSelfEdit } from '../../fields/Doc';
 import { Touchable } from './Touchable';
 import { computed, action, observable } from 'mobx';
 import { Cast, BoolCast, ScriptCast } from '../../fields/Types';
@@ -131,7 +131,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
         removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean {
             const effectiveAcl = GetEffectiveAcl(this.dataDoc);
             const indocs = doc instanceof Doc ? [doc] : doc;
-            const docs = indocs.filter(doc => effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin);
+            const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin);
             if (docs.length) {
                 setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin
                     Doc.SetInPlace(doc, "isPushpin", undefined, true);
@@ -199,7 +199,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
                         });
                     }
 
-                    if (effectiveAcl === AclAddonly) {
+                    if (effectiveAcl === AclAugment) {
                         added.map(doc => {
                             if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 005e46836..8f37172a0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -107,7 +107,7 @@ export class MainView extends React.Component {
         new InkStrokeProperties();
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "showSidebar", "_sidebarWidthPercent", "_width", "_height", "width", "height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden", "nativeWidth", "_nativeWidth"]); // can play with these fields on someone else's
+            DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", "panX", "panY", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
         }
         DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
 
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 805cda95c..a3a3bce56 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 { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../fields/Doc";
+import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { List } from "../../fields/List";
 import { NumCast } from "../../fields/Types";
@@ -156,7 +156,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
-        const annotationDoc = [AclAddonly, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
+        const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
         return annotationDoc as Doc ?? undefined;
     }
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8136edf04..de437e1df 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -5,7 +5,7 @@ import { intersection } from "lodash";
 import { action, autorun, computed, Lambda, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { ColorState, SketchPicker } from "react-color";
-import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
+import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, Opt, WidthSym, AclSelfEdit } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkField } from "../../fields/InkField";
 import { ComputedField } from "../../fields/ScriptField";
@@ -342,12 +342,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <select className="permissions-select"
             value={permission}
             onChange={e => this.changePermissions(e, user)}>
-            {dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => {
-                return (
-                    <option key={permission} value={permission}>
-                        {permission === SharingPermissions.Add ? "Can Augment" : permission}
-                    </option>);
-            })}
+            {dropdownValues.filter(permission =>
+                !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+                    <option key={permission} value={permission}>  {permission}  </option>)}
         </select>;
     }
 
@@ -402,7 +399,8 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             [AclUnset, "None"],
             [AclPrivate, SharingPermissions.None],
             [AclReadonly, SharingPermissions.View],
-            [AclAddonly, SharingPermissions.Add],
+            [AclAugment, SharingPermissions.Augment],
+            [AclSelfEdit, SharingPermissions.SelfEdit],
             [AclEdit, SharingPermissions.Edit],
             [AclAdmin, SharingPermissions.Admin]
         ]);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 846d28214..19da7ea00 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,6 +1,6 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { AclAddonly, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
@@ -298,7 +298,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this._downX = x;
             this._downY = y;
             const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
-            if ([AclAdmin, AclEdit, AclAddonly].includes(effectiveAcl)) {
+            if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) {
                 PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
             }
             this.clearSelection();
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index edd7177de..f7bed2fa1 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,7 +11,7 @@ import { ReplaceStep } from 'prosemirror-transform';
 import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state";
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from "../../../../fields/Doc";
+import { AclAdmin, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym, AclAugment } from "../../../../fields/Doc";
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
@@ -253,7 +253,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const removeSelection = (json: string | undefined) => json?.indexOf("\"storedMarks\"") === -1 ?
                 json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
 
-            if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) {
+            if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) {
                 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("#")) {
@@ -1401,6 +1401,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._rules!.EnteringStyle = false;
         }
         e.stopPropagation();
+        for (var i = state.selection.from; i < state.selection.to; i++) {
+            const node = state.doc.resolve(i);
+            if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
+                mark.attrs.userid !== Doc.CurrentUserEmail) &&
+                [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+                e.preventDefault();
+            }
+        }
         switch (e.key) {
             case "Escape":
                 this._editorView!.dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from)));
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index d5c77786c..1f78b2204 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,13 +7,14 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym, DocListCast } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Docs } from "../../../documents/Documents";
 import { Utils } from "../../../../Utils";
 import { listSpec } from "../../../../fields/Schema";
 import { List } from "../../../../fields/List";
+import { GetEffectiveAcl } from "../../../../fields/util";
 
 const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
 
@@ -70,25 +71,39 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return false;
     };
 
+    const canEdit = (state: any) => {
+        for (var i = state.selection.from; i < state.selection.to; i++) {
+            const node = state.doc.resolve(i);
+            if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark &&
+                mark.attrs.userid !== Doc.CurrentUserEmail) &&
+                GetEffectiveAcl(props.Document) === AclAugment) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    const toggleEditableMark = (mark: any) => (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && toggleMark(mark)(state, dispatch);
+
     //History commands
     bind("Mod-z", undo);
     bind("Shift-Mod-z", redo);
     !mac && bind("Mod-y", redo);
 
     //Commands to modify Mark
-    bind("Mod-b", toggleMark(schema.marks.strong));
-    bind("Mod-B", toggleMark(schema.marks.strong));
+    bind("Mod-b", toggleEditableMark(schema.marks.strong));
+    bind("Mod-B", toggleEditableMark(schema.marks.strong));
 
-    bind("Mod-e", toggleMark(schema.marks.em));
-    bind("Mod-E", toggleMark(schema.marks.em));
+    bind("Mod-e", toggleEditableMark(schema.marks.em));
+    bind("Mod-E", toggleEditableMark(schema.marks.em));
 
-    bind("Mod-*", toggleMark(schema.marks.code));
+    bind("Mod-*", toggleEditableMark(schema.marks.code));
 
-    bind("Mod-u", toggleMark(schema.marks.underline));
-    bind("Mod-U", toggleMark(schema.marks.underline));
+    bind("Mod-u", toggleEditableMark(schema.marks.underline));
+    bind("Mod-U", toggleEditableMark(schema.marks.underline));
 
     //Commands for lists
-    bind("Ctrl-i", wrapInList(schema.nodes.ordered_list));
+    bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any));
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         /// bcz; Argh!!  replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
@@ -96,6 +111,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             if (!props.LayoutTemplateString) return addTextBox(false, true);
             return true;
         }
+        if (!canEdit(state)) return true;
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
@@ -121,6 +137,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         /// bcz; Argh!!  replace with a onShiftTab prop conditionally handles Tab);
         if (props.Document._singleLine) return true;
+        if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
         if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => {
@@ -140,24 +157,19 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     //Commands to modify BlockType
-    bind("Ctrl->", wrapIn(schema.nodes.blockquote));
-    bind("Alt-\\", setBlockType(schema.nodes.paragraph));
-    bind("Shift-Ctrl-\\", setBlockType(schema.nodes.code_block));
+    bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any)));
+    bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any));
+    bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any));
 
-    bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() })));
-    });
+    bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))));
 
     for (let i = 1; i <= 6; i++) {
-        bind("Shift-Ctrl-" + i, setBlockType(schema.nodes.heading, { level: i }));
+        bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any));
     }
 
     //Command to create a horizontal break line
     const hr = schema.nodes.horizontal_rule;
-    bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView());
-        return true;
-    });
+    bind("Mod-_", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()));
 
     //Command to unselect all
     bind("Escape", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
@@ -173,13 +185,15 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     };
 
     //Command to create a text document to the right of the selected textbox
-    bind("Alt-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => addTextBox(false, true));
+    bind("Alt-Enter", () => addTextBox(false, true));
 
     //Command to create a text document to the bottom of the selected textbox
-    bind("Ctrl-Enter", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => addTextBox(true, true));
+    bind("Ctrl-Enter", () => addTextBox(true, true));
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
     bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+        if (!canEdit(state)) return true;
+
         if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => {
             dispatch(updateBullets(tx, schema));
         })) {
@@ -200,6 +214,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     //command to break line
     bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
         if (addTextBox(true, false)) return true;
+
+        if (!canEdit(state)) return true;
+
         const trange = state.selection.$from.blockRange(state.selection.$to);
         const path = (state.selection.$from as any).path;
         const depth = trange ? liftTarget(trange) : undefined;
@@ -238,18 +255,19 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     //Command to create a blank space
     bind("Space", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
+        if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         dispatch(splitMetadata(marks, state.tr));
         return false;
     });
 
-    bind("Alt-ArrowUp", joinUp);
-    bind("Alt-ArrowDown", joinDown);
-    bind("Mod-BracketLeft", lift);
+    bind("Alt-ArrowUp", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && joinUp(state, dispatch as any));
+    bind("Alt-ArrowDown", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && joinDown(state, dispatch as any));
+    bind("Mod-BracketLeft", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && lift(state, dispatch as any));
 
     const cmd = chainCommands(exitCode, (state, dispatch) => {
         if (dispatch) {
-            dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView());
+            canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView());
             return true;
         }
         return false;
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 0cbfaf067..85ea3cfa9 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -90,7 +90,8 @@ export const DirectLinksSym = Symbol("DirectLinks");
 export const AclUnset = Symbol("AclUnset");
 export const AclPrivate = Symbol("AclOwnerOnly");
 export const AclReadonly = Symbol("AclReadOnly");
-export const AclAddonly = Symbol("AclAddonly");
+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");
@@ -102,7 +103,8 @@ const AclMap = new Map<string, symbol>([
     ["None", AclUnset],
     [SharingPermissions.None, AclPrivate],
     [SharingPermissions.View, AclReadonly],
-    [SharingPermissions.Add, AclAddonly],
+    [SharingPermissions.Augment, AclAugment],
+    [SharingPermissions.SelfEdit, AclSelfEdit],
     [SharingPermissions.Edit, AclEdit],
     [SharingPermissions.Admin, AclAdmin]
 ]);
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 526e5af72..2bb6b45c2 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,5 +1,5 @@
 import { UndoManager } from "../client/util/UndoManager";
-import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing } from "./Doc";
+import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc";
 import { SerializationHelper } from "../client/util/SerializationHelper";
 import { ProxyField, PrefetchProxy } from "./Proxy";
 import { RefField } from "./RefField";
@@ -14,6 +14,7 @@ import CursorField from "./CursorField";
 import { List } from "./List";
 import { SnappingManager } from "../client/util/SnappingManager";
 import { computedFn } from "mobx-utils";
+import { RichTextField } from "./RichTextField";
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -77,7 +78,9 @@ 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 === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly);
-    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Default) && !DocServer.Control.isReadOnly();// && !playgroundMode;
+    const writeToServer =
+        (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) &&
+        !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
@@ -157,9 +160,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
  */
 export enum SharingPermissions {
     Admin = "Admin",
-    Edit = "Can Edit",
-    Add = "Can Augment",
-    View = "Can View",
+    Edit = "Edit",
+    SelfEdit = "Self Edit",
+    Augment = "Augment",
+    View = "View",
     None = "Not Shared"
 }
 
@@ -176,7 +180,7 @@ export function GetEffectiveAcl(target: any, user?: string): symbol {
 
 function getPropAcl(target: any, prop: string | symbol | number) {
     if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin;  // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
-    if (prop && DocServer.PlaygroundFields?.includes(prop.toString())) return AclEdit; // playground props are always editable
+    if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
     return GetEffectiveAcl(target);
 }
 
@@ -192,7 +196,8 @@ function getEffectiveAcl(target: any, user?: string): symbol {
         HierarchyMapping = HierarchyMapping || new Map<symbol, number>([
             [AclPrivate, 0],
             [AclReadonly, 1],
-            [AclAddonly, 2],
+            [AclAugment, 2],
+            [AclSelfEdit, 2.5],
             [AclEdit, 3],
             [AclAdmin, 4]
         ]);
@@ -235,6 +240,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         ["Not Shared", 0],
         ["Can View", 1],
         ["Can Augment", 2],
+        ["Self Edit", 2.5],
         ["Can Edit", 3],
         ["Admin", 4]
     ]);
@@ -294,7 +300,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 = getPropAcl(target, prop);
-    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin) return true;
+    if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
     // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
     if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true;
     // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true;
-- 
cgit v1.2.3-70-g09d2


From 81ae5175aebf7e14a781a73d4a7cbaec0b8f3e4a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 9 Aug 2021 14:26:14 -0400
Subject: fixed augment only text to resize correctly with autoheight. fixed
 acls to cache correctly for new documents. fixed links to be shared across
 users.  fixed links to always default to public.

---
 src/client/DocServer.ts                            |  2 +-
 src/client/documents/Documents.ts                  |  8 ++-
 src/client/util/CurrentUserUtils.ts                | 16 +++---
 src/client/util/LinkManager.ts                     | 67 ++++++++++++++--------
 src/client/util/SettingsManager.tsx                |  3 +-
 src/client/util/SharingManager.tsx                 |  4 +-
 src/client/views/MainView.tsx                      |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 20 ++++---
 src/fields/util.ts                                 |  4 +-
 9 files changed, 79 insertions(+), 47 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 5712a1d64..f4e3a22cf 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,6 +1,6 @@
 import * as io from 'socket.io-client';
 import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc, UpdatingFromServer } from '../fields/Doc';
+import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc';
 import { Utils, emptyFunction } from '../Utils';
 import { SerializationHelper } from './util/SerializationHelper';
 import { RefField } from '../fields/RefField';
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 93f0880a4..48886aa3b 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,7 +1,7 @@
 import { action, runInAction } from "mobx";
 import { basename, extname } from "path";
 import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing } from "../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing, updateCachedAcls } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { HtmlField } from "../../fields/HtmlField";
 import { InkField } from "../../fields/InkField";
@@ -577,7 +577,7 @@ export namespace Docs {
             dataProps.creationDate = new DateField;
             dataProps[`${fieldKey}-lastModified`] = new DateField;
             dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps[fieldKey] = data;
 
@@ -588,13 +588,15 @@ export namespace Docs {
 
             viewProps.author = Doc.CurrentUserEmail;
             viewProps["acl-Override"] = "None";
-            viewProps["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            viewProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true);
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
             !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
                 !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
 
+            updateCachedAcls(dataDoc);
+            updateCachedAcls(viewDoc);
             return viewDoc;
         }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 14c43fb1c..34990e121 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
 import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
+import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { listSpec } from "../../fields/Schema";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types";
+import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
 import { nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
@@ -19,6 +20,7 @@ import { Networking } from "../Network";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
 import { LabelBox } from "../views/nodes/LabelBox";
@@ -31,13 +33,10 @@ import { LinkManager } from "./LinkManager";
 import { Scripting } from "./Scripting";
 import { SearchUtil } from "./SearchUtil";
 import { SelectionManager } from "./SelectionManager";
-import { UndoManager } from "./UndoManager";
-import { SnappingManager } from "./SnappingManager";
-import { InkTool } from "../../fields/InkField";
-import { SharingManager } from "./SharingManager";
-import { computedFn } from "mobx-utils";
 import { ColorScheme } from "./SettingsManager";
-import { Colors } from "../views/global/globalEnums";
+import { SharingManager } from "./SharingManager";
+import { SnappingManager } from "./SnappingManager";
+import { UndoManager } from "./UndoManager";
 
 
 export let resolvedPorts: { server: number, socket: number };
@@ -915,6 +914,7 @@ export class CurrentUserUtils {
             let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
             if (!linkDocs) {
                 linkDocs = new Doc(linkDatabaseId, true);
+                (linkDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail;
                 (linkDocs as Doc).author = Doc.CurrentUserEmail;
                 (linkDocs as Doc).data = new List<Doc>([]);
                 (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment;
@@ -1029,7 +1029,7 @@ export class CurrentUserUtils {
         doc.fontFamily = StrCast(doc.fontFamily, "Arial");
         doc.fontColor = StrCast(doc.fontColor, "black");
         doc.fontHighlight = StrCast(doc.fontHighlight, "");
-        doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true);
+        doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
         doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white");
         doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
         doc.noviceMode = BoolCast(doc.noviceMode, true);
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 8abfd740c..933b98a8c 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,4 +1,4 @@
-import { observable, observe, action } from "mobx";
+import { observable, observe, action, reaction, computed } from "mobx";
 import { computedFn } from "mobx-utils";
 import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
 import { List } from "../../fields/List";
@@ -26,36 +26,41 @@ type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => vo
 export class LinkManager {
 
     @observable static _instance: LinkManager;
-    @observable static userDocs: Doc[] = [];
+    @observable static userLinkDBs: Doc[] = [];
     public static currentLink: Opt<Doc>;
     public static get Instance() { return LinkManager._instance; }
+    public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
+    static links: Doc[] = [];
     constructor() {
         LinkManager._instance = this;
         setTimeout(() => {
-            LinkManager.userDocs = [Doc.LinkDBDoc().data as Doc, ...SharingManager.Instance.users.map(user => user.linkDatabase)];
-            const addLinkToDoc = action((link: Doc): any => {
+            LinkManager.userLinkDBs = [];
+            const addLinkToDoc = (link: Doc) => {
                 const a1 = link?.anchor1;
                 const a2 = link?.anchor2;
-                if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => addLinkToDoc(link)));
-                if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                    Doc.GetProto(a1)[DirectLinksSym].add(link);
-                    Doc.GetProto(a2)[DirectLinksSym].add(link);
-                    Doc.GetProto(link)[DirectLinksSym].add(link);
-                }
-            });
-            const remLinkFromDoc = action((link: Doc): any => {
+                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)[DirectLinksSym].add(link);
+                        Doc.GetProto(a2)[DirectLinksSym].add(link);
+                        Doc.GetProto(link)[DirectLinksSym].add(link);
+                    }
+                }));
+            }
+            const remLinkFromDoc = (link: Doc) => {
                 const a1 = link?.anchor1;
                 const a2 = link?.anchor2;
-                if (a1 instanceof Promise || a2 instanceof Promise) return PromiseValue(a1).then(a1 => PromiseValue(a2).then(a2 => remLinkFromDoc(link)));
-                if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                    Doc.GetProto(a1)[DirectLinksSym].delete(link);
-                    Doc.GetProto(a2)[DirectLinksSym].delete(link);
-                    Doc.GetProto(link)[DirectLinksSym].delete(link);
-                }
-            });
-            const watchUserLinks = (userLinks: List<Doc>) => {
+                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)[DirectLinksSym].delete(link);
+                        Doc.GetProto(a2)[DirectLinksSym].delete(link);
+                        Doc.GetProto(link)[DirectLinksSym].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
-                observe(userLinks, change => {
+                observe(userLinkDBDoc.data as Doc, change => {  // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
                     switch (change.type as any) {
                         case "splice":
                             (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
@@ -64,13 +69,29 @@ export class LinkManager {
                         case "update": //let oldValue = change.oldValue;
                     }
                 }, true);
+                observe(userLinkDBDoc, "data",  // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+                    change => {
+                        switch (change.type as any) {
+                            case "update":
+                                Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
+                                    const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
+                                    const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
+
+                                    const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
+                                    const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
+                                    added?.forEach((link: any) => addLinkToDoc(toRealField(link)));
+                                    removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+                                });
+                        }
+                    }, true);
             };
-            observe(LinkManager.userDocs, change => {
+            observe(LinkManager.userLinkDBs, change => {
                 switch (change.type as any) {
-                    case "splice": (change as any).added.forEach(watchUserLinks); break;
+                    case "splice": (change as any).added.forEach(watchUserLinkDB); break;
                     case "update": //let oldValue = change.oldValue;
                 }
             }, true);
+            LinkManager.addLinkDB(Doc.LinkDBDoc());
         });
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 3987497b8..bd91db779 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -268,7 +268,8 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="tab-column-content">
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
-                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)} onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
+                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
+                            onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
                 </div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index a8972b988..6c4556250 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -23,6 +23,7 @@ import { GroupManager, UserOptions } from "./GroupManager";
 import { GroupMemberView } from "./GroupMemberView";
 import { SelectionManager } from "./SelectionManager";
 import "./SharingManager.scss";
+import { LinkManager } from "./LinkManager";
 
 export interface User {
     email: string;
@@ -154,10 +155,11 @@ export class SharingManager extends React.Component<{}> {
                 }
             });
             return Promise.all(evaluating).then(() => {
-                runInAction(() => {
+                runInAction(async () => {
                     for (const sharer of sharingDocs) {
                         if (!this.users.find(user => user.user.email === sharer.user.email)) {
                             this.users.push(sharer);
+                            LinkManager.addLinkDB(sharer.linkDatabase);
                         }
                     }
                 });
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2be3c3135..3d7446cbe 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -108,7 +108,7 @@ export class MainView extends React.Component {
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
             DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
-                "panX", "panY", "width", "height", "nativeWidth", "nativeHeight",
+                "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height",
                 "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
         }
         DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index f7bed2fa1..9bba15b28 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -431,6 +431,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         this.ProseRef = ele;
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc));
+        // if (this.autoHeight) this.tryUpdateScrollHeight(); 
     }
 
     @undoBatch
@@ -1438,16 +1439,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    tryUpdateScrollHeight() {
+    tryUpdateScrollHeight = () => {
         if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
             const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
-            const proseHeight = !this.ProseRef ? 0 : Array.from(this.ProseRef.children[0].children).reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
-            const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-            if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
-                const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
-                if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
-                    setScrollHeight();
-                } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
+            const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
+            if (children) {
+                const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+                const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+                if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+                    const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
+                    if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
+                        setScrollHeight();
+                    } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
+                }
             }
         }
     }
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 2bb6b45c2..439c4d333 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -141,7 +141,9 @@ export function denormalizeEmail(email: string) {
 export function inheritParentAcls(parent: Doc, child: Doc) {
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
-        key.startsWith("acl") && distributeAcls(key, dataDoc[key], child);
+        // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
+        const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key];
+        key.startsWith("acl") && distributeAcls(key, permission, child);
     }
 }
 
-- 
cgit v1.2.3-70-g09d2


From 136b900ed0b939c2a10b601470f764dcb50809ad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 12 Aug 2021 17:07:41 -0400
Subject: fixed textboxes which were generally a line too short when opened in
 contentfitting view (e.g. stackingView or pivotView)

---
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9bba15b28..1058070f8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1444,12 +1444,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
             const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
             if (children) {
-                const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
-                const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+                var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+                var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
                 if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                     const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
                     if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                         setScrollHeight();
+                        setTimeout(() => {
+                            proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
+                            scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+                            setScrollHeight();
+                        }, 10);
                     } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
                 }
             }
-- 
cgit v1.2.3-70-g09d2


From bd41980b6d8849c5ae875fbe5325f88373b3640b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 17 Aug 2021 11:58:21 -0400
Subject: fixed canEdit func in prosemirrorExampleTransfer to prevent error
 when hitting enter at end of uneditable doc.   fixed scrollheight setting
 error in formattedtextbox caused by assigning null

---
 .../views/nodes/formattedText/FormattedTextBox.tsx    |  6 +++---
 .../nodes/formattedText/ProsemirrorExampleTransfer.ts | 19 +++++++++++--------
 2 files changed, 14 insertions(+), 11 deletions(-)

(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')

diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1058070f8..b0b8ce75d 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -535,7 +535,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
             const min = Math.round(Date.now() / 1000 / 60);
             numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() }));
-            setTimeout(() => this.updateHighlights());
+            setTimeout(this.updateHighlights);
         }
         if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
@@ -882,7 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     }
                 },
             );
-            if (this._recording) setTimeout(() => this.recordDictation());
+            if (this._recording) setTimeout(this.recordDictation);
         }
         var quickScroll: string | undefined = "";
         this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
@@ -1453,7 +1453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         setTimeout(() => {
                             proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
                             scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-                            setScrollHeight();
+                            scrollHeight && setScrollHeight();
                         }, 10);
                     } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template.  So set a timeout and hope its arrived...
                 }
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 1f78b2204..eff400a98 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list";
 import { EditorState, Transaction, TextSelection } from "prosemirror-state";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc";
+import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc";
 import { FormattedTextBox } from "./FormattedTextBox";
 import { Id } from "../../../../fields/FieldSymbols";
 import { Docs } from "../../../documents/Documents";
@@ -72,13 +72,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     };
 
     const canEdit = (state: any) => {
-        for (var i = state.selection.from; i < state.selection.to; i++) {
-            const node = state.doc.resolve(i);
-            if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark &&
-                mark.attrs.userid !== Doc.CurrentUserEmail) &&
-                GetEffectiveAcl(props.Document) === AclAugment) {
-                return false;
-            }
+        switch (GetEffectiveAcl(props.Document)) {
+            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;
     }
-- 
cgit v1.2.3-70-g09d2

