From bd6b9c40f150fab76e8907c45e29fa809f9acae0 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Fri, 2 Apr 2021 19:04:09 -0400 Subject: dashboard sharing initial setup, inherits acls from dashboard - looks like it works --- src/client/views/DocComponent.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 90449ab6c..447daeb02 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,7 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils'; import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; -import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util'; +import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail, inheritParentAcls } from '../../fields/util'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DocUtils } from '../documents/Documents'; import { returnFalse } from '../../Utils'; @@ -198,15 +198,15 @@ export function ViewBoxAnnotatableComponent

{ for (const [key, value] of Object.entries(this.props.Document[AclSym])) { - if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); - //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); - // else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); + if (d.author === denormalizeEmail(key.substring(4)) && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d); } }); } if (effectiveAcl === AclAddonly) { added.map(doc => { + + if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; this.props.layerProvider?.(doc, true); @@ -220,6 +220,8 @@ export function ViewBoxAnnotatableComponent

; if (annoDocs) annoDocs.push(...added); -- cgit v1.2.3-70-g09d2 From 0c6269f2eda7a090ebd7d10298d55b7bc832297b Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 19 Jun 2021 19:41:12 -0400 Subject: trying to solve sidebar issues --- src/client/views/DocComponent.tsx | 1 - src/client/views/MainView.tsx | 6 +++--- src/fields/util.ts | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index f1042de0f..2baf9fbda 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -205,7 +205,6 @@ export function ViewBoxAnnotatableComponent

{ - if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4eeb1fc95..9d7999672 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -103,7 +103,7 @@ export class MainView extends React.Component { } new InkStrokeProperties(); this._sidebarContent.proto = undefined; - DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeHidden"]); // can play with these fields on someone else's + 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.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); @@ -180,8 +180,8 @@ export class MainView extends React.Component { const targClass = targets[0].className.toString(); if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); + (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || + thing.className === "collectionSchema-header-menuOptions")); !check && SearchBox.Instance.resetSearch(true); } !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); diff --git a/src/fields/util.ts b/src/fields/util.ts index f0cc20d74..526e5af72 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -249,8 +249,8 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc layoutDocChanged = true; if (isDashboard) { - DocListCast(target[Doc.LayoutFieldKey(target)]).forEach(d => { - distributeAcls(key, acl, d, inheritingFromCollection, visited); + DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => { + docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited)); }); } } -- cgit v1.2.3-70-g09d2 From 2ac055ce068c2380a76c03d962b1e8b218c213de Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Thu, 24 Jun 2021 18:22:25 -0400 Subject: more cleanup --- src/client/util/GroupMemberView.tsx | 1 - src/client/views/DocComponent.tsx | 7 ------- 2 files changed, 8 deletions(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 927200ed3..b7f89794d 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -50,7 +50,6 @@ export class GroupMemberView extends React.Component { onChange={selectedOption => GroupManager.Instance.addMemberToGroup(this.props.group, (selectedOption as UserOptions).value)} placeholder={"Add members"} value={null} - closeMenuOnSelect={true} styles={{ dropdownIndicator: (base, state) => ({ ...base, diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2baf9fbda..fc2a968fe 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -107,13 +107,6 @@ export function ViewBoxAnnotatableComponent

([ - [AclPrivate, SharingPermissions.None], - [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], - [AclEdit, SharingPermissions.Edit], - [AclAdmin, SharingPermissions.Admin] - ]); lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; -- cgit v1.2.3-70-g09d2 From e7bbdd3b489fea1c508af53345cd0d1f31685cb9 Mon Sep 17 00:00:00 2001 From: bobzel 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/DocComponent.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([]); - (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([ [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 => ( ) ); @@ -546,7 +547,7 @@ export class SharingManager extends React.Component<{}> { ) : (

- {permissions === SharingPermissions.Add ? "Can Augment" : permissions} + {permissions}
)} 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

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

{ 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 { highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap) => { // 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 { return ; } @@ -402,7 +399,8 @@ export class PropertiesView extends React.Component { [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 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>(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, dispatch: (tx: Transaction) => 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, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); @@ -96,6 +111,7 @@ export function buildKeymap>(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>(schema: S, props: any, mapKey bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => 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>(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, dispatch: (tx: Transaction) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any))); + bind("Alt-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any)); + bind("Shift-Ctrl-\\", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any)); - bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))); - }); + bind("Ctrl-m", (state: EditorState, dispatch: (tx: Transaction) => 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, dispatch: (tx: Transaction) => 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, dispatch: (tx: Transaction) => void) => { - dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView()); - return true; - }); + bind("Mod-_", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView())); //Command to unselect all bind("Escape", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -173,13 +185,15 @@ export function buildKeymap>(schema: S, props: any, mapKey }; //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", (state: EditorState, dispatch: (tx: Transaction>) => 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, dispatch: (tx: Transaction) => void) => addTextBox(true, true)); + bind("Ctrl-Enter", () => addTextBox(true, true)); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { + if (!canEdit(state)) return true; + if (!deleteSelection(state, (tx: Transaction>) => { dispatch(updateBullets(tx, schema)); })) { @@ -200,6 +214,9 @@ export function buildKeymap>(schema: S, props: any, mapKey //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => 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>(schema: S, props: any, mapKey //Command to create a blank space bind("Space", (state: EditorState, dispatch: (tx: Transaction) => 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, dispatch: (tx: Transaction) => void) => canEdit(state) && joinUp(state, dispatch as any)); + bind("Alt-ArrowDown", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && joinDown(state, dispatch as any)); + bind("Mod-BracketLeft", (state: EditorState, dispatch: (tx: Transaction) => 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([ ["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([ [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 88074b6b085b0b5542759cb41519de719ee61cee Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 10 Aug 2021 21:34:59 -0400 Subject: fixed error when adding to a collection that does not have a list as its data --- src/client/views/DocComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/DocComponent.tsx') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 99c695a4a..14d32ef12 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -219,7 +219,7 @@ export function ViewBoxAnnotatableComponent

; - if (annoDocs) annoDocs.push(...added); + if (annoDocs instanceof List) annoDocs.push(...added); else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); } -- cgit v1.2.3-70-g09d2