From 5a9af979cc293d0e3843270ee053c24bf0eb6ef5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Jul 2023 16:15:27 -0400 Subject: changed acl inheritance for docking views. --- src/client/documents/Documents.ts | 10 ++-- src/client/util/LinkFollower.ts | 2 +- src/client/util/SharingManager.tsx | 70 ++++++++++------------ src/client/views/DocComponent.tsx | 30 +++++----- src/client/views/DocumentDecorations.tsx | 40 ++++++++----- .../views/collections/CollectionDockingView.tsx | 12 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 8 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/ComparisonBox.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- .../formattedText/ProsemirrorExampleTransfer.ts | 10 ++-- src/fields/Doc.ts | 11 +++- src/fields/util.ts | 33 +++++----- 15 files changed, 123 insertions(+), 113 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 06ff95df0..18e8b5940 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -859,7 +859,7 @@ export namespace Docs { // users placeholderDoc as proto if it exists const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); - + if (placeholderDoc) { dataDoc.proto = proto; } @@ -1053,7 +1053,7 @@ export namespace Docs { export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _type_collection: CollectionViewType.Freeform }, id); - documents.forEach(d => (d.embedContainer = inst)); + documents.forEach(d => Doc.SetContainer(d, inst)); return inst; } @@ -1158,7 +1158,9 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id); + const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewFreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id); + documents.map(c => Doc.SetContainer(c, ret)); + return ret; } export function DirectoryImportDocument(options: DocumentOptions = {}) { @@ -1186,7 +1188,7 @@ export namespace Docs { options, id ); - configs.map(c => (c.doc.embedContainer = doc)); + configs.map(c => Doc.SetContainer(c.doc, doc)); return doc; } diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index f74409e42..3e526c4c0 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -103,7 +103,7 @@ export class LinkFollower { Doc.AddDocToList(sourceDocParent, Doc.LayoutFieldKey(sourceDocParent), target); movedTarget = true; } - target.embedContainer = sourceDocParent; + Doc.SetContainer(target, sourceDocParent); const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)]; if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) { target.x = moveTo[0]; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 534a9cfde..64b2bb5b8 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,12 +1,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton, Size } from 'browndash-components'; import { concat, intersection } from 'lodash'; 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, DocCastAsync, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; +import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; @@ -24,7 +23,6 @@ import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; import { SelectionManager } from './SelectionManager'; import './SharingManager.scss'; -import { Docs } from '../documents/Documents'; export interface User { email: string; @@ -166,7 +164,7 @@ export class SharingManager extends React.Component<{}> { // or if the 'Override Nested' checkbox is selected var childDocs = DocListCast(target.data); childDocs.map(doc => { - if (this.overrideNested || doc[acl]==undefined){ + if (this.overrideNested || doc[acl] == undefined) { this.setInternalSharing(recipient, permission, doc); } }); @@ -207,7 +205,7 @@ export class SharingManager extends React.Component<{}> { // or if the 'Override Private' checkbox is selected var childDocs = DocListCast(target.data); childDocs.map(doc => { - if (this.overrideNested || doc[acl]==undefined){ + if (this.overrideNested || doc[acl] == undefined) { this.setInternalGroupSharing(group, permission, doc); } }); @@ -288,7 +286,7 @@ export class SharingManager extends React.Component<{}> { this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout); } if (GetEffectiveAcl(doc) === AclAdmin) { - if ( shareWith == 'Public' && layout) shareWith = 'Public-layout'; + if (shareWith == 'Public' && layout) shareWith = 'Public-layout'; distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard); } this.setDashboardBackground(doc, permission as SharingPermissions); @@ -538,13 +536,12 @@ export class SharingManager extends React.Component<{}> { const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]); // const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(Doc.GetProto(targetDoc)[userKey])) : StrCast(targetDoc[userKey]); - if (this.layoutDocAcls){ + if (this.layoutDocAcls) { if (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name; - else if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]); + else if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]); else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[userKey]) : '-multiple-'; - } - else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; - + } else permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-'; + return !permissions ? null : (
{user.email} @@ -570,12 +567,11 @@ export class SharingManager extends React.Component<{}> { // the owner of the doc and the current user are placed at the top of the user list. const userKey = `acl-${normalizeEmail(Doc.CurrentUserEmail)}`; var curUserPermission; - if (this.layoutDocAcls){ - if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name; - else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]); - else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]); - } - else curUserPermission = StrCast(targetDoc[userKey]); + if (this.layoutDocAcls) { + if (targetDoc[DocAcl][userKey]) curUserPermission = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name; + else if (targetDoc['embedContainer']) curUserPermission = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[userKey]); + else curUserPermission = StrCast(Doc.GetProto(targetDoc)?.[userKey]); + } else curUserPermission = StrCast(targetDoc[userKey]); // const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name userListContents.unshift( sameAuthor ? ( @@ -609,16 +605,15 @@ export class SharingManager extends React.Component<{}> { .every(doc => (this.layoutDocAcls ? doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey] : doc?.[DocData]?.[DocAcl]?.[groupKey] === docs[0]?.[DocData]?.[DocAcl]?.[groupKey])); // const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-'; let permissions = this.layoutDocAcls ? (targetDoc[DocAcl][groupKey] ? HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name : StrCast(Doc.GetProto(targetDoc)[groupKey])) : StrCast(targetDoc[groupKey]); - if (this.layoutDocAcls){ - if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout'; - if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name; - else{ - if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public'; - if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]); - else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-'; - } + if (this.layoutDocAcls) { + if (groupKey == 'acl-Public') groupKey = 'acl-Public-layout'; + if (targetDoc[DocAcl][groupKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][groupKey])?.name; + else { + if (groupKey == 'acl-Public-layout') groupKey = 'acl-Public'; + if (targetDoc['embedContainer']) permissions = StrCast(Doc.GetProto(DocCast(targetDoc['embedContainer']))[groupKey]); + else permissions = uniform ? StrCast(Doc.GetProto(targetDoc)?.[groupKey]) : '-multiple-'; } - else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; + } else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : (
@@ -629,7 +624,7 @@ export class SharingManager extends React.Component<{}> {
) : null} -
+
{admin || this.myDocAcls ? ( {this.sharingOptions(true)} @@ -706,9 +700,9 @@ export class SharingManager extends React.Component<{}> {
) : ( -
-
-
+
+
+
(this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
@@ -717,7 +711,7 @@ export class SharingManager extends React.Component<{}> {
(this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}> -
+
Individuals   {this.individualSort === 'ascending' ? ( @@ -732,7 +726,7 @@ export class SharingManager extends React.Component<{}> {
(this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}> -
+
Groups  
GroupManager.Instance?.open())}> diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index f3aa8451a..1d0feec74 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -196,52 +196,52 @@ export function ViewBoxAnnotatableComponent

() aclKeys.forEach(key => added.forEach(d => { - if (key != 'acl-Me'){ - const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]) - const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl - const permission = HierarchyMapping.get(permissionSymbol)!.name - distributeAcls(key, permission, Doc.GetProto(d)) + if (key != 'acl-Me') { + const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]); + const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl; + const permission = HierarchyMapping.get(permissionSymbol)!.name; + distributeAcls(key, permission, Doc.GetProto(d)); } }) ); if (effectiveAcl === AclAugment) { added.map(doc => { - doc.embedContainer = this.props.Document; + Doc.SetContainer(doc, this.props.Document); if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); const parent = DocCast(doc.embedContainer); doc.embedContainer && inheritParentAcls(parent, doc); for (const key of Object.keys(parent)) { - const symbol = ReverseHierarchyMap.get(StrCast(parent[key])) - if (symbol && key.startsWith('acl')){ + const symbol = ReverseHierarchyMap.get(StrCast(parent[key])); + if (symbol && key.startsWith('acl')) { const sharePermission = HierarchyMapping.get(symbol.acl!)!.name; const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0]; if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc); } } }); - } else { - added + } else { + added .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) .map(doc => { // only make a pushpin if we have acl's to edit the document //DocUtils.LeavePushpin(doc); doc._dragOnlyWithinContainer = undefined; - doc.embedContainer = this.props.Document; + Doc.SetContainer(doc, this.props.Document); if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc; const parent = DocCast(doc.embedContainer); doc.embedContainer && inheritParentAcls(parent, doc); for (const key of Object.keys(Doc.GetProto(parent))) { - const symbol = ReverseHierarchyMap.get(StrCast(parent[key])) - if (symbol && key.startsWith('acl')){ + const symbol = ReverseHierarchyMap.get(StrCast(parent[key])); + if (symbol && key.startsWith('acl')) { const sharePermission = HierarchyMapping.get(symbol.acl!)!.name; const user = SharingManager.Instance?.users.filter(({ user: { email } }) => normalizeEmail(email) == key.slice(4))[0]; if (user && sharePermission !== SharingPermissions.None) return Doc.AddDocToList(user.sharingDoc, 'data', doc); } } - }); - + }); + const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List; if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add))); else targetDataDoc[annotationKey ?? this.annotationKey] = new List(added); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4454a3ec1..cdd9e62d8 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -201,9 +201,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => { const first = SelectionManager.Views()[0]; const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc); - if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment){ + if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) { return false; - } + } const dragDocView = SelectionManager.Views()[0]; const containers = new Set(); SelectionManager.Views().forEach(v => containers.add(DocCast(v.rootDoc.embedContainer))); @@ -766,13 +766,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P // sharing const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc); - const docShareMode = HierarchyMapping.get(acl)!.name + const docShareMode = HierarchyMapping.get(acl)!.name; const shareMode = StrCast(docShareMode); var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image; // hide the decorations if the parent chooses to hide it or if the document itself hides it const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; - const hideResizers = ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; + const hideResizers = + ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveLayoutAcl(seldocview.rootDoc)) || hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.layout_hideResizeHandles || this._isRounding || this._isRotating; const hideTitle = hideDecorations || seldocview.props.hideDecorationTitle || seldocview.rootDoc.layout_hideDecorationTitle || this._isRounding || this._isRotating; const hideDocumentButtonBar = hideDecorations || seldocview.props.hideDocumentButtonBar || seldocview.rootDoc.layout_hideDocumentButtonBar || this._isRounding || this._isRotating; // if multiple documents have been opened at the same time, then don't show open button @@ -826,26 +827,26 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const radiusHandleLocation = Math.min(radiusHandle, maxDist); const sharingMenu = docShareMode ? ( -

+
  {shareSymbolIcon + ' ' + shareMode}   - {!Doc.noviceMode ? -
-
- (this.showLayoutAcl = !this.showLayoutAcl))} /> + {!Doc.noviceMode ? ( +
+
+ (this.showLayoutAcl = !this.showLayoutAcl))} />
-
Layout
+
Layout
- : null } + ) : null}  
) : (
); - + const titleArea = this._editingTitle ? ( e.stopPropagation()} /> ) : ( -
{e.stopPropagation}}> - { hideTitle ? null : {this.selectionTitle}} +
{ + e.stopPropagation; + }}> + {hideTitle ? null : ( + + {this.selectionTitle} + + )} {sharingMenu} {!useLock ? null : ( toggle ability to interact with document
} placement="top"> @@ -872,7 +882,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )}
); - + return (
Doc.MakeEmbedding(origtabdoc)); if (newtabdocs.length) { Doc.GetProto(newtab).data = new List(newtabdocs); - newtabdocs.forEach(ntab => (ntab.embedContainer = newtab)); + newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab)); } json = json.replace(origtab[Id], newtab[Id]); return newtab; @@ -497,11 +497,11 @@ export class CollectionDockingView extends CollectionSubView() { tabCreated = (tab: any) => { const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {}); aclKeys.forEach(key => { - if (key != 'acl-Me'){ - const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]) - const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl - const permission = HierarchyMapping.get(permissionSymbol)!.name - distributeAcls(key, permission, Doc.GetProto(tab)) + if (key != 'acl-Me') { + const permissionString = StrCast(Doc.GetProto(this.props.Document)[key]); + const permissionSymbol = ReverseHierarchyMap.get(permissionString)!.acl; + const permission = HierarchyMapping.get(permissionSymbol)!.name; + distributeAcls(key, permission, Doc.GetProto(tab)); } }); this.tabMap.add(tab); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 4cd3885f5..d78a0e781 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -180,7 +180,7 @@ export class CollectionTreeView extends CollectionSubView (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => { const res = flg && Doc.AddDocToList(this.doc[DocData], this.props.fieldKey, doc, relativeTo, before); - res && (doc.embedContainer = this.props.Document); + res && Doc.SetContainer(doc, this.props.Document); return res; }, true); if (this.doc.resolvedDataDoc instanceof Promise) return false; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 4d780f46b..3473eee18 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -262,7 +262,7 @@ export class TabDocView extends React.Component { pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; pinDoc.presDuration = pinDoc.presDuration ?? 1000; pinDoc.groupWithUp = false; - pinDoc.embedContainer = curPres; + Doc.SetContainer(pinDoc, curPres); // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 8d8d895c6..a2269075d 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -392,7 +392,7 @@ export class TreeView extends React.Component { const innerAdd = (doc: Doc) => { const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc); - dataIsComputed && (doc.embedContainer = this.doc.embedContainer); + dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer); return added; }; return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); @@ -455,7 +455,7 @@ export class TreeView extends React.Component { const innerAdd = (doc: Doc) => { const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - dataIsComputed && (doc.embedContainer = this.doc.embedContainer); + dataIsComputed && Doc.SetContainer(doc, this.doc.embedContainer); return added; }; return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean); @@ -563,7 +563,7 @@ export class TreeView extends React.Component { } const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField; const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true); - !dataIsComputed && added && (doc.embedContainer = this.doc.embedContainer); + !dataIsComputed && added && Doc.SetContainer(doc, this.doc.embedContainer); return added; }; @@ -1192,7 +1192,7 @@ export class TreeView extends React.Component { TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined; Doc.AddDocToList(newParent, fieldKey, child, addAfter, false); newParent.treeViewOpen = true; - child.embedContainer = treeView.Document; + Doc.SetContainer(child, treeView.Document); } }; const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1])); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 9b0abc48b..764b1e08a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -392,7 +392,7 @@ export class MarqueeView extends React.Component { - d.embedContainer = newCollection; + Doc.SetContainer(d, newCollection); d['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; }); this.hideMarquee(); diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 2290e0711..ca5ec9389 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -46,7 +46,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent this.addDoc(doc instanceof Doc ? doc : doc.lastElement(), fieldKey)); - droppedDocs.lastElement().embedContainer = this.dataDoc; + Doc.SetContainer(droppedDocs.lastElement(), this.dataDoc); !added && e.preventDefault(); e.stopPropagation(); // prevent parent Doc from registering new position so that it snaps back into place return added; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 202a9f851..e5ea8e0c1 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -564,7 +564,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent>(schema: S, props: any, mapKey switch (GetEffectiveAcl(props.DataDoc)) { case AclAugment: const prevNode = state.selection.$cursor.nodeBefore; - const prevUser = prevNode.marks[prevNode.marks.length-1].attrs.userid - if (prevUser != Doc.CurrentUserEmail){ + const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid; + if (prevUser != Doc.CurrentUserEmail) { return false; } } @@ -263,7 +263,7 @@ export function buildKeymap>(schema: S, props: any, mapKey bind('Backspace', (state: EditorState, dispatch: (tx: Transaction) => void) => { if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; - + if ( !deleteSelection(state, (tx: Transaction) => { dispatch(updateBullets(tx, schema)); @@ -334,7 +334,7 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a blank space bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (GetEffectiveAcl(props.DataDoc)!=AclEdit && GetEffectiveAcl(props.DataDoc)!=AclAugment && GetEffectiveAcl(props.DataDoc)!=AclAdmin) return true; + if (GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); dispatch(splitMetadata(marks, state.tr)); return false; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 99712fb04..9c138d348 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -20,7 +20,6 @@ import { AclEdit, AclPrivate, AclReadonly, - AclSelfEdit, AclUnset, Animation, CachedUpdates, @@ -138,7 +137,7 @@ export const HierarchyMapping: Map = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }])); +export const ReverseHierarchyMap: Map = new Map(Array.from(HierarchyMapping.entries()).map(value => [value[1].name, { level: value[1].level, acl: value[0], image: value[1].image }])); // caches the document access permissions for the current user. // this recursively updates all protos as well. @@ -478,6 +477,14 @@ export namespace Doc { // }); // } + export function SetContainer(doc: Doc, container: Doc) { + doc.embedContainer = container; + if (Doc.GetProto(container).author === doc.author) { + Object.keys(Doc.GetProto(container)) + .filter(key => key.startsWith('acl')) + .forEach(key => (doc[key] = Doc.GetProto(container)[key])); + } + } export function RunCachedUpdate(doc: Doc, field: string) { const update = doc[CachedUpdates][field]; if (update) { diff --git a/src/fields/util.ts b/src/fields/util.ts index 0f613d926..48de36efe 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -131,10 +131,10 @@ export function inheritParentAcls(parent: Doc, child: Doc) { for (const key of Object.keys(parent)) { // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key]; - const symbol = ReverseHierarchyMap.get(StrCast(parent[key])) - if (symbol){ + const symbol = ReverseHierarchyMap.get(StrCast(parent[key])); + if (symbol) { const sharePermission = HierarchyMapping.get(symbol.acl!)!.name; - key.startsWith('acl') && distributeAcls(key, sharePermission, child) + key.startsWith('acl') && distributeAcls(key, sharePermission, child); } } } @@ -171,7 +171,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) { // return layout acl from cache or chache the acl and return. const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) { return getEffectiveLayoutAcl(target, user); - }, true); +}, true); /** * Calculates the effective access right to a document for the current user. @@ -183,13 +183,13 @@ export function GetEffectiveAcl(target: any, user?: string): symbol { } /** -* Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set. -*/ + * Calculates the effective access layout right to a document for the current user. By getting the container's effective acl if the layout acl isn't set. + */ export function GetEffectiveLayoutAcl(target: any, user?: string): symbol { if (!target) return AclPrivate; if (target[UpdatingFromServer]) return AclAdmin; return getEffectiveLayoutAclCache(target, user); - } +} function getPropAcl(target: any, prop: string | symbol | number) { if (typeof prop === 'symbol' || target[UpdatingFromServer]) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent @@ -234,9 +234,9 @@ function getEffectiveAcl(target: any, user?: string): symbol { } /** -* Returns the layout acl that is effective on the document passed through as the target. If no layout acls -* have been set, it returns the regular acls for the document target is contained in. -*/ + * Returns the layout acl that is effective on the document passed through as the target. If no layout acls + * have been set, it returns the regular acls for the document target is contained in. + */ function getEffectiveLayoutAcl(target: any, user?: string): symbol { const targetAcls = target[DocAcl]; @@ -248,27 +248,24 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol { if ((GetCachedGroupByName(entity) || userChecked === entity || entity === 'Me') && entity != 'Public') { if (effectiveAcl && HierarchyMapping.get(value as symbol)!.level > HierarchyMapping.get(effectiveAcl)!.level) { effectiveAcl = value as symbol; - } - else{ + } else { effectiveAcl = value as symbol; } } } - if (effectiveAcl){ + if (effectiveAcl) { return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl; - } - else{ + } else { return GetEffectiveAcl(Doc.GetProto(target['embedContainer']), user); } } // authored documents are private until an ACL is set. const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc of Proxy, so check __fieldTuples.author and .author if (targetAuthor && targetAuthor !== userChecked) return AclPrivate; - return AclAdmin; + return AclAdmin; } - /** * Recursively distributes the access right for a user across the children of a document and its annotations. * @param key the key storing the access right (e.g. acl-groupname) @@ -286,7 +283,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???) updateCachedAcls(target); } - return; + //return; } visited.push(target); -- cgit v1.2.3-70-g09d2