aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/DocServer.ts7
-rw-r--r--src/client/util/GroupManager.tsx60
-rw-r--r--src/client/util/SharingManager.tsx42
-rw-r--r--src/client/views/DocComponent.tsx7
-rw-r--r--src/client/views/collections/CollectionView.tsx8
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx6
-rw-r--r--src/client/views/nodes/DocumentView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx8
-rw-r--r--src/fields/Doc.ts67
-rw-r--r--src/fields/util.ts60
10 files changed, 151 insertions, 119 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index eac53bb02..860a8fd92 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -39,9 +39,10 @@ export namespace DocServer {
const docsWithUpdates: { [field: string]: Set<Doc> } = {};
export var PlaygroundFields: string[];
- export function setPlaygroundFields(livePlayougroundFields: string[]) {
- DocServer.PlaygroundFields = livePlayougroundFields;
- livePlayougroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+ export function setPlaygroundFields(livePlaygroundFields: string[]) {
+ console.log("here");
+ DocServer.PlaygroundFields = livePlaygroundFields;
+ livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
}
export function setFieldWriteMode(field: string, writeMode: WriteMode) {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index b14dcf55b..83b206f94 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -33,7 +33,7 @@ export default class GroupManager extends React.Component<{}> {
@observable private selectedUsers: UserOptions[] | null = null; // list of users selected in the "Select users" dropdown.
@observable currentGroup: Opt<Doc>; // the currently selected group.
private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
- private currentUserGroups: Doc[] = [];
+ currentUserGroups: string[] = [];
constructor(props: Readonly<{}>) {
super(props);
@@ -51,7 +51,7 @@ export default class GroupManager extends React.Component<{}> {
DocListCastAsync(this.GroupManagerDoc?.data).then(groups => {
groups?.forEach(group => {
const members: string[] = JSON.parse(StrCast(group.members));
- if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(group);
+ if (members.includes(Doc.CurrentUserEmail)) this.currentUserGroups.push(StrCast(group.groupName));
});
})
.finally(() => console.log(this.currentUserGroups));
@@ -82,11 +82,6 @@ export default class GroupManager extends React.Component<{}> {
return this.users.map(user => ({ label: user, value: user }));
}
-
- get groupMemberships() {
- return this.currentUserGroups;
- }
-
/**
* Makes the GroupManager visible.
*/
@@ -151,6 +146,11 @@ export default class GroupManager extends React.Component<{}> {
);
}
+ getGroupMembers(group: string | Doc): string[] {
+ if (group instanceof Doc) return JSON.parse(StrCast(group.members)) as string[];
+ else return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[];
+ }
+
/**
* @returns the members of the admin group.
*/
@@ -280,52 +280,6 @@ export default class GroupManager extends React.Component<{}> {
}
/**
- * A getter that @returns the interface rendered to view an individual group.
- */
- // private get editingInterface() {
- // const members: string[] = this.currentGroup ? JSON.parse(StrCast(this.currentGroup.members)) : [];
- // const options: UserOptions[] = this.currentGroup ? this.options.filter(option => !(JSON.parse(StrCast(this.currentGroup!.members)) as string[]).includes(option.value)) : [];
- // return (!this.currentGroup ? null :
- // <div className="editing-interface">
- // <div className="editing-header">
- // <b>{this.currentGroup.groupName}</b>
- // <div className={"close-button"} onClick={action(() => this.currentGroup = undefined)}>
- // <FontAwesomeIcon icon={fa.faWindowClose} size={"lg"} />
- // </div>
-
- // {this.hasEditAccess(this.currentGroup) ?
- // <div className="group-buttons">
- // <div className="add-member-dropdown">
- // <Select
- // // isMulti={true}
- // isSearchable={true}
- // options={options}
- // onChange={selectedOption => this.addMemberToGroup(this.currentGroup!, (selectedOption as UserOptions).value)}
- // placeholder={"Add members"}
- // value={null}
- // closeMenuOnSelect={true}
- // />
- // </div>
- // <button onClick={() => this.deleteGroup(this.currentGroup!)}>Delete group</button>
- // </div> :
- // null}
- // </div>
- // <div className="editing-contents">
- // {members.map(member => (
- // <div className="editing-row">
- // <div className="user-email">
- // {member}
- // </div>
- // {this.hasEditAccess(this.currentGroup!) ? <button onClick={() => this.removeMemberFromGroup(this.currentGroup!, member)}> Remove </button> : null}
- // </div>
- // ))}
- // </div>
- // </div>
- // );
-
- // }
-
- /**
* A getter that @returns the main interface for the GroupManager.
*/
private get groupInterface() {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index e74824581..bec6b973b 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -43,18 +43,18 @@ export enum SharingPermissions {
// [SharingPermissions.Edit, "green"]
// ]);
-const HierarchyMapping = new Map<string, string>([
- [SharingPermissions.None, "0"],
- [SharingPermissions.View, "1"],
- [SharingPermissions.Add, "2"],
- [SharingPermissions.Edit, "3"],
+// export const HierarchyMapping = new Map<string, number>([
+// [SharingPermissions.None, 0],
+// [SharingPermissions.View, 1],
+// [SharingPermissions.Add, 2],
+// [SharingPermissions.Edit, 3]
- ["0", SharingPermissions.None],
- ["1", SharingPermissions.View],
- ["2", SharingPermissions.Add],
- ["3", SharingPermissions.Edit]
+// // ["0", SharingPermissions.None],
+// // ["1", SharingPermissions.View],
+// // ["2", SharingPermissions.Add],
+// // ["3", SharingPermissions.Edit]
-]);
+// ]);
interface GroupOptions {
label: string;
@@ -161,14 +161,6 @@ export default class SharingManager extends React.Component<{}> {
const ACL = `ACL-${StrCast(group.groupName)}`;
target[ACL] = permission;
- // const sharingDoc = this.sharingDoc!;
- // if (permission === SharingPermissions.None) {
- // const metadata = sharingDoc[StrCast(group.groupName)];
- // if (metadata) sharingDoc[StrCast(group.groupName)] = undefined;
- // }
- // else {
- // sharingDoc[StrCast(group.groupName)] = permission;
- // }
group.docsShared ? Doc.IndexOf(target, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(target) : group.docsShared = new List<Doc>([target]);
@@ -206,7 +198,7 @@ export default class SharingManager extends React.Component<{}> {
const users: ValidatedUser[] = this.users.filter(user => members.includes(user.user.email));
users.forEach(user => Doc.RemoveDocFromList(user.notificationDoc, storage, doc));
- })
+ });
}
}
@@ -224,8 +216,16 @@ export default class SharingManager extends React.Component<{}> {
target[ACL] = permission;
- if (permission !== SharingPermissions.None) Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target);
- else Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target);
+ if (permission !== SharingPermissions.None) {
+ !this.sharedUsers.includes(recipient) && this.sharedUsers.push(recipient);
+
+ Doc.IndexOf(target, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, target);
+ }
+ else {
+ const index = this.sharedUsers.findIndex(user => user === recipient);
+ index !== -1 && this.sharedUsers.splice(index, 1);
+ Doc.IndexOf(target, DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, target);
+ }
}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 9b9a28f0f..e8c34d931 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -7,6 +7,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
import { List } from '../../fields/List';
import { DateField } from '../../fields/DateField';
import { ScriptField } from '../../fields/ScriptField';
+import { getEffectiveAcl } from '../../fields/util';
/// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
@@ -137,10 +138,12 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
const targetDataDoc = this.props.Document[DataSym];
const docList = DocListCast(targetDataDoc[this.annotationKey]);
const added = docs.filter(d => !docList.includes(d));
+ console.log("here");
+ const effectiveAcl = getEffectiveAcl(this.dataDoc);
if (added.length) {
- if (this.dataDoc[AclSym] === AclReadonly) {
+ if (effectiveAcl === AclReadonly) {
return false;
- } else if (this.dataDoc[AclSym] === AclAddonly) {
+ } else if (effectiveAcl === AclAddonly) {
added.map(doc => Doc.AddDocToList(targetDataDoc, this.annotationKey, doc));
} else {
added.map(doc => doc.context = this.props.Document);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 26abd2529..0dfe9c52a 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -17,7 +17,7 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { TraceMobx } from '../../../fields/util';
+import { TraceMobx, getEffectiveAcl } from '../../../fields/util';
import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
@@ -132,10 +132,12 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
const targetDataDoc = this.props.Document[DataSym];
const docList = DocListCast(targetDataDoc[this.props.fieldKey]);
const added = docs.filter(d => !docList.includes(d));
+ console.log("here");
+ const effectiveAcl = getEffectiveAcl(this.dataDoc);
if (added.length) {
- if (this.dataDoc[AclSym] === AclReadonly) {
+ if (effectiveAcl === AclReadonly) {
return false;
- } else if (this.dataDoc[AclSym] === AclAddonly) {
+ } else if (effectiveAcl === AclAddonly) {
added.map(doc => Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc));
} else {
added.map(doc => {
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index f1438fd54..d480c76d0 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -36,7 +36,7 @@ import { WebBox } from "./WebBox";
import { InkingStroke } from "../InkingStroke";
import React = require("react");
import { RecommendationsBox } from "../RecommendationsBox";
-import { TraceMobx } from "../../../fields/util";
+import { TraceMobx, getEffectiveAcl } from "../../../fields/util";
import { ScriptField } from "../../../fields/ScriptField";
import XRegExp = require("xregexp");
@@ -184,8 +184,8 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
const bindings = this.CreateBindings(onClick, onInput);
// layoutFrame = splits.length > 1 ? splits[0] + splits[1].replace(/{([^{}]|(?R))*}/, replacer4) : ""; // might have been more elegant if javascript supported recursive patterns
-
- return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || this.layoutDoc[AclSym] === AclPrivate) ? (null) :
+ console.log("here");
+ return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || getEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) :
<ObserverJsxParser
key={42}
blacklistedAttrs={[]}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 297a9f2a5..e277ddc36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,7 +12,7 @@ import { listSpec } from "../../../fields/Schema";
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from '../../../fields/util';
+import { TraceMobx, getEffectiveAcl } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
@@ -1188,7 +1188,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
render() {
- if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
+ console.log("here");
+ if (getEffectiveAcl(this.props.Document) === AclPrivate) return (null);
if (!(this.props.Document instanceof Doc)) return (null);
const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fc63dfbf5..a0dbcd980 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../../fields/DateField';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym } from "../../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclSym, AclEdit } from "../../../../fields/Doc";
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
@@ -24,7 +24,7 @@ import { RichTextField } from "../../../../fields/RichTextField";
import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { createSchema, makeInterface } from "../../../../fields/Schema";
import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types";
-import { TraceMobx, OVERRIDE_ACL } from '../../../../fields/util';
+import { TraceMobx, OVERRIDE_ACL, getEffectiveAcl } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from "../../../DocServer";
@@ -226,7 +226,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
const json = JSON.stringify(state.toJSON());
- if (!this.dataDoc[AclSym]) {
+ // if (!this.dataDoc[AclSym]) { // what?
+ console.log("here");
+ if (getEffectiveAcl(this.dataDoc) === AclEdit) {
if (!this._applyingChange && json.replace(/"selection":.*/, "") !== curProto?.Data.replace(/"selection":.*/, "")) {
this._applyingChange = true;
(curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())));
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index bbc9d388a..c965dc282 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -17,8 +17,9 @@ import { RichTextField } from "./RichTextField";
import { listSpec } from "./Schema";
import { ComputedField } from "./ScriptField";
import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
-import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
+import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, getEffectiveAcl } from "./util";
import { LinkManager } from "../client/util/LinkManager";
+import { SharingPermissions } from "../client/util/SharingManager";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -100,23 +101,39 @@ export const AclEdit = Symbol("AclEdit");
export const UpdatingFromServer = Symbol("UpdatingFromServer");
const CachedUpdates = Symbol("Cached updates");
+const AclMap = new Map<string, symbol>([
+ [SharingPermissions.None, AclPrivate],
+ [SharingPermissions.View, AclReadonly],
+ [SharingPermissions.Add, AclAddonly],
+ [SharingPermissions.Edit, AclEdit]
+]);
export function fetchProto(doc: Doc) {
+ console.log("in fetchproto");
if (doc.author !== Doc.CurrentUserEmail) { // storing acls for groups needs to be extended here - AclSym should store a datastructure that stores information about permissions
- const acl = Doc.Get(doc, "ACL", true);
- switch (acl) {
- case "ownerOnly":
- doc[AclSym] = AclPrivate;
- return undefined;
- case "readOnly":
- doc[AclSym] = AclReadonly;
- break;
- case "addOnly":
- doc[AclSym] = AclAddonly;
- break;
- case "edit":
- doc[AclSym] = AclEdit;
- }
+
+ const permissions: { [key: string]: symbol } = {};
+
+ Object.keys(doc).forEach(key => {
+ if (key.startsWith("ACL")) permissions[key] = AclMap.get(StrCast(doc[key]))!;
+ });
+
+ doc[AclSym] = permissions;
+
+ // const acl = Doc.Get(doc, "ACL", true);
+ // switch (acl) {
+ // case "ownerOnly":
+ // doc[AclSym] = AclPrivate;
+ // return undefined;
+ // case "readOnly":
+ // doc[AclSym] = AclReadonly;
+ // break;
+ // case "addOnly":
+ // doc[AclSym] = AclAddonly;
+ // break;
+ // // case "edit":
+ // // doc[AclSym] = AclEdit;
+ // }
}
if (doc.proto instanceof Promise) {
@@ -134,10 +151,10 @@ export class Doc extends RefField {
set: setter,
get: getter,
// getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter
- has: (target, key) => target[AclSym] !== AclPrivate && key in target.__fields,
+ has: (target, key) => getEffectiveAcl(target) !== AclPrivate && key in target.__fields,
ownKeys: target => {
const obj = {} as any;
- if (target[AclSym] !== AclPrivate) Object.assign(obj, target.___fields);
+ if (getEffectiveAcl(target) !== AclPrivate) Object.assign(obj, target.___fields);
runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__);
return Object.keys(obj);
},
@@ -191,11 +208,11 @@ export class Doc extends RefField {
private [Self] = this;
private [SelfProxy]: any;
- public [AclSym]: any = undefined;
+ public [AclSym]: any;
public [WidthSym] = () => NumCast(this[SelfProxy]._width);
public [HeightSym] = () => NumCast(this[SelfProxy]._height);
public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; }
- public [ToString]() { return `Doc(${this[AclSym] === AclPrivate ? "-inaccessible-" : this.title})`; }
+ public [ToString]() { return `Doc(${getEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; }
public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; }
public get [DataSym]() {
const self = this[SelfProxy];
@@ -823,7 +840,8 @@ export namespace Doc {
}
// don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
export function IsBrushedDegreeUnmemoized(doc: Doc) {
- if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return 0;
+ console.log("here");
+ if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return 0;
return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0;
}
export function IsBrushedDegree(doc: Doc) {
@@ -832,15 +850,15 @@ export namespace Doc {
})(doc);
}
export function BrushDoc(doc: Doc) {
-
- if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
+ console.log("here");
+ if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
brushManager.BrushedDoc.set(doc, true);
brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
return doc;
}
export function UnBrushDoc(doc: Doc) {
- if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return doc;
+ if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
brushManager.BrushedDoc.delete(doc);
brushManager.BrushedDoc.delete(Doc.GetProto(doc));
return doc;
@@ -870,7 +888,8 @@ export namespace Doc {
}
const highlightManager = new HighlightBrush();
export function IsHighlighted(doc: Doc) {
- if (!doc || doc[AclSym] === AclPrivate || Doc.GetProto(doc)[AclSym] === AclPrivate) return false;
+ console.log("here");
+ if (!doc || getEffectiveAcl(doc) === AclPrivate || getEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return false;
return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc));
}
export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 7bb090a93..a3e7a36f8 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,5 +1,5 @@
import { UndoManager } from "../client/util/UndoManager";
-import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate } from "./Doc";
+import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym, AclSym, AclPrivate, AclEdit, AclReadonly, AclAddonly } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField, PrefetchProxy } from "./Proxy";
import { RefField } from "./RefField";
@@ -9,6 +9,7 @@ import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
import { DocServer } from "../client/DocServer";
import { ComputedField } from "./ScriptField";
import { ScriptCast } from "./Types";
+import GroupManager from "../client/util/GroupManager";
function _readOnlySetter(): never {
@@ -107,11 +108,57 @@ export function OVERRIDE_ACL(val: boolean) {
_overrideAcl = val;
}
+const HierarchyMapping = new Map<symbol, number>([
+ [AclPrivate, 0],
+ [AclReadonly, 1],
+ [AclAddonly, 2],
+ [AclEdit, 3]
+]);
+
+export function getEffectiveAcl(target: any): symbol {
+
+ console.log("in getEffectiveAcl");
+
+ if (target[AclSym].ACL) return target[AclSym].ACL;
+
+ let effectiveAcl = AclEdit;
+
+ for (const [key, value] of Object.entries(target[AclSym])) {
+ if (key.startsWith("ACL-")) {
+ if (GroupManager.Instance.currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) {
+ if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) {
+ effectiveAcl = value as symbol;
+ if (effectiveAcl === AclEdit) break;
+ }
+ }
+ }
+ }
+
+ return effectiveAcl;
+}
+
+function testPermission(target: any, in_prop: string | symbol | number): boolean {
+
+ console.log("here");
+ // if (target[AclSym].ACL !== AclEdit && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return false;
+ if (target[AclSym].ACL === AclEdit) return true;
+ for (const [key, value] of Object.entries(target[AclSym])) {
+ if (key.startsWith("ACL-")) {
+ if (GroupManager.Instance.currentUserGroups.includes(key.substring(4))) {
+ if (value === AclEdit) return true;
+ }
+ }
+ }
+ return _overrideAcl || DocServer.PlaygroundFields.includes(in_prop.toString());
+}
+
const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox",
"chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"];
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
+ console.log("in setter")
let prop = in_prop;
- if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function
+ // if (target[AclSym] && !_overrideAcl && !DocServer.PlaygroundFields.includes(in_prop.toString())) return true; // generalise to a testpermission function
+ if (!testPermission(target, in_prop)) return true;
if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {
if (!prop.startsWith("_")) {
console.log(prop + " is deprecated - switch to _" + prop);
@@ -129,9 +176,11 @@ 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 {
+ console.log("in getter")
let prop = in_prop;
- if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym];
- if (target[AclSym] === AclPrivate && !_overrideAcl) return undefined;
+ const effectiveAcl = getEffectiveAcl(target);
+ if (in_prop === AclSym) return _overrideAcl ? undefined : effectiveAcl;
+ if (effectiveAcl === AclPrivate && !_overrideAcl) return undefined;
if (prop === LayoutSym) {
return target.__LAYOUT__;
}
@@ -168,7 +217,8 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP
}
if (field === undefined && !ignoreProto && prop !== "proto") {
const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters
- if (proto instanceof Doc && proto[AclSym] !== AclPrivate) {
+ console.log("here");
+ if (proto instanceof Doc && getEffectiveAcl(proto) !== AclPrivate) {
return getFieldImpl(proto[Self], prop, receiver, ignoreProto);
}
return undefined;