From c8e8cbe52a97835342c3a3d04033462f4497cd72 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 9 Oct 2020 11:36:56 -0400 Subject: trying to get group updates to work automatically --- src/client/documents/Documents.ts | 2 ++ src/client/util/CurrentUserUtils.ts | 7 ++-- src/client/util/GroupManager.tsx | 71 ++++++++++++------------------------- src/client/util/GroupMemberView.tsx | 4 +-- src/client/util/SharingManager.tsx | 24 ++++++------- src/fields/util.ts | 39 ++++++++++++-------- 6 files changed, 67 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7ee8267f8..7d78bd76a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -244,6 +244,7 @@ export namespace Docs { view: LayoutSource, dataField: string }, + data?: any, options?: Partial }; type TemplateMap = Map; @@ -464,6 +465,7 @@ export namespace Docs { const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; options.layout = layout.view?.LayoutString(layout.dataField); const doc = Doc.assign(new Doc(prototypeId, true), { system: true, layoutKey: "layout", ...options }); + doc.data = template.data; doc.layout_keyValue = KeyValueBox.LayoutString(""); return doc; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d011d7b09..b4e24f70e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -30,7 +30,7 @@ import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; -import { SharingPermissions } from "../../fields/util"; +import { SharingPermissions, UserGroups } from "../../fields/util"; import { Networking } from "../Network"; @@ -988,9 +988,10 @@ export class CurrentUserUtils { this.setupDockedButtons(doc); // the bottom bar of font icons await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels await this.setupMenuPanel(doc, sharingDocumentId); - doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); + if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); + if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); if (!doc.myLinkDatabase) doc.myLinkDatabase = new List([]); + UserGroups.setCurrentUserGroups((doc.globalGroupDatabase as Doc).data as List); setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 48e3ca737..a0db46fdb 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,19 +1,18 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; import Select from 'react-select'; import * as RequestPromise from "request-promise"; -import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { Cast, StrCast } from "../../fields/Types"; -import { setGroups } from "../../fields/util"; import { Utils } from "../../Utils"; -import { DocServer } from "../DocServer"; import { MainViewModal } from "../views/MainViewModal"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; import "./GroupManager.scss"; import { GroupMemberView } from "./GroupMemberView"; import { SharingManager, User } from "./SharingManager"; +import { listSpec } from "../../fields/Schema"; /** * Interface for options for the react-select component @@ -34,12 +33,8 @@ export class GroupManager extends React.Component<{}> { @observable private createGroupModalOpen: boolean = false; private inputRef: React.RefObject = React.createRef(); // the ref for the input box. private createGroupButtonRef: React.RefObject = React.createRef(); // the ref for the group creation button - private currentUserGroups: string[] = []; // the list of groups the current user is a member of @observable private buttonColour: "#979797" | "black" = "#979797"; @observable private groupSort: "ascending" | "descending" | "none" = "none"; - private populating: boolean = false; - - constructor(props: Readonly<{}>) { super(props); @@ -51,32 +46,15 @@ export class GroupManager extends React.Component<{}> { */ componentDidMount() { this.populateUsers(); - this.populateGroups(); } /** * Fetches the list of users stored on the database. */ populateUsers = async () => { - if (!this.populating) { - const userList = await RequestPromise.get(Utils.prepend("/getUsers")); - const raw = JSON.parse(userList) as User[]; - raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email))); - } - } - - /** - * Populates the list of groups the current user is a member of and sets this list to be used in the GetEffectiveAcl in util.ts - */ - populateGroups = () => { - 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(StrCast(group.groupName)); - }); - this.currentUserGroups.push("Public"); - setGroups(this.currentUserGroups); - }); + const userList = await RequestPromise.get(Utils.prepend("/getUsers")); + const raw = JSON.parse(userList) as User[]; + raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email))); } /** @@ -94,7 +72,6 @@ export class GroupManager extends React.Component<{}> { // SelectionManager.DeselectAll(); this.isOpen = true; this.populateUsers(); - this.populateGroups(); } /** @@ -113,14 +90,14 @@ export class GroupManager extends React.Component<{}> { /** * @returns the database of groups. */ - get GroupManagerDoc(): Doc | undefined { + @computed get GroupManagerDoc(): Doc | undefined { return Doc.UserDoc().globalGroupDatabase as Doc; } /** * @returns a list of all group documents. */ - getAllGroups(): Doc[] { + @computed get allGroups(): Doc[] { const groupDoc = this.GroupManagerDoc; return groupDoc ? DocListCast(groupDoc.data) : []; } @@ -130,7 +107,7 @@ export class GroupManager extends React.Component<{}> { * @param groupName */ getGroup(groupName: string): Doc | undefined { - const groupDoc = this.getAllGroups().find(group => group.groupName === groupName); + const groupDoc = this.allGroups.find(group => group.title === groupName); return groupDoc; } @@ -164,15 +141,13 @@ export class GroupManager extends React.Component<{}> { * @param groupName * @param memberEmails */ + @action createGroupDoc(groupName: string, memberEmails: string[] = []) { - const groupDoc = new Doc; - groupDoc.groupName = groupName.toLowerCase() === "admin" ? "Admin" : groupName; + const name = groupName.toLowerCase() === "admin" ? "Admin" : groupName; + const groupDoc = new Doc("GROUP:" + name); + groupDoc.title = name; groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]); groupDoc.members = JSON.stringify(memberEmails); - if (memberEmails.includes(Doc.CurrentUserEmail)) { - this.currentUserGroups.push(groupName); - setGroups(this.currentUserGroups); - } this.addGroup(groupDoc); } @@ -192,19 +167,19 @@ export class GroupManager extends React.Component<{}> { * Deletes a group from the database of group documents and @returns whether the group was deleted or not. * @param group */ + @action deleteGroup(group: Doc): boolean { if (group) { if (this.GroupManagerDoc && this.hasEditAccess(group)) { Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group); SharingManager.Instance.removeGroup(group); - const members: string[] = JSON.parse(StrCast(group.members)); + const members = JSON.parse(StrCast(group.members)); if (members.includes(Doc.CurrentUserEmail)) { - const index = this.currentUserGroups.findIndex(groupName => groupName === group.groupName); - index !== -1 && this.currentUserGroups.splice(index, 1); - setGroups(this.currentUserGroups); + const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group); + index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1); } if (group === this.currentGroup) { - runInAction(() => this.currentGroup = undefined); + this.currentGroup = undefined; } return true; } @@ -368,13 +343,13 @@ export class GroupManager extends React.Component<{}> { private get groupInterface() { const sortGroups = (d1: Doc, d2: Doc) => { - const g1 = StrCast(d1.groupName); - const g2 = StrCast(d2.groupName); + const g1 = StrCast(d1.title); + const g2 = StrCast(d2.title); return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; }; - let groups = this.getAllGroups(); + let groups = this.allGroups; groups = this.groupSort === "ascending" ? groups.sort(sortGroups) : this.groupSort === "descending" ? groups.sort(sortGroups).reverse() : groups; return ( @@ -408,9 +383,9 @@ export class GroupManager extends React.Component<{}> { {groups.map(group =>
-
{group.groupName}
+
{group.title}
this.currentGroup = group)}>
diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 4ead01e9f..d9174561c 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -33,8 +33,8 @@ export class GroupMemberView extends React.Component { this.props.group.groupName = e.currentTarget.value} + value={StrCast(this.props.group.title)} + onChange={e => this.props.group.title = e.currentTarget.value} disabled={!hasEditAccess} > diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 1cc4c59f2..a9abeedaf 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -152,10 +152,10 @@ export class SharingManager extends React.Component<{}> { * @param group * @param permission */ - setInternalGroupSharing = (group: Doc | { groupName: string }, permission: string, targetDoc?: Doc) => { + setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => { const target = targetDoc || this.targetDoc!; - const key = normalizeEmail(StrCast(group.groupName)); + const key = normalizeEmail(StrCast(group.title)); const acl = `acl-${key}`; const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document); @@ -233,7 +233,7 @@ export class SharingManager extends React.Component<{}> { removeGroup = (group: Doc) => { if (group.docsShared) { DocListCast(group.docsShared).forEach(doc => { - const acl = `acl-${StrCast(group.groupName)}`; + const acl = `acl-${StrCast(group.title)}`; distributeAcls(acl, SharingPermissions.None, doc); @@ -411,8 +411,8 @@ export class SharingManager extends React.Component<{}> { * Sorting algorithm to sort groups. */ sortGroups = (group1: Doc, group2: Doc) => { - const g1 = StrCast(group1.groupName); - const g2 = StrCast(group2.groupName); + const g1 = StrCast(group1.title); + const g2 = StrCast(group2.title); return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; } @@ -421,7 +421,7 @@ export class SharingManager extends React.Component<{}> { */ @computed get sharingInterface() { TraceMobx(); - const groupList = GroupManager.Instance?.getAllGroups() || []; + const groupList = GroupManager.Instance?.allGroups || []; const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email })); const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) })); @@ -527,19 +527,19 @@ export class SharingManager extends React.Component<{}> { // the list of groups shared with - const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(groupName))}`) : true); - groupListMap.unshift({ groupName: "Public" }, { groupName: "Override" }); + const groupListMap: (Doc | { title: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(groupName))}`) : true); + groupListMap.unshift({ title: "Public" }, { title: "Override" }); const groupListContents = groupListMap.map(group => { - const groupKey = `acl-${StrCast(group.groupName)}`; + const groupKey = `acl-${StrCast(group.title)}`; const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]); - const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.groupName)}`]) : "-multiple-"; + const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : "-multiple-"; return !permissions ? (null) : (
-
{group.groupName}
+
{group.title}
{group instanceof Doc ? (
GroupManager.Instance.currentGroup = group)}> @@ -552,7 +552,7 @@ export class SharingManager extends React.Component<{}> { value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)} > - {this.sharingOptions(uniform, group.groupName === "Override")} + {this.sharingOptions(uniform, group.title === "Override")} ) : (
diff --git a/src/fields/util.ts b/src/fields/util.ts index 7293db0c2..1e6707a9a 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -4,7 +4,7 @@ import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; -import { action, trace } from "mobx"; +import { action, trace, observable, reaction } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; @@ -22,6 +22,24 @@ export function TraceMobx() { tracing && trace(); } +// the list of groups that the current user is a member of +export class UserGroups { + @observable static Current: string[]; + @action static setCurrentUserGroups(groupList: List) { + reaction(() => groupList, + groupList => { + UserGroups.Current = [ + "Public", + ...(groupList as List). + filter(group => group instanceof Doc). + map(group => group as Doc). + filter(group => JSON.parse(StrCast(group.members))?.includes(Doc.CurrentUserEmail)). + map(group => StrCast(group.title)) + ]; + }, { fireImmediately: true }); + } +} + export interface GetterResult { value: FieldResult; shouldReturn?: boolean; @@ -130,14 +148,6 @@ export function denormalizeEmail(email: string) { // playgroundMode = !playgroundMode; // } -// the list of groups that the current user is a member of -let currentUserGroups: string[] = []; - -// called from GroupManager once the groups have been fetched from the server -export function setGroups(groups: string[]) { - currentUserGroups = groups; -} - /** * These are the various levels of access a user can have to a document. * @@ -164,6 +174,7 @@ export enum SharingPermissions { */ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol { if (!target) return AclPrivate; + if (target[Id] === "groupdbProto" || target[Id]?.startsWith("GROUP:")) return AclAdmin; // all changes received fromt the server must be processed as Admin if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin; @@ -171,7 +182,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, // 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 (currentUserGroups.includes("Admin")) return AclAdmin; + if (UserGroups.Current?.includes("Admin")) return AclAdmin; if (target[AclSym] && Object.keys(target[AclSym]).length) { @@ -192,7 +203,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, // 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. const entity = denormalizeEmail(key.substring(4)); // an individual or a group - if (currentUserGroups.includes(entity) || userChecked === entity) { + if (UserGroups.Current?.includes(entity) || userChecked === entity) { if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { effectiveAcl = value as symbol; if (effectiveAcl === AclAdmin) return effectiveAcl; @@ -308,12 +319,10 @@ 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 === "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 (in_prop === "toString" || (in_prop !== HeightSym && in_prop !== WidthSym && in_prop !== LayoutSym && typeof prop === "symbol")) return target.__fields[prop] || target[prop]; if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined; - if (prop === LayoutSym) { - return target.__LAYOUT__; - } + if (prop === LayoutSym) return target.__LAYOUT__; if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { if (!prop.startsWith("_")) { console.log(prop + " is deprecated - switch to _" + prop); -- cgit v1.2.3-70-g09d2 From ec8673566721a4625466401502c2e16878658007 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 9 Oct 2020 13:02:35 -0400 Subject: less broken, but still not working --- src/client/util/CurrentUserUtils.ts | 13 +++++++------ src/client/util/GroupManager.tsx | 12 +++++------- src/client/util/SharingManager.tsx | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b4e24f70e..759fd6eca 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -10,10 +10,12 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; +import { SharingPermissions, UserGroups } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; +import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; @@ -30,8 +32,6 @@ import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; -import { SharingPermissions, UserGroups } from "../../fields/util"; -import { Networking } from "../Network"; export let resolvedPorts: { server: number, socket: number }; @@ -231,7 +231,7 @@ export class CurrentUserUtils { } else { const curButnTypes = Cast(doc["template-buttons"], Doc, null); DocListCastAsync(curButnTypes.data).then(async curBtns => { - await Promise.all(curBtns!); + curBtns && await Promise.all(curBtns); requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype)); }); } @@ -280,7 +280,7 @@ export class CurrentUserUtils { const curNoteTypes = Cast(doc["template-notes"], Doc, null); const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc];//, doc["template-note-Todo"] as any as Doc]; DocListCastAsync(curNoteTypes.data).then(async curNotes => { - await Promise.all(curNotes!); + curNotes && await Promise.all(curNotes); requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype)); }); } @@ -351,7 +351,7 @@ export class CurrentUserUtils { const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; DocListCastAsync(templateIconsDoc.data).then(async curIcons => { - await Promise.all(curIcons!); + curIcons && await Promise.all(curIcons); requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); }); } @@ -989,8 +989,9 @@ export class CurrentUserUtils { await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels await this.setupMenuPanel(doc, sharingDocumentId); if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); + doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); if (!doc.myLinkDatabase) doc.myLinkDatabase = new List([]); + const x = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); UserGroups.setCurrentUserGroups((doc.globalGroupDatabase as Doc).data as List); setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index a0db46fdb..f579f3dae 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,11 +1,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; import Select from 'react-select'; import * as RequestPromise from "request-promise"; -import { Doc, DocListCast, Opt } from "../../fields/Doc"; -import { Cast, StrCast } from "../../fields/Types"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; +import { StrCast, Cast } from "../../fields/Types"; import { Utils } from "../../Utils"; import { MainViewModal } from "../views/MainViewModal"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; @@ -98,8 +98,7 @@ export class GroupManager extends React.Component<{}> { * @returns a list of all group documents. */ @computed get allGroups(): Doc[] { - const groupDoc = this.GroupManagerDoc; - return groupDoc ? DocListCast(groupDoc.data) : []; + return DocListCast(this.GroupManagerDoc?.data); } /** @@ -141,10 +140,9 @@ export class GroupManager extends React.Component<{}> { * @param groupName * @param memberEmails */ - @action createGroupDoc(groupName: string, memberEmails: string[] = []) { const name = groupName.toLowerCase() === "admin" ? "Admin" : groupName; - const groupDoc = new Doc("GROUP:" + name); + const groupDoc = new Doc("GROUP:" + name, true); groupDoc.title = name; groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]); groupDoc.members = JSON.stringify(memberEmails); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index a9abeedaf..729742890 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -423,7 +423,7 @@ export class SharingManager extends React.Component<{}> { TraceMobx(); const groupList = GroupManager.Instance?.allGroups || []; const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email })); - const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ groupName }) => ({ label: StrCast(groupName), value: groupType + StrCast(groupName) })); + const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) })); // the next block handles the users shown (individuals/groups/both) const options: GroupedOptions[] = []; @@ -527,7 +527,7 @@ export class SharingManager extends React.Component<{}> { // the list of groups shared with - const groupListMap: (Doc | { title: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(groupName))}`) : true); + const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true); groupListMap.unshift({ title: "Public" }, { title: "Override" }); const groupListContents = groupListMap.map(group => { const groupKey = `acl-${StrCast(group.title)}`; -- cgit v1.2.3-70-g09d2 From 2608cc4dbf852a318b9d6a453bdaf64249917cda Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 9 Oct 2020 16:54:21 -0400 Subject: fixed updating groups to take effect immediately --- src/client/util/CurrentUserUtils.ts | 6 +++--- src/client/util/SharingManager.tsx | 12 +++++++----- src/fields/util.ts | 26 +++++++++++++++++--------- 3 files changed, 27 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 759fd6eca..580c6040e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -956,6 +956,9 @@ export class CurrentUserUtils { } static async updateUserDocument(doc: Doc, sharingDocumentId: string) { + if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); + await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + UserGroups.Current; doc.system = true; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; @@ -989,10 +992,7 @@ export class CurrentUserUtils { await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels await this.setupMenuPanel(doc, sharingDocumentId); if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); if (!doc.myLinkDatabase) doc.myLinkDatabase = new List([]); - const x = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); - UserGroups.setCurrentUserGroups((doc.globalGroupDatabase as Doc).data as List); setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 729742890..ee397ab4f 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import Select from "react-select"; import * as RequestPromise from "request-promise"; -import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly } from "../../fields/Doc"; +import { AclAdmin, AclPrivate, DataSym, Doc, DocListCast, Opt, AclSym, AclAddonly, AclEdit, AclReadonly, DocListCastAsync } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Cast, StrCast } from "../../fields/Types"; import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail } from "../../fields/util"; @@ -189,7 +189,9 @@ export class SharingManager extends React.Component<{}> { const self = this; if (group.docsShared) { if (!user) retry && this.populateUsers().then(() => self.shareWithAddedMember(group, emailId, false)); - else DocListCast(group.docsShared).forEach(doc => Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) === -1 && Doc.AddDocToList(user.sharingDoc, storage, doc)); + else DocListCastAsync(group.docsShared).then(dl => dl?.forEach(doc => { + Doc.AddDocToList(user.sharingDoc, storage, doc); + })); } } @@ -220,9 +222,9 @@ export class SharingManager extends React.Component<{}> { const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!; if (group.docsShared) { - DocListCast(group.docsShared).forEach(doc => { - Doc.IndexOf(doc, DocListCast(user.sharingDoc[storage])) !== -1 && Doc.RemoveDocFromList(user.sharingDoc, storage, doc); // remove the doc only if it is in the list - }); + DocListCastAsync(group.docsShared).then(dl => dl?.forEach(doc => { + Doc.RemoveDocFromList(user.sharingDoc, storage, doc); + })); } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 1e6707a9a..791b98b83 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -4,7 +4,7 @@ import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; -import { action, trace, observable, reaction } from "mobx"; +import { action, trace, observable, reaction, computed } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; @@ -24,19 +24,28 @@ export function TraceMobx() { // the list of groups that the current user is a member of export class UserGroups { - @observable static Current: string[]; - @action static setCurrentUserGroups(groupList: List) { - reaction(() => groupList, - groupList => { - UserGroups.Current = [ + static computing = false; + static cachedGroups: string[] = []; + static globalGroupDoc: Doc | undefined; + static get Current() { + if (!Doc.UserDoc() || UserGroups.computing) return UserGroups.cachedGroups; + UserGroups.computing = true; + if (!UserGroups.globalGroupDoc) UserGroups.globalGroupDoc = Doc.UserDoc().globalGroupDatabase as Doc; + if (UserGroups.globalGroupDoc) { + const dbgroups = DocListCast(UserGroups.globalGroupDoc.data); + if (dbgroups.length !== UserGroups.cachedGroups.length - 1) { + UserGroups.cachedGroups = [ "Public", - ...(groupList as List). + ...dbgroups?. filter(group => group instanceof Doc). map(group => group as Doc). filter(group => JSON.parse(StrCast(group.members))?.includes(Doc.CurrentUserEmail)). map(group => StrCast(group.title)) ]; - }, { fireImmediately: true }); + } + } + UserGroups.computing = false; + return UserGroups.cachedGroups; } } @@ -174,7 +183,6 @@ export enum SharingPermissions { */ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol { if (!target) return AclPrivate; - if (target[Id] === "groupdbProto" || target[Id]?.startsWith("GROUP:")) return AclAdmin; // all changes received fromt the server must be processed as Admin if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin; -- cgit v1.2.3-70-g09d2 From 2555972e14046d7042ed575db1d80c8a7ba06ae6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 9 Oct 2020 17:07:06 -0400 Subject: from last --- src/client/util/GroupManager.tsx | 57 +++++++++++++++------------------------- 1 file changed, 21 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index f579f3dae..4bb5a93fc 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -41,12 +41,7 @@ export class GroupManager extends React.Component<{}> { GroupManager.Instance = this; } - /** - * Populates the list of users and groups. - */ - componentDidMount() { - this.populateUsers(); - } + componentDidMount() { this.populateUsers(); } /** * Fetches the list of users stored on the database. @@ -90,24 +85,24 @@ export class GroupManager extends React.Component<{}> { /** * @returns the database of groups. */ - @computed get GroupManagerDoc(): Doc | undefined { - return Doc.UserDoc().globalGroupDatabase as Doc; - } + @computed get GroupManagerDoc(): Doc | undefined { return Doc.UserDoc().globalGroupDatabase as Doc; } /** * @returns a list of all group documents. */ - @computed get allGroups(): Doc[] { - return DocListCast(this.GroupManagerDoc?.data); - } + @computed get allGroups(): Doc[] { return DocListCast(this.GroupManagerDoc?.data); } + + /** + * @returns the members of the admin group. + */ + @computed get adminGroupMembers(): string[] { return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : ""; } /** * @returns a group document based on the group name. * @param groupName */ getGroup(groupName: string): Doc | undefined { - const groupDoc = this.allGroups.find(group => group.title === groupName); - return groupDoc; + return this.allGroups.find(group => group.title === groupName); } /** @@ -115,15 +110,9 @@ export 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[]; + return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[]; } - /** - * @returns the members of the admin group. - */ - get adminGroupMembers(): string[] { - return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : ""; - } /** * @returns a boolean indicating whether the current user has access to edit group documents. @@ -192,7 +181,7 @@ export class GroupManager extends React.Component<{}> { */ addMemberToGroup(groupDoc: Doc, email: string) { if (this.hasEditAccess(groupDoc)) { - const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); + const memberList = JSON.parse(StrCast(groupDoc.members)); !memberList.includes(email) && memberList.push(email); groupDoc.members = JSON.stringify(memberList); SharingManager.Instance.shareWithAddedMember(groupDoc, email); @@ -206,7 +195,7 @@ export class GroupManager extends React.Component<{}> { */ removeMemberFromGroup(groupDoc: Doc, email: string) { if (this.hasEditAccess(groupDoc)) { - const memberList: string[] = JSON.parse(StrCast(groupDoc.members)); + const memberList = JSON.parse(StrCast(groupDoc.members)); const index = memberList.indexOf(email); if (index !== -1) { const user = memberList.splice(index, 1)[0]; @@ -347,8 +336,7 @@ export class GroupManager extends React.Component<{}> { return g1 < g2 ? -1 : g1 === g2 ? 0 : 1; }; - let groups = this.allGroups; - groups = this.groupSort === "ascending" ? groups.sort(sortGroups) : this.groupSort === "descending" ? groups.sort(sortGroups).reverse() : groups; + const groups = this.groupSort === "ascending" ? this.allGroups.sort(sortGroups) : this.groupSort === "descending" ? this.allGroups.sort(sortGroups).reverse() : this.allGroups; return (
@@ -397,16 +385,13 @@ export class GroupManager extends React.Component<{}> { } render() { - return ( - - ); + return ; } - } \ No newline at end of file -- cgit v1.2.3-70-g09d2