aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/DocServer.ts14
-rw-r--r--src/client/util/SharingManager.tsx205
-rw-r--r--src/client/views/DashboardView.scss141
-rw-r--r--src/client/views/DashboardView.tsx32
-rw-r--r--src/client/views/DocComponent.tsx61
-rw-r--r--src/client/views/DocumentDecorations.tsx33
-rw-r--r--src/client/views/GlobalKeyHandler.ts12
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/PropertiesView.tsx50
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx17
-rw-r--r--src/client/views/collections/TreeView.tsx6
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/fields/Doc.ts4
-rw-r--r--src/fields/util.ts125
14 files changed, 265 insertions, 441 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 2a7f5a09b..8b8a9a618 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -76,10 +76,14 @@ export namespace DocServer {
const fieldWriteModes: { [field: string]: WriteMode } = {};
const docsWithUpdates: { [field: string]: Set<Doc> } = {};
- export var PlaygroundFields: string[];
- export function setPlaygroundFields(livePlaygroundFields: string[]) {
- DocServer.PlaygroundFields = livePlaygroundFields;
- livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
+ export var PlaygroundFields: string[] = [];
+ export function setLivePlaygroundFields(livePlaygroundFields: string[]) {
+ DocServer.PlaygroundFields.push(...livePlaygroundFields);
+ livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.LivePlayground));
+ }
+ export function setPlaygroundFields(playgroundFields: string[]) {
+ DocServer.PlaygroundFields.push(...playgroundFields);
+ playgroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
}
export function IsPlaygroundField(field: string) {
return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
@@ -97,7 +101,7 @@ export namespace DocServer {
}
export function getFieldWriteMode(field: string) {
- return Doc.CurrentUserEmail === 'guest' ? WriteMode.LiveReadonly : fieldWriteModes[field] || WriteMode.Default;
+ return Doc.CurrentUserEmail === 'guest' ? WriteMode.LivePlayground : fieldWriteModes[field] || WriteMode.Default;
}
export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3a0672113..d3241009a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -6,10 +6,9 @@ import * as React from 'react';
import Select from 'react-select';
import * as RequestPromise from 'request-promise';
import { Doc, DocListCast, DocListCastAsync, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
+import { AclAdmin, AclPrivate, DocAcl, DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
-import { List } from '../../fields/List';
-import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
import { Utils } from '../../Utils';
import { DocServer } from '../DocServer';
@@ -73,13 +72,11 @@ export class SharingManager extends React.Component<{}> {
@observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals
@observable private groupSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of groups
private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
- private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute button, used for the position of the popup
// if both showUserOptions and showGroupOptions are false then both are displayed
@observable private showUserOptions: boolean = false; // whether to show individuals as options when sharing (in the react-select component)
@observable private showGroupOptions: boolean = false; // // whether to show groups as options when sharing (in the react-select component)
private populating: boolean = false; // whether the list of users is populating or not
@observable private layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
- @observable private overrideNested: boolean = false; // whether child docs in a collection/dashboard should be changed to be less private - initially selected so default is override
@observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
// private get linkVisible() {
@@ -94,7 +91,6 @@ export class SharingManager extends React.Component<{}> {
DictationOverlay.Instance.hasActiveModal = true;
this.isOpen = this.targetDoc !== undefined;
this.permissions = SharingPermissions.Augment;
- this.overrideNested = true;
});
};
@@ -111,7 +107,6 @@ export class SharingManager extends React.Component<{}> {
500
);
this.layoutDocAcls = false;
- this.overrideNested = false;
});
constructor(props: {}) {
@@ -154,41 +149,19 @@ export class SharingManager extends React.Component<{}> {
/**
* Shares the document with a user.
*/
- setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc?: Doc) => {
+ setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => {
const { user, sharingDoc } = recipient;
const target = targetDoc || this.targetDoc!;
const acl = `acl-${normalizeEmail(user.email)}`;
const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
- const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
- // setting the same acl for a docs within the doc being shared if they haven't been set yet
- // or if the 'Override Nested' checkbox is selected
- var childDocs = DocListCast(target.data);
- childDocs.map(doc => {
- if (this.overrideNested || doc[acl] == undefined) {
- this.setInternalSharing(recipient, permission, doc);
- }
- });
-
- const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
- // ! ensures it returns true if document has been shared successfully, false otherwise
- return !docs
- .map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc)))
- .map(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+ docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+ doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined);
- if (permission === SharingPermissions.None) {
- if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
- } else {
- if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
- }
- distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
- this.setDashboardBackground(doc, permission as SharingPermissions);
- if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
- return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
- })
- .some(success => !success);
+ distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+ if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
+ else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
+ });
};
/**
@@ -200,51 +173,24 @@ export class SharingManager extends React.Component<{}> {
const target = targetDoc || this.targetDoc!;
const key = normalizeEmail(StrCast(group.title));
let acl = `acl-${key}`;
- const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
-
- // setting the same acl for a docs within the doc being shared if they haven't been set yet
- // or if the 'Override Nested' checkbox is selected
- var childDocs = DocListCast(target.data);
- childDocs.map(doc => {
- if (this.overrideNested || doc[acl] == undefined) {
- this.setInternalGroupSharing(group, permission, doc);
- }
- });
-
- const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
- if (acl == 'acl-Public' && this.layoutDocAcls) acl = 'acl-Public-layout';
- // ! ensures it returns true if document has been shared successfully, false otherwise
- return !docs
- .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
- .map(doc => {
- doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
- if (permission === SharingPermissions.None) {
- if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
- } else {
- if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
- }
- distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
- this.setDashboardBackground(doc, permission as SharingPermissions);
+ const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.rootDoc);
+ docs.map(doc => (this.layoutDocAcls ? doc : Doc.GetProto(doc))).forEach(doc => {
+ doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined);
- if (group instanceof Doc) {
- const members: string[] = JSON.parse(StrCast(group.members));
- const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
+ distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
- // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
- group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc]));
+ if (group instanceof Doc) {
+ Doc.AddDocToList(group, 'docsShared', doc);
- return users
- .map(({ user, sharingDoc }) => {
- if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
- else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
- })
- .some(success => !success);
- }
- })
- .some(success => success);
+ this.users
+ .filter(({ user: { email } }) => JSON.parse(StrCast(group.members)).includes(email))
+ .forEach(({ user, sharingDoc }) => {
+ if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+ else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc); // remove the doc from the list if it already exists
+ });
+ }
+ });
};
/**
@@ -280,45 +226,16 @@ export class SharingManager extends React.Component<{}> {
else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
});
} else {
- const dashboards = DocListCast(Doc.MyDashboards.data);
docs.forEach(doc => {
- const isDashboard = dashboards.indexOf(doc) !== -1;
- if (this.overrideNested) {
- this.shareFromPropertiesSidebar(shareWith, permission, DocListCast(doc.data), layout);
- }
if (GetEffectiveAcl(doc) === AclAdmin) {
- if (shareWith == 'Public' && layout) shareWith = 'Public-layout';
- distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
+ distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined);
}
- this.setDashboardBackground(doc, permission as SharingPermissions);
});
}
this.layoutDocAcls = false;
};
/**
- * Sets the background of the Dashboard if it has been shared as a visual indicator
- */
- setDashboardBackground = (doc: Doc, permission: SharingPermissions) => {
- if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
- if (permission !== SharingPermissions.None) {
- doc.isShared = true;
- doc.backgroundColor = 'green';
- } else {
- const acls = doc[DocData][DocAcl];
- if (
- Object.keys(acls)
- .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && key !== 'acl-Me')
- .every(key => [AclUnset, AclPrivate].includes(acls[key]))
- ) {
- doc.isShared = undefined;
- doc.backgroundColor = undefined;
- }
- }
- }
- };
-
- /**
* Removes the documents shared with a user through a group when the user is removed from the group.
* @param group
* @param emailId
@@ -342,12 +259,9 @@ export class SharingManager extends React.Component<{}> {
*/
removeGroup = (group: Doc) => {
if (group.docsShared) {
- const dashboards = DocListCast(Doc.MyDashboards.data);
DocListCast(group.docsShared).forEach(doc => {
const acl = `acl-${StrCast(group.title)}`;
- const isDashboard = dashboards.indexOf(doc) !== -1;
- distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined, isDashboard);
- distributeAcls(acl, SharingPermissions.None, Doc.GetProto(doc), undefined, undefined, isDashboard);
+ distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined);
const members: string[] = JSON.parse(StrCast(group.members));
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -441,7 +355,7 @@ export class SharingManager extends React.Component<{}> {
if (this.selectedUsers) {
this.selectedUsers.forEach(user => {
if (user.value.includes(indType)) {
- this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+ this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions, undefined);
} else {
this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
}
@@ -509,8 +423,7 @@ export class SharingManager extends React.Component<{}> {
const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
- // handles the case where multiple documents are selected
- let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DocData]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DocData]));
+ let docs = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
if (this.myDocAcls) {
const newDocs: Doc[] = [];
@@ -518,36 +431,32 @@ export class SharingManager extends React.Component<{}> {
docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin);
}
- const targetDoc: Doc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
+ const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData];
// tslint:disable-next-line: no-unnecessary-callback-wrapper
const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
// users in common between all docs
- const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).map(doc => doc?.[DocAcl] && Object.keys(doc[DocAcl])));
+ const commonKeys = intersection(docs).reduce((list, doc) => (doc?.[DocAcl] ? [...list, ...Object.keys(doc[DocAcl])] : list), [] as string[]);
// the list of users shared with
- const userListContents: (JSX.Element | null)[] = users
+ const userListContents = users
// .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
.filter(({ user }) => docs[0]?.author !== user.email)
.map(({ user, linkDatabase, sharingDoc, userColor }) => {
const userKey = `acl-${normalizeEmail(user.email)}`;
- const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DocData])).every(doc => doc?.[DocAcl]?.[userKey] === docs[0]?.[DocAcl]?.[userKey]);
+ const uniform = docs.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 (targetDoc[DocAcl][userKey]) permissions = HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name;
- 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-';
+ let permissions = targetDoc[DocAcl][userKey] ? HierarchyMapping.get(targetDoc[DocAcl][userKey])?.name : StrCast(targetDoc[userKey]);
+ permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
return !permissions ? null : (
<div key={userKey} className={'container'}>
<span className={'padding'}>{user.email}</span>
<div className="edit-actions">
{admin || this.myDocAcls ? (
- <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}>
+ <select className={`permissions-dropdown-${permissions}`} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value, undefined)}>
{this.sharingOptions(uniform)}
</select>
) : (
@@ -566,12 +475,7 @@ 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]);
+ const curUserPermission = StrCast(targetDoc[userKey]);
// const curUserPermission = HierarchyMapping.get(effectiveAcls[0])!.name
userListContents.unshift(
sameAuthor ? (
@@ -582,7 +486,7 @@ export class SharingManager extends React.Component<{}> {
</div>
</div>
) : null,
- sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail && Doc.CurrentUserEmail != 'guest' ? (
+ sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ? (
<div key={'me'} className={'container'}>
<span className={'padding'}>Me</span>
<div className="edit-actions">
@@ -600,20 +504,8 @@ export class SharingManager extends React.Component<{}> {
groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
const groupListContents = groupListMap.map(group => {
let groupKey = `acl-${StrCast(group.title)}`;
- const uniform = docs
- .map(doc => (this.layoutDocAcls ? doc : doc[DocData]))
- .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-';
- }
- } else permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
+ const uniform = docs.every(doc => doc?.[DocAcl]?.[groupKey] === docs[0]?.[DocAcl]?.[groupKey]);
+ const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
return !permissions ? null : (
<div key={groupKey} className={'container'}>
@@ -693,7 +585,6 @@ export class SharingManager extends React.Component<{}> {
<div className="acl-container">
{Doc.noviceMode ? null : (
<div className="layoutDoc-acls">
- <input type="checkbox" onChange={action(() => (this.overrideNested = !this.overrideNested))} checked={this.overrideNested} /> <label>Override Nested </label>
<input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
</div>
)}
@@ -713,16 +604,10 @@ export class SharingManager extends React.Component<{}> {
<div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
<div className="title-individual">
Individuals &nbsp;
- {this.individualSort === 'ascending' ? (
- <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
- ) : this.individualSort === 'descending' ? (
- <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
- ) : (
- <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
- )}
+ <FontAwesomeIcon icon={this.individualSort === 'ascending' ? 'caret-up' : this.individualSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
</div>
</div>
- <div className={'users-list'}>{userListContents}</div>
+ <div className="users-list">{userListContents}</div>
</div>
<div className={'group-container'}>
<div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
@@ -732,13 +617,7 @@ export class SharingManager extends React.Component<{}> {
<FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
</div>
&nbsp;
- {this.groupSort === 'ascending' ? (
- <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
- ) : this.groupSort === 'descending' ? (
- <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
- ) : (
- <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
- )}
+ <FontAwesomeIcon icon={this.groupSort === 'ascending' ? 'caret-up' : this.groupSort === 'descending' ? 'caret-down' : 'caret-right'} size="xs" />
</div>
</div>
<div className={'groups-list'}>{groupListContents}</div>
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index b8a6f6c05..583edac08 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -1,39 +1,40 @@
-@import "./global/globalCssVariables";
-
+@import './global/globalCssVariables';
.dashboard-view {
- padding: 50px;
- display: flex;
- flex-direction: row;
- width: 100%;
- position: absolute;
-
- .left-menu {
- display: flex;
- justify-content: flex-start;
- flex-direction: column;
- width: 250px;
- min-width: 250px;
- }
-
- .all-dashboards {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- overflow-y: scroll;
- }
+ padding: 50px;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ position: absolute;
+ height: 100%;
+ overflow: auto;
+
+ .left-menu {
+ display: flex;
+ justify-content: flex-start;
+ flex-direction: column;
+ width: 250px;
+ min-width: 250px;
+ }
+
+ .all-dashboards {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ overflow-y: scroll;
+ }
}
.text-button {
cursor: pointer;
- padding: 3px 0;
- &:hover {
- font-weight: 500;
- }
-
- &.selected {
- font-weight: 700;
- }
+ padding: 3px 0;
+ &:hover {
+ font-weight: 500;
+ }
+
+ &.selected {
+ font-weight: 700;
+ }
}
.new-dashboard-button {
@@ -64,41 +65,51 @@
}
.dashboard-container {
- border-radius: 10px;
- cursor: pointer;
- width: 250px;
- height: 200px;
- outline: solid 2px $light-gray;
- display: flex;
- flex-direction: column;
- margin: 0 0px 30px 30px;
- overflow: hidden;
-
- &:hover{
+ border-radius: 10px;
+ cursor: pointer;
+ width: 250px;
+ height: 200px;
+ outline: solid 2px $light-gray;
+ display: flex;
+ flex-direction: column;
+ margin: 0 0px 30px 30px;
+ overflow: hidden;
+
+ &:hover {
outline: solid 2px $light-blue;
- }
-
- .title {
- margin: 10px;
- font-weight: 500;
- }
-
- img {
- width: auto;
- height: 80%;
- }
-
- .info {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- padding: 0px 10px;
- }
-
- .more {
- z-index: 100;
- }
+ }
+
+ .title {
+ margin: 10px;
+ font-weight: 500;
+ }
+
+ img {
+ width: auto;
+ height: 80%;
+ }
+
+ .info {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0px 10px;
+ }
+ .dashboard-status,
+ .dashboard-status-shared {
+ font-size: 9;
+ left: 10%;
+ position: relative;
+ top: -5;
+ }
+ .dashboard-status-shared {
+ background: 'lightgreen';
+ }
+
+ .more {
+ z-index: 100;
+ }
}
.new-dashboard {
@@ -136,4 +147,4 @@
flex-direction: row;
justify-content: flex-end;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index b60b84015..123090fcf 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -4,7 +4,7 @@ import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
-import { DocData } from '../../fields/DocSymbols';
+import { AclPrivate, AclUnset, DocAcl, DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { PrefetchProxy } from '../../fields/Proxy';
@@ -62,15 +62,13 @@ export class DashboardView extends React.Component {
Doc.ActivePage = 'dashboard';
};
- getDashboards = () => {
+ getDashboards = (whichGroup: DashboardGroup) => {
const allDashboards = DocListCast(Doc.MyDashboards.data);
- if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
+ if (whichGroup === DashboardGroup.MyDashboards) {
return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
- } else {
- const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig)
- // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._type_collection === CollectionViewType.Docking);
- return sharedDashboards;
}
+ const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc.dockingConfig);
+ return sharedDashboards;
};
isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
@@ -159,24 +157,33 @@ export class DashboardView extends React.Component {
<Button icon={<FaPlus />} size={Size.MEDIUM} text="New" onClick={() => this.setNewDashboardName('')} />
</div>
<div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>
- My Dashboards
+ {'My Dashboards (' + this.getDashboards(DashboardGroup.MyDashboards).length + ')'}
</div>
<div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>
- Shared Dashboards
+ {'Shared Dashboards (' + this.getDashboards(DashboardGroup.SharedDashboards).length + ')'}
</div>
</div>
<div className="all-dashboards">
- {this.getDashboards().map(dashboard => {
+ {this.getDashboards(this.selectedDashboardGroup).map(dashboard => {
const href = ImageCast(dashboard.thumb)?.url.href;
+ const shared = Object.keys(dashboard[DocAcl])
+ .filter(key => key !== `acl-${Doc.CurrentUserEmailNormalized}` && !['acl-Me', 'acl-Public'].includes(key))
+ .some(key => ![AclUnset, AclPrivate].includes(dashboard[DocAcl][key]));
return (
- <div className="dashboard-container" key={dashboard[Id]} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
+ <div className="dashboard-container" key={dashboard[Id]} style={{ background: shared ? 'lightgreen' : '' }} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}>
<img
src={
href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
}
/>
<div className="info">
- <input style={{ border: 'unset' }} className="input" onClick={e => e.stopPropagation()} defaultValue={StrCast(dashboard.title)} onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)} />
+ <input
+ style={{ border: 'unset', borderRadius: '5px' }}
+ className="input"
+ onClick={e => e.stopPropagation()}
+ defaultValue={StrCast(dashboard.title)}
+ onChange={e => (Doc.GetProto(dashboard).title = (e.target as any).value)}
+ />
{this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
<div
className="more"
@@ -192,6 +199,7 @@ export class DashboardView extends React.Component {
<Button size={Size.SMALL} icon={<FontAwesomeIcon color="black" size="lg" icon="bars" />} />
</div>
</div>
+ <div className={'dashboard-status' + (shared ? '-shared' : '')}>{shared ? 'shared' : ''}</div>
</div>
);
})}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 1d0feec74..44af51341 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,18 +1,16 @@
import { action, computed, observable } from 'mobx';
import { DateField } from '../../fields/DateField';
-import { DocListCast, Opt, Doc, ReverseHierarchyMap, HierarchyMapping } from '../../fields/Doc';
+import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { Cast, DocCast, ScriptCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
+import { Cast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
import { returnFalse } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
import { InteractionUtils } from '../util/InteractionUtils';
-import { UndoManager } from '../util/UndoManager';
import { DocumentView } from './nodes/DocumentView';
import { Touchable } from './Touchable';
-import { SharingManager } from '../util/SharingManager';
/// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
export interface DocComponentProps {
@@ -184,7 +182,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
return false;
}
- const targetDataDoc = this.props.Document[DocData];
+ const targetDataDoc = this.rootDoc[DocData];
const effectiveAcl = GetEffectiveAcl(targetDataDoc);
if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
@@ -194,52 +192,33 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
if (added.length) {
const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
- 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));
- }
- })
- );
+ GetEffectiveAcl(this.rootDoc) === AclAdmin &&
+ 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 = permissionSymbol && HierarchyMapping.get(permissionSymbol)?.name;
+ distributeAcls(key, permission ?? SharingPermissions.Augment, Doc.GetProto(d));
+ }
+ })
+ );
if (effectiveAcl === AclAugment) {
added.map(doc => {
- Doc.SetContainer(doc, this.props.Document);
- if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
+ if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
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 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);
- }
- }
+ Doc.SetContainer(doc, targetDataDoc);
+ inheritParentAcls(targetDataDoc, doc);
});
} 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.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 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);
- }
- }
+ Doc.SetContainer(doc, this.rootDoc);
+ inheritParentAcls(targetDataDoc, doc);
});
const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4cd172034..3522830e5 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -5,7 +5,6 @@ import { IconButton } from 'browndash-components';
import { action, computed, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { FaUndo } from 'react-icons/fa';
-import { Utils, aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
import { DateField } from '../../fields/DateField';
import { Doc, DocListCast, Field, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData, Height, Width } from '../../fields/DocSymbols';
@@ -13,27 +12,27 @@ import { InkField } from '../../fields/InkField';
import { RichTextField } from '../../fields/RichTextField';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, GetEffectiveLayoutAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
-import { DocumentType } from '../documents/DocumentTypes';
+import { GetEffectiveAcl } from '../../fields/util';
+import { aggregateBounds, emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
import { Docs } from '../documents/Documents';
+import { DocumentType } from '../documents/DocumentTypes';
import { DocumentManager } from '../util/DocumentManager';
import { DragManager } from '../util/DragManager';
import { LinkFollower } from '../util/LinkFollower';
import { SelectionManager } from '../util/SelectionManager';
-import { SettingsManager } from '../util/SettingsManager';
import { SnappingManager } from '../util/SnappingManager';
import { UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
-import { InkStrokeProperties } from './InkStrokeProperties';
+import { Colors } from './global/globalEnums';
import { InkingStroke } from './InkingStroke';
+import { InkStrokeProperties } from './InkStrokeProperties';
import { LightboxView } from './LightboxView';
-import { CollectionDockingView } from './collections/CollectionDockingView';
-import { CollectionFreeFormView } from './collections/collectionFreeForm';
-import { Colors } from './global/globalEnums';
import { DocumentView, OpenWhereMod } from './nodes/DocumentView';
-import { ImageBox } from './nodes/ImageBox';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { ImageBox } from './nodes/ImageBox';
import React = require('react');
import _ = require('lodash');
@@ -165,7 +164,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@action onContainerDown = (e: React.PointerEvent): void => {
const first = SelectionManager.Views()[0];
- const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+ const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
setupMoveUpEvents(
this,
@@ -179,7 +178,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@action onTitleDown = (e: React.PointerEvent): void => {
const first = SelectionManager.Views()[0];
- const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+ const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
if (effectiveLayoutAcl == AclAdmin || effectiveLayoutAcl == AclEdit || effectiveLayoutAcl == AclAugment) {
setupMoveUpEvents(
this,
@@ -200,7 +199,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@action
onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
const first = SelectionManager.Views()[0];
- const effectiveLayoutAcl = GetEffectiveLayoutAcl(first.rootDoc);
+ const effectiveLayoutAcl = GetEffectiveAcl(first.rootDoc);
if (effectiveLayoutAcl != AclAdmin && effectiveLayoutAcl != AclEdit && effectiveLayoutAcl != AclAugment) {
return false;
}
@@ -496,7 +495,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
const first = SelectionManager.Views()[0];
- const effectiveAcl = GetEffectiveLayoutAcl(first.rootDoc);
+ const effectiveAcl = GetEffectiveAcl(first.rootDoc);
if (!(effectiveAcl == AclAdmin || effectiveAcl == AclEdit || effectiveAcl == AclAugment)) return false;
if (!first) return false;
let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
@@ -765,7 +764,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
// sharing
- const acl = this.showLayoutAcl ? GetEffectiveLayoutAcl(seldocview.rootDoc) : GetEffectiveAcl(seldocview.rootDoc);
+ const acl = GetEffectiveAcl(!this.showLayoutAcl ? Doc.GetProto(seldocview.rootDoc) : seldocview.rootDoc);
const docShareMode = HierarchyMapping.get(acl)!.name;
const shareMode = StrCast(docShareMode);
var shareSymbolIcon = ReverseHierarchyMap.get(shareMode)?.image;
@@ -773,7 +772,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
// 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;
+ ![AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(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
@@ -832,14 +831,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
&nbsp;
{shareSymbolIcon + ' ' + shareMode}
&nbsp;
- {/* {!Doc.noviceMode ? (
+ {!Doc.noviceMode ? (
<div className="checkbox">
<div className="checkbox-box">
<input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} />
</div>
<div className="checkbox-text"> Layout </div>
</div>
- ) : null} */}
+ ) : null}
&nbsp;
</div>
</div>
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 47dcdd2e4..347c40c18 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -285,13 +285,17 @@ export class KeyManager {
preventDefault = false;
break;
case 'y':
- SelectionManager.DeselectAll();
- UndoManager.Redo();
+ if (Doc.ActivePage !== 'home') {
+ SelectionManager.DeselectAll();
+ UndoManager.Redo();
+ }
stopPropagation = false;
break;
case 'z':
- SelectionManager.DeselectAll();
- UndoManager.Undo();
+ if (Doc.ActivePage !== 'home') {
+ SelectionManager.DeselectAll();
+ UndoManager.Undo();
+ }
stopPropagation = false;
break;
case 'a':
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 258674d53..b708e2587 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -153,10 +153,12 @@ export class MainView extends React.Component {
}
this._sidebarContent.proto = undefined;
if (!MainView.Live) {
- DocServer.setPlaygroundFields([
+ DocServer.setPlaygroundFields(['dockingConfig']);
+ DocServer.setLivePlaygroundFields([
'dataTransition',
'viewTransition',
'treeViewOpen',
+ 'treeViewExpandedView',
'carousel_index',
'itemIndex', // for changing slides in presentations
'layout_sidebarWidthPercent',
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 2b12a7b58..633401d58 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -395,9 +395,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div>
<div className={'propertiesView-shareDropDown'}>
<div className={`propertiesView-shareDropDown${permission}`}>
- <div className="propertiesView-shareDropDown">
- {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}
- </div>
+ <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : concat(shareImage, ' ', permission)}</div>
</div>
</div>
</div>
@@ -425,11 +423,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@computed get sharingTable() {
// all selected docs
- const docs =
- SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutDocAcls ? this.selectedDoc : this.dataDoc!] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DocData]));
+ const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.rootDoc);
const target = docs[0];
- const showAdmin = GetEffectiveAcl(target) == AclAdmin
+ const showAdmin = GetEffectiveAcl(target) == AclAdmin;
const individualTableEntries = [];
const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
@@ -450,27 +447,21 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
usersAdded.sort(this.sortUsers);
usersAdded.map(userEmail => {
const userKey = `acl-${normalizeEmail(userEmail)}`;
- var permission;
- if (this.layoutDocAcls){
- if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
- else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
- else permission = StrCast(Doc.GetProto(target)?.[userKey]);
- }
- else permission = StrCast(target[userKey]);
+ var permission = StrCast(target[userKey]);
individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
});
// adds current user
var userEmail = Doc.CurrentUserEmail;
const userKey = `acl-${normalizeEmail(userEmail)}`;
- if (!usersAdded.includes(userEmail) && userEmail != 'guest' && userEmail != target.author) {
+ if (userEmail == 'guest') userEmail = 'Public';
+ if (!usersAdded.includes(userEmail) && userEmail != 'Public' && userEmail != target.author) {
var permission;
- if (this.layoutDocAcls){
+ if (this.layoutDocAcls) {
if (target[DocAcl][userKey]) permission = HierarchyMapping.get(target[DocAcl][userKey])?.name;
else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[userKey]);
else permission = StrCast(Doc.GetProto(target)?.[userKey]);
- }
- else permission = StrCast(target[userKey]);
+ } else permission = StrCast(target[userKey]);
individualTableEntries.unshift(this.sharingItem(userEmail, showAdmin, permission!, false)); // adds each user
}
@@ -480,36 +471,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
// adds groups
const groupTableEntries: JSX.Element[] = [];
const groupList = GroupManager.Instance?.allGroups || [];
- groupList.sort(this.sortGroups)
+ groupList.sort(this.sortGroups);
groupList.map(group => {
if (group.title != 'Public' && this.selectedDoc) {
const groupKey = 'acl-' + normalizeEmail(StrCast(group.title));
if (this.selectedDoc[groupKey] != '' && this.selectedDoc[groupKey] != undefined) {
var permission;
- if (this.layoutDocAcls){
- if (target[DocAcl][groupKey]){
+ if (this.layoutDocAcls) {
+ if (target[DocAcl][groupKey]) {
permission = HierarchyMapping.get(target[DocAcl][groupKey])?.name;
- }
- else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
+ } else if (target['embedContainer']) permission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))[groupKey]);
else permission = StrCast(Doc.GetProto(target)?.[groupKey]);
- }
- else permission = StrCast(target[groupKey]);
+ } else permission = StrCast(target[groupKey]);
groupTableEntries.unshift(this.sharingItem(StrCast(group.title), showAdmin, permission!, false));
}
}
});
// public permission
- let publicPermission = StrCast(target['acl-Public']);
- if (this.layoutDocAcls){
- if (target['acl-Public-layout']) publicPermission = StrCast(target['acl-Public-layout']);
- else if (target['embedContainer']) publicPermission = StrCast(Doc.GetProto(DocCast(target['embedContainer']))['acl-Public']);
- else StrCast(Doc.GetProto(target)['acl-Public']);
- }
+ const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Public']);
return (
<div>
- <br/>
+ <br />
Public / Guest Users
<div>{this.colorACLDropDown('Public', showAdmin, publicPermission!, false)}</div>
<div>
@@ -517,7 +501,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<br></br> Individual Users with Access to this Document{' '}
</div>
<div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div>
- {groupTableEntries.length>0 ?
+ {groupTableEntries.length > 0 ? (
<div>
<div>
{' '}
@@ -525,7 +509,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
</div>
<div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div>
</div>
- : null}
+ ) : null}
</div>
);
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index ce9eb9f17..0daa3dd92 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -8,7 +8,7 @@ import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { distributeAcls, inheritParentAcls } from '../../../fields/util';
+import { distributeAcls, GetEffectiveAcl, GetPropAcl, inheritParentAcls } from '../../../fields/util';
import { emptyFunction, incrementTitleCopy } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
@@ -30,7 +30,7 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { TabDocView } from './TabDocView';
import React = require('react');
import { DocumentManager } from '../../util/DocumentManager';
-import { DocAcl } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocAcl } from '../../../fields/DocSymbols';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -386,8 +386,13 @@ export class CollectionDockingView extends CollectionSubView() {
.map(f => f as Doc);
const changesMade = this.props.Document.dockingConfig !== json;
if (changesMade) {
- this.props.Document.dockingConfig = json;
- this.props.Document.data = new List<Doc>(docs);
+ if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
+ this.layoutDoc.dockingConfig = json;
+ this.layoutDoc.data = new List<Doc>(docs);
+ } else {
+ Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
+ Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+ }
}
this._flush?.end();
this._flush = undefined;
@@ -521,7 +526,7 @@ export class CollectionDockingView extends CollectionSubView() {
_layout_fitWidth: true,
title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
});
- this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+ inheritParentAcls(this.rootDoc, docToAdd);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
});
@@ -564,7 +569,7 @@ export class CollectionDockingView extends CollectionSubView() {
_freeform_backgroundGrid: true,
title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
});
- this.props.Document.isShared && inheritParentAcls(Doc.GetProto(this.props.Document), Doc.GetProto(docToAdd));
+ inheritParentAcls(this.dataDoc, Doc.GetProto(docToAdd));
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
})
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index a2269075d..7767c5b79 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<TreeViewProps> {
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.SetContainer(doc, this.doc.embedContainer);
+ dataIsComputed && Doc.SetContainer(doc, DocCast(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<TreeViewProps> {
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.SetContainer(doc, this.doc.embedContainer);
+ dataIsComputed && Doc.SetContainer(doc, DocCast(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<TreeViewProps> {
}
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.SetContainer(doc, this.doc.embedContainer);
+ !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
return added;
};
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dab269474..a4f5eb62b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -535,7 +535,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// if this is part of a template, let the event go up to the template root unless right/ctrl clicking
if (
// prettier-ignore
- this.props.isDocumentActive?.() &&
+ (this.props.isDocumentActive?.() || this.props.isContentActive?.()) &&
!this.props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
e.button === 0 &&
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9c138d348..28fbdc192 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -481,7 +481,7 @@ export namespace Doc {
doc.embedContainer = container;
if (Doc.GetProto(container).author === doc.author) {
Object.keys(Doc.GetProto(container))
- .filter(key => key.startsWith('acl'))
+ .filter(key => key.startsWith('acl') && !key.includes(Doc.CurrentUserEmailNormalized))
.forEach(key => (doc[key] = Doc.GetProto(container)[key]));
}
}
@@ -696,7 +696,7 @@ export namespace Doc {
Doc.SetLayout(embedding, Doc.MakeEmbedding(layout));
}
embedding.createdFrom = doc;
- embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = NumCast(Doc.GetProto(doc).proto_embeddingId) + 1;
+ embedding.proto_embeddingId = Doc.GetProto(doc).proto_embeddingId = DocListCast(Doc.GetProto(doc).proto_embeddings).length + 1;
embedding.title = ComputedField.MakeFunction(`renameEmbedding(this)`);
embedding.author = Doc.CurrentUserEmail;
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 068323dce..0e9940ced 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -14,6 +14,7 @@ import { List } from './List';
import { ObjectField } from './ObjectField';
import { PrefetchProxy, ProxyField } from './Proxy';
import { RefField } from './RefField';
+import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
import { ComputedField } from './ScriptField';
import { ScriptCast, StrCast } from './Types';
@@ -62,8 +63,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
const writeMode = DocServer.getFieldWriteMode(prop as string);
const fromServer = target[UpdatingFromServer];
const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
- const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
- const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin) && !DocServer.Control.isReadOnly();
+ const writeToDoc =
+ sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode === DocServer.WriteMode.Playground || writeMode === DocServer.WriteMode.LivePlayground || (effectiveAcl === AclAugment && value instanceof RichTextField);
+ const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclAugment && Doc.CurrentUserEmail !== 'guest' && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
if (writeToDoc) {
if (value === undefined) {
@@ -104,7 +106,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
);
return true;
}
- return false;
+ return true;
});
let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl;
@@ -128,13 +130,14 @@ export function denormalizeEmail(email: string) {
* Copies parent's acl fields to the child
*/
export function inheritParentAcls(parent: Doc, child: Doc) {
- for (const key of Object.keys(Doc.GetProto(parent))) {
+ if (GetEffectiveAcl(parent) !== AclAdmin) return;
+ 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 : Doc.GetProto(parent)[key];
- const symbol = ReverseHierarchyMap.get(StrCast(Doc.GetProto(parent)[key]));
+ // const permission: string = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : parent[key];
+ const symbol = ReverseHierarchyMap.get(StrCast(parent[key]));
if (symbol) {
const sharePermission = HierarchyMapping.get(symbol.acl!)!.name;
- key.startsWith('acl') && distributeAcls(key, sharePermission, Doc.GetProto(child));
+ key.startsWith('acl') && distributeAcls(key, sharePermission, child);
}
}
}
@@ -168,30 +171,16 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
return getEffectiveAcl(target, user);
}, true);
-// return layout acl from cache or chache the acl and return.
-const getEffectiveLayoutAclCache = computedFn(function (target: any, user?: string) {
- return getEffectiveLayoutAcl(target, user);
-}, true);
-
/**
* Calculates the effective access right to a document for the current user.
*/
export function GetEffectiveAcl(target: any, user?: string): symbol {
if (!target) return AclPrivate;
if (target[UpdatingFromServer]) return AclAdmin;
- return getEffectiveAclCache(Doc.GetProto(target), user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
-}
-
-/**
- * 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);
+ return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
}
-function getPropAcl(target: any, prop: string | symbol | number) {
+export 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
if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
return GetEffectiveAcl(target);
@@ -209,13 +198,9 @@ export function SetCachedGroups(groups: string[]) {
}
function getEffectiveAcl(target: any, user?: string): symbol {
const targetAcls = target[DocAcl];
- // if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
+ if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
-
- // guest dashboard permissions error
- if (userChecked == 'guest' && target._type_collection == CollectionViewType.Docking) return AclAugment;
-
if (targetAcls && Object.keys(targetAcls).length) {
let effectiveAcl = AclPrivate;
for (const [key, value] of Object.entries(targetAcls)) {
@@ -238,37 +223,6 @@ 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.
- */
-function getEffectiveLayoutAcl(target: any, user?: string): symbol {
- const targetAcls = target[DocAcl];
-
- const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
- if (targetAcls && Object.keys(targetAcls).length) {
- var effectiveAcl;
- for (const [key, value] of Object.entries(targetAcls)) {
- const entity = denormalizeEmail(key.substring(4)); // an individual or a group
- 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 {
- effectiveAcl = value as symbol;
- }
- }
- }
-
- if (effectiveAcl) {
- return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(effectiveAcl)!.level < aclLevel.editable ? AclEdit : effectiveAcl;
- }
- }
- // authored documents are private until an ACL is set.
- const targetAuthor = target.__fieldTuples?.author || target.author; // target may be a Doc or Proxy, so check __fieldTuples.author and .author
- if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
- 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)
* @param acl the access right being stored (e.g. "Can Edit")
@@ -276,56 +230,51 @@ function getEffectiveLayoutAcl(target: any, user?: string): symbol {
* @param inheritingFromCollection whether the target is being assigned rights after being dragged into a collection (and so is inheriting the acls from the collection)
* inheritingFromCollection is not currently being used but could be used if acl assignment defaults change
*/
-export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[], isDashboard?: boolean) {
+export function distributeAcls(key: string, acl: SharingPermissions, target: Doc, inheritingFromCollection?: boolean, visited?: Doc[]) {
if (!visited) visited = [] as Doc[];
if (!target || visited.includes(target)) return;
if ((target._type_collection === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
- target[key] = acl;
- // if (target !== Doc.GetProto(target)) {
- // //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
- // updateCachedAcls(target);
- // }
+ if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+ target[key] = acl;
+ }
+ if (target !== Doc.GetProto(target)) {
+ //apparently we can't call updateCachedAcls twice (once for the main dashboard, and again for the nested dashboard...???)
+ updateCachedAcls(target);
+ }
//return;
}
visited.push(target);
let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
// if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive
- // taken care of in SharingManager now so that users can decide if they want nested docs changed
- // if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
- // target[key] = acl;
- // layoutDocChanged = true;
- // if (isDashboard) {
- // DocListCastAsync(target[Doc.LayoutFieldKey(target)]).then(docs => {
- // docs?.forEach(d => distributeAcls(key, acl, d, inheritingFromCollection, visited));
- // });
- // }
- // }
+ if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || ReverseHierarchyMap.get(StrCast(target[key]))!.level > ReverseHierarchyMap.get(acl)!.level)) {
+ if (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`) {
+ target[key] = acl;
+ layoutDocChanged = true;
+ }
+ }
let dataDocChanged = false;
const dataDoc = target[DocData];
if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || ReverseHierarchyMap.get(StrCast(dataDoc[key]))! > ReverseHierarchyMap.get(acl)!)) {
-
- // changing doc proto - but this messes up layout
- // if (GetEffectiveAcl(dataDoc) === AclAdmin) {
- // dataDoc[key] = acl;
- // dataDocChanged = true;
- // }
+ if (GetEffectiveAcl(dataDoc) === AclAdmin && (target.author !== Doc.CurrentUserEmail || key !== `acl-${Doc.CurrentUserEmailNormalized}`)) {
+ dataDoc[key] = acl;
+ dataDocChanged = true;
+ }
// maps over the links of the document
LinkManager.Links(dataDoc).forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited));
// maps over the children of the document
- // taken care of in SharingManager now so that users can decide if they want nested docs changed
- // DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
- // distributeAcls(key, acl, d, inheritingFromCollection, visited);
- // distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
- // });
+ DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).forEach(d => {
+ distributeAcls(key, acl, d, inheritingFromCollection, visited);
+ d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
+ });
// maps over the annotations of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '_annotations']).forEach(d => {
distributeAcls(key, acl, d, inheritingFromCollection, visited);
- distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
+ d !== d[DocData] && distributeAcls(key, acl, d[DocData], inheritingFromCollection, visited);
});
}
@@ -335,7 +284,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean {
let prop = in_prop;
- const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : getPropAcl(target, prop);
+ const effectiveAcl = in_prop === 'constructor' || typeof in_prop === 'symbol' ? AclAdmin : GetPropAcl(target, prop);
if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin) return true;
// if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined].includes(value))) return true;