diff options
Diffstat (limited to 'src/fields')
-rw-r--r-- | src/fields/DateField.ts | 4 | ||||
-rw-r--r-- | src/fields/Doc.ts | 90 | ||||
-rw-r--r-- | src/fields/List.ts | 27 | ||||
-rw-r--r-- | src/fields/RichTextField.ts | 2 | ||||
-rw-r--r-- | src/fields/RichTextUtils.ts | 2 | ||||
-rw-r--r-- | src/fields/ScriptField.ts | 16 | ||||
-rw-r--r-- | src/fields/documentSchemas.ts | 12 | ||||
-rw-r--r-- | src/fields/util.ts | 47 |
8 files changed, 133 insertions, 67 deletions
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts index bee62663e..48106d978 100644 --- a/src/fields/DateField.ts +++ b/src/fields/DateField.ts @@ -20,14 +20,14 @@ export class DateField extends ObjectField { } toString() { - return `${this.date.toISOString()}`; + return `${this.date.toLocaleString()}`; } [ToScriptString]() { return `new DateField(new Date(${this.date.toISOString()}))`; } [ToString]() { - return this.date.toISOString(); + return this.date.toLocaleString(); } getDate() { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ea53bc9a2..08d949b5e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { LinkManager } from "../client/util/LinkManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; +import { CollectionDockingView } from "../client/views/collections/CollectionDockingView"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -91,6 +92,9 @@ export async function DocCastAsync(field: FieldResult): Promise<Opt<Doc>> { export function DocListCast(field: FieldResult): Doc[] { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } +export function DocListCastOrNull(field: FieldResult) { + return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; +} export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); @@ -195,6 +199,8 @@ export class Doc extends RefField { @observable private ___fieldKeys: any = {}; + @observable + public [AclSym]: { [key: string]: symbol }; private [UpdatingFromServer]: boolean = false; @@ -204,19 +210,11 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = (clear?: boolean) => { - if (clear) { - this.___fields = {}; - this.___fieldKeys = {}; - } - return this.___fields; - } - @observable - public [AclSym]: { [key: string]: symbol }; + public [FieldsSym](clear?: boolean) { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); - public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } - public [ToString]() { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } + public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`; + public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? "-inaccessible-" : this[SelfProxy].title})`; public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; @@ -346,6 +344,9 @@ export namespace Doc { export function IsBaseProto(doc: Doc) { return GetT(doc, "baseProto", "boolean", true); } + export function IsSystem(doc: Doc) { + return GetT(doc, "system", "boolean", true); + } export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { const hasProto = doc.proto instanceof Doc; const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1; @@ -500,7 +501,7 @@ export namespace Doc { } export function MakeAlias(doc: Doc, id?: string) { - const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); + const alias = !GetT(doc, "isPrototype", "boolean", true) && doc.proto ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); const layout = Doc.LayoutField(alias); if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) { Doc.SetLayout(alias, Doc.MakeAlias(layout)); @@ -781,6 +782,7 @@ export namespace Doc { if (doc) { const delegate = new Doc(id, true); delegate.proto = doc; + delegate.author = Doc.CurrentUserEmail; title && (delegate.title = title); return delegate; } @@ -790,7 +792,9 @@ export namespace Doc { let _applyCount: number = 0; export function ApplyTemplate(templateDoc: Doc) { if (templateDoc) { - const target = Doc.MakeDelegate(new Doc()); + const proto = new Doc(); + proto.author = Doc.CurrentUserEmail; + const target = Doc.MakeDelegate(proto); const targetKey = StrCast(templateDoc.layoutKey, "layout"); const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")"); target.layoutKey = targetKey; @@ -881,6 +885,7 @@ export namespace Doc { export class DocBrush { BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap(); + SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap(); } const brushManager = new DocBrush(); @@ -906,6 +911,34 @@ export namespace Doc { export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } + + export function IsSearchMatch(doc: Doc) { + return computedFn(function IsSearchMatch(doc: Doc) { + return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : + brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined; + })(doc); + } + export function IsSearchMatchUnmemoized(doc: Doc) { + return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : + brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined; + } + export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) { + if (doc && GetEffectiveAcl(doc) !== AclPrivate && GetEffectiveAcl(Doc.GetProto(doc)) !== AclPrivate) { + brushManager.SearchMatchDoc.set(doc, results); + } + return doc; + } + export function SearchMatchNext(doc: Doc, backward: boolean) { + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + const result = brushManager.SearchMatchDoc.get(doc); + const num = Math.abs(result?.searchMatch || 0) + 1; + runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num })); + return doc; + } + export function ClearSearchMatches() { + brushManager.SearchMatchDoc.clear(); + } + export function IsBrushed(doc: Doc) { return computedFn(function IsBrushed(doc: Doc) { return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); @@ -999,10 +1032,10 @@ export namespace Doc { const fieldVal = doc[key]; if (Cast(fieldVal, listSpec("string"), []).length) { const vals = Cast(fieldVal, listSpec("string"), []); - return vals.some(v => v === value); + return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } const fieldStr = Field.toString(fieldVal as Field); - return fieldStr === value; + return fieldStr.includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring } export function deiconifyView(doc: any) { @@ -1016,7 +1049,8 @@ export namespace Doc { doc.layoutKey = deiconify || "layout"; } export function setDocFilterRange(target: Doc, key: string, range?: number[]) { - const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []); + const container = target ?? CollectionDockingView.Instance.props.Document; + const docRangeFilters = Cast(container._docRangeFilters, listSpec("string"), []); for (let i = 0; i < docRangeFilters.length; i += 3) { if (docRangeFilters[i] === key) { docRangeFilters.splice(i, 3); @@ -1027,22 +1061,19 @@ export namespace Doc { docRangeFilters.push(key); docRangeFilters.push(range[0].toString()); docRangeFilters.push(range[1].toString()); - target._docRangeFilters = new List<string>(docRangeFilters); + container._docRangeFilters = new List<string>(docRangeFilters); } } - export function aliasDocs(field: any) { - return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); - } - // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) { + export function setDocFilter(target: Opt<Doc>, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + const container = target ?? CollectionDockingView.Instance.props.Document; const docFilters = Cast(container._docFilters, listSpec("string"), []); runInAction(() => { for (let i = 0; i < docFilters.length; i += 3) { - if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) { + if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match" || modifiers === "remove")) { if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return; docFilters.splice(i, 3); container._docFilters = new List<string>(docFilters); @@ -1052,7 +1083,7 @@ export namespace Doc { if (typeof modifiers === "string") { if (!docFilters.length && modifiers === "match" && value === undefined) { container._docFilters = undefined; - } else { + } else if (modifiers !== "remove") { docFilters.push(key); docFilters.push(value); docFilters.push(modifiers); @@ -1100,6 +1131,15 @@ export namespace Doc { return false; } + export function copyDragFactory(dragFactory: Doc) { + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); + } + return ndoc; + } + export namespace Get { @@ -1248,8 +1288,8 @@ Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); +Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); }); Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; }); -Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); }); Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); diff --git a/src/fields/List.ts b/src/fields/List.ts index a9da75abb..c9e4bd3c1 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -1,14 +1,16 @@ -import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper"; +import { action, observable, runInAction } from "mobx"; +import { alias, list, serializable } from "serializr"; +import { DocServer } from "../client/DocServer"; +import { Scripting } from "../client/util/Scripting"; +import { afterDocDeserialize, autoObject, Deserializable } from "../client/util/SerializationHelper"; import { Field } from "./Doc"; -import { setter, getter, deleteProperty, updateFunction } from "./util"; -import { serializable, alias, list } from "serializr"; -import { observable, action, runInAction } from "mobx"; +import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols"; import { ObjectField } from "./ObjectField"; -import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; -import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols"; -import { Scripting } from "../client/util/Scripting"; -import { DocServer } from "../client/DocServer"; +import { RefField } from "./RefField"; +import { listSpec } from "./Schema"; +import { Cast } from "./Types"; +import { deleteProperty, getter, setter, updateFunction } from "./util"; const listHandlers: any = { /// Mutator methods @@ -322,10 +324,15 @@ class ListImpl<T extends Field> extends ObjectField { return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; } [ToString]() { - return "List"; + return `List(${(this as any).length})`; } } export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[]; export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any; -Scripting.addGlobal("List", List);
\ No newline at end of file +Scripting.addGlobal("List", List); +Scripting.addGlobal(function compareLists(l1: any, l2: any) { + const L1 = Cast(l1, listSpec("string"), []); + const L2 = Cast(l2, listSpec("string"), []); + return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true); +}, "compare two lists");
\ No newline at end of file diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index 2ca5ac082..ae5f301d0 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -28,7 +28,7 @@ export class RichTextField extends ObjectField { } [ToScriptString]() { - return `new RichTextField("${this.Data}", "${this.Text}")`; + return `new RichTextField("${this.Data.replace(/"/g, "\\\"")}", "${this.Text}")`; } [ToString]() { return this.Text; diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index a590c88c4..0b5f14d74 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -278,7 +278,7 @@ export namespace RichTextUtils { } else { docid = backingDocId; } - return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" }); + return schema.node("image", { src, agnostic, width, docid, float: null, location: "add:right" }); }; const textNode = (schema: any, run: docs_v1.Schema$TextRun) => { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 9391f56ac..47efccc99 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -38,14 +38,14 @@ const scriptSchema = createSimpleSchema({ }); async function deserializeScript(script: ScriptField) { - if (script.script.originalScript === 'getCopy(this.dragFactory, true)') { - return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')))?.script; + if (script.script.originalScript === 'copyDragFactory(this.dragFactory)') { + return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('copyDragFactory(this.dragFactory)')))?.script; } if (script.script.originalScript === 'links(self)') { return (script as any).script = (ScriptField.LinksSelf ?? (ScriptField.LinksSelf = ComputedField.MakeFunction('links(self)')))?.script; } - if (script.script.originalScript === 'openOnRight(getCopy(this.dragFactory, true))') { - return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(getCopy(this.dragFactory, true))')))?.script; + if (script.script.originalScript === 'openOnRight(copyDragFactory(this.dragFactory))') { + return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(copyDragFactory(this.dragFactory))')))?.script; } if (script.script.originalScript === 'deiconifyView(self)') { return (script as any).script = (ScriptField.DeiconifyView ?? (ScriptField.DeiconifyView = ComputedField.MakeFunction('deiconifyView(self)')))?.script; @@ -73,6 +73,13 @@ async function deserializeScript(script: ScriptField) { throw new Error("Couldn't compile loaded script"); } (script as any).script = comp; + if (script.setterscript) { + const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options); + if (!compset.compiled) { + throw new Error("Couldn't compile setter script"); + } + (script as any).setterscript = compset; + } } @scriptingGlobal @@ -98,6 +105,7 @@ export class ScriptField extends ObjectField { if (script?.options.capturedVariables) { const doc = Doc.assign(new Doc, script.options.capturedVariables); + doc.system = true; this.captures = new ProxyField(doc); } this.setterscript = setterscript; diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 848a648e1..71294c59c 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -13,10 +13,11 @@ export const documentSchema = createSchema({ links: listSpec(Doc), // computed (readonly) list of links associated with this document // "Location" properties in a very general sense - currentFrame: "number", // current frame of a frame based collection (e.g., a progressive slide) + _curPage: "number", // current page of a page based document + _currentFrame: "number", // current frame of a frame based collection (e.g., a progressive slide) lastFrame: "number", // last frame of a frame based collection (e.g., a progressive slide) activeFrame: "number", // the active frame of a frame based animated document - currentTimecode: "number", // current play back time of a temporal document (video / audio) + _currentTimecode: "number", // current play back time of a temporal document (video / audio) displayTimecode: "number", // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently isLabel: "boolean", // whether the document is a label or not (video / audio) @@ -83,7 +84,10 @@ export const documentSchema = createSchema({ textTransform: "string", treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree + treeViewLockExpandedView: "boolean", // whether the expanded view can be changed + treeViewDefaultExpandedView: "string", // name of field whose contents are displayed by default treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) + treeViewOutlineMode: "boolean", // whether tree view is an outline and clicks edit document titles immediately since double-click opening is turned off // interaction and linking properties ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) @@ -91,13 +95,13 @@ export const documentSchema = createSchema({ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) + followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., add:right, inPlace, default, ) hideLinkButton: "boolean", // whether the blue link counter button should be hidden hideAllLinks: "boolean", // whether all individual blue anchor dots should be hidden linkDisplay: "boolean", // whether a link connection should be shown between link anchor endpoints. isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked - isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) + _isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) lockedPosition: "boolean", // whether the document can be moved (dragged) _lockedTransform: "boolean",// whether a freeformview can pan/zoom diff --git a/src/fields/util.ts b/src/fields/util.ts index 3d832636f..82525f92b 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -5,7 +5,7 @@ import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action, trace } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate } from "./FieldSymbols"; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; @@ -154,14 +154,19 @@ export enum SharingPermissions { /** * Calculates the effective access right to a document for the current user. */ -export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { +export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol { if (!target) return AclPrivate; + + // all changes received fromt the server must be processed as Admin if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin; + // if the current user is the author of the document / the current user is a member of the admin group + const userChecked = user || Doc.CurrentUserEmail; + if (userChecked === (target.__fields?.author || target.author)) return AclAdmin; + if (target[AclSym] && Object.keys(target[AclSym]).length) { - // if the current user is the author of the document / the current user is a member of the admin group - if (Doc.CurrentUserEmail === (target.__fields?.author || target.author) || currentUserGroups.includes("admin")) return AclAdmin; + if (currentUserGroups.includes("Admin")) return AclAdmin; // if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified) if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; @@ -178,7 +183,8 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) for (const [key, value] of Object.entries(target[AclSym])) { // there are issues with storing fields with . in the name, so they are replaced with _ during creation // as a result we need to restore them again during this comparison. - if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { + const entity = key.substring(4).replace('_', '.'); // an individual or a group + if (currentUserGroups.includes(entity) || userChecked === entity) { if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { effectiveAcl = value as symbol; if (effectiveAcl === AclAdmin) break; @@ -208,52 +214,53 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Admin", 4] ]); - let changed = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) + let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) + let dataDocChanged = false; const dataDoc = target[DataSym]; // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) { target[key] = acl; - changed = true; + layoutDocChanged = true; + } + + if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { + dataDoc[key] = acl; + dataDocChanged = true; // maps over the aliases of the document - const aliases = DocListCast(target.aliases); + const aliases = DocListCast(dataDoc.aliases); if (aliases.length) { aliases.map(alias => { alias !== target && distributeAcls(key, acl, alias, inheritingFromCollection); }); } - } - - if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { - dataDoc[key] = acl; - changed = true; - // maps over the children of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { + if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection); } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data, inheritingFromCollection); } }); // maps over the annotations of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { - if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { + if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection); } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data, inheritingFromCollection); } }); } - changed && fetchProto(target); // updates target[AclSym] when changes to acls have been made + layoutDocChanged && fetchProto(target); // updates target[AclSym] when changes to acls have been made + dataDocChanged && fetchProto(dataDoc); } const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", @@ -286,7 +293,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { let prop = in_prop; - if (in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; + if (in_prop === "toString" || in_prop === ToString || in_prop === ToScriptString || in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined; if (prop === LayoutSym) { |