aboutsummaryrefslogtreecommitdiff
path: root/src/fields
diff options
context:
space:
mode:
Diffstat (limited to 'src/fields')
-rw-r--r--src/fields/Doc.ts25
-rw-r--r--src/fields/RichTextField.ts2
-rw-r--r--src/fields/SchemaHeaderField.ts2
-rw-r--r--src/fields/ScriptField.ts12
-rw-r--r--src/fields/documentSchemas.ts2
-rw-r--r--src/fields/util.ts47
6 files changed, 52 insertions, 38 deletions
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index b535fea5a..c4a49962b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -195,6 +195,8 @@ export class Doc extends RefField {
@observable
private ___fieldKeys: any = {};
+ @observable
+ public [AclSym]: { [key: string]: symbol };
private [UpdatingFromServer]: boolean = false;
@@ -204,19 +206,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];
@@ -781,6 +775,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 +785,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;
@@ -806,7 +803,8 @@ export namespace Doc {
target[targetKey] = new PrefetchProxy(templateDoc);
} else {
titleTarget && (Doc.GetProto(target).title = titleTarget);
- Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc);
+ const setDoc = [AclAdmin, AclEdit].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
+ setDoc[targetKey] = new PrefetchProxy(templateDoc);
}
}
return target;
@@ -1044,6 +1042,7 @@ export namespace Doc {
if (docFilters[i] === key && (docFilters[i + 1] === value || modifiers === "match")) {
if (docFilters[i + 2] === modifiers && modifiers && docFilters[i + 1] === value) return;
docFilters.splice(i, 3);
+ container._docFilters = new List<string>(docFilters);
break;
}
}
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/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 07c90f5a2..22ae454f8 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -114,7 +114,7 @@ export class SchemaHeaderField extends ObjectField {
}
[ToScriptString]() {
- return `invalid`;
+ return `header(${this.heading},${this.type}})`;
}
[ToString]() {
return `SchemaHeaderField`;
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index 4cc3a7cc7..52730ed00 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -56,6 +56,9 @@ async function deserializeScript(script: ScriptField) {
if (script.script.originalScript === 'self.userDoc.noviceMode') {
return (script as any).script = (ScriptField.NoviceMode ?? (ScriptField.NoviceMode = ComputedField.MakeFunction('self.userDoc.noviceMode')))?.script;
}
+ if (script.script.originalScript === `selectMainMenu(self)`) {
+ return (script as any).script = (ScriptField.SelectMenu ?? (ScriptField.SelectMenu = ComputedField.MakeFunction('selectMainMenu(self)')))?.script;
+ }
const captures: ProxyField<Doc> = (script as any).captures;
if (captures) {
const doc = (await captures.value())!;
@@ -70,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
@@ -89,11 +99,13 @@ export class ScriptField extends ObjectField {
public static DeiconifyView: Opt<ScriptField>;
public static ConvertToButtons: Opt<ScriptField>;
public static NoviceMode: Opt<ScriptField>;
+ public static SelectMenu: Opt<ScriptField>;
constructor(script: CompiledScript, setterscript?: CompiledScript) {
super();
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 ada13226e..848a648e1 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -102,7 +102,7 @@ export const documentSchema = createSchema({
_lockedTransform: "boolean",// whether a freeformview can pan/zoom
// drag drop properties
- stayInCollection: "boolean",// whether document can be dropped into a different collection
+ _stayInCollection: "boolean",// whether document can be dropped into a different collection
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 4c71572db..d8523dfa2 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,15 +154,16 @@ 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;
if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin;
if (target[AclSym] && Object.keys(target[AclSym]).length) {
+ const userChecked = user || Doc.CurrentUserEmail;
+
// if the current user is the author of the document / the current user is a member of the admin group
- // but not if the doc in question is an alias - the current user will be the author of their alias rather than the original author
- if ((Doc.CurrentUserEmail === (target.__fields?.author || target.author) && !(target.aliasOf || target.__fields?.aliasOf)) || currentUserGroups.includes("admin")) return AclAdmin;
+ if (userChecked === (target.__fields?.author || target.author) || 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;
@@ -179,7 +180,7 @@ 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("_", ".")) {
+ if (currentUserGroups.includes(key.substring(4).replace("_", ".")) || userChecked === key.substring(4).replace("_", ".")) {
if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) {
effectiveAcl = value as symbol;
if (effectiveAcl === AclAdmin) break;
@@ -209,51 +210,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;
-
- // maps over the aliases of the document
- if (target.aliases) {
- DocListCast(target.aliases).map(alias => {
- distributeAcls(key, acl, alias, inheritingFromCollection);
- });
- }
-
+ layoutDocChanged = true;
}
if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
dataDoc[key] = acl;
- changed = true;
+ dataDocChanged = true;
+
+ // maps over the aliases of the document
+ const aliases = DocListCast(dataDoc.aliases);
+ if (aliases.length) {
+ aliases.map(alias => {
+ alias !== target && distributeAcls(key, acl, alias, inheritingFromCollection);
+ });
+ }
// 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 +289,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 === 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) {