aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts6
-rw-r--r--src/client/util/SharingManager.tsx109
-rw-r--r--src/client/views/DocComponent.tsx32
-rw-r--r--src/client/views/PreviewCursor.tsx26
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx12
5 files changed, 92 insertions, 93 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 8b8a9a618..67be96d13 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -198,7 +198,7 @@ export namespace DocServer {
export namespace Control {
let _isReadOnly = false;
export function makeReadOnly() {
- if (!_isReadOnly) {
+ if (!Control.isReadOnly()) {
_isReadOnly = true;
_CreateField = field => (_cache[field[Id]] = field);
_UpdateField = emptyFunction;
@@ -207,7 +207,7 @@ export namespace DocServer {
}
export function makeEditable() {
- if (_isReadOnly) {
+ if (Control.isReadOnly() && Doc.CurrentUserEmail !== 'guest') {
location.reload();
// _isReadOnly = false;
// _CreateField = _CreateFieldImpl;
@@ -218,7 +218,7 @@ export namespace DocServer {
}
export function isReadOnly() {
- return _isReadOnly;
+ return _isReadOnly || Doc.CurrentUserEmail === 'guest';
}
}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d3241009a..828271270 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,4 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Colors } from 'browndash-components';
import { concat, intersection } from 'lodash';
import { action, computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -22,6 +23,7 @@ import { GroupManager, UserOptions } from './GroupManager';
import { GroupMemberView } from './GroupMemberView';
import { SelectionManager } from './SelectionManager';
import './SharingManager.scss';
+import { undoable } from './UndoManager';
export interface User {
email: string;
@@ -76,8 +78,10 @@ export class SharingManager extends React.Component<{}> {
@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 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 layoutDocAcls: boolean = false; // whether the layout doc or data doc's acls are to be used
@observable private myDocAcls: boolean = false; // whether the My Docs checkbox is selected or not
+ @observable private _buttonDown = false;
// private get linkVisible() {
// return this.targetDoc ? this.targetDoc["acl-" + PublicKey] !== SharingPermissions.None : false;
@@ -91,6 +95,7 @@ export class SharingManager extends React.Component<{}> {
DictationOverlay.Instance.hasActiveModal = true;
this.isOpen = this.targetDoc !== undefined;
this.permissions = SharingPermissions.Augment;
+ this.overrideNested = true;
});
};
@@ -149,36 +154,30 @@ export class SharingManager extends React.Component<{}> {
/**
* Shares the document with a user.
*/
- setInternalSharing = (recipient: ValidatedUser, permission: string, targetDoc: Doc | undefined) => {
+ setInternalSharing = undoable((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 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);
-
- distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+ distributeAcls(acl, permission as SharingPermissions, doc);
if (permission !== SharingPermissions.None) Doc.AddDocToList(sharingDoc, storage, doc);
else GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.createdFrom as Doc) || doc);
});
- };
+ }, 'set Doc permissions');
/**
* Sets the permission on the target for the group.
* @param group
* @param permission
*/
- setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
+ setInternalGroupSharing = undoable((group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
const target = targetDoc || this.targetDoc!;
- const key = normalizeEmail(StrCast(group.title));
- let acl = `acl-${key}`;
+ const acl = `acl-${normalizeEmail(StrCast(group.title))}`;
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);
-
- distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined);
+ distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.overrideNested);
if (group instanceof Doc) {
Doc.AddDocToList(group, 'docsShared', doc);
@@ -191,7 +190,7 @@ export class SharingManager extends React.Component<{}> {
});
}
});
- };
+ }, 'set group permissions');
/**
* Shares the documents shared with a group with a new user who has been added to that group.
@@ -217,23 +216,23 @@ export class SharingManager extends React.Component<{}> {
/**
* Called from the properties sidebar to change permissions of a user.
*/
- shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
+ shareFromPropertiesSidebar = undoable((shareWith: string, permission: SharingPermissions, docs: Doc[], layout: boolean) => {
if (layout) this.layoutDocAcls = true;
if (shareWith !== 'Public' && shareWith !== 'Override') {
const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
docs.forEach(doc => {
if (user) this.setInternalSharing(user, permission, doc);
- else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
+ else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc, undefined, true);
});
} else {
docs.forEach(doc => {
if (GetEffectiveAcl(doc) === AclAdmin) {
- distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined);
+ distributeAcls(`acl-${shareWith}`, permission, doc, undefined, true);
}
});
}
this.layoutDocAcls = false;
- };
+ }, 'sidebar set permissions');
/**
* Removes the documents shared with a user through a group when the user is removed from the group.
@@ -261,7 +260,7 @@ export class SharingManager extends React.Component<{}> {
if (group.docsShared) {
DocListCast(group.docsShared).forEach(doc => {
const acl = `acl-${StrCast(group.title)}`;
- distributeAcls(acl, SharingPermissions.None, doc, undefined, undefined);
+ distributeAcls(acl, SharingPermissions.None, doc);
const members: string[] = JSON.parse(StrCast(group.members));
const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
@@ -342,39 +341,43 @@ export class SharingManager extends React.Component<{}> {
/**
* Handles changes in the permission chosen to share with someone with
*/
- @action
- handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
- this.permissions = event.currentTarget.value as SharingPermissions;
- };
+ handlePermissionsChange = undoable(
+ action((event: React.ChangeEvent<HTMLSelectElement>) => {
+ this.permissions = event.currentTarget.value as SharingPermissions;
+ }),
+ 'permission change'
+ );
/**
* Calls the relevant method for sharing, displays the popup, and resets the relevant variables.
*/
- @action
- share = () => {
- 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, undefined);
- } else {
- this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
- }
- });
-
- const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
- TaskCompletionBox.popupX = left - 1.5 * width;
- TaskCompletionBox.popupY = top - 1.5 * height;
- TaskCompletionBox.textDisplayed = 'Document shared!';
- TaskCompletionBox.taskCompleted = true;
- setTimeout(
- action(() => (TaskCompletionBox.taskCompleted = false)),
- 2000
- );
+ share = undoable(
+ action(() => {
+ 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, undefined);
+ } else {
+ this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
+ }
+ });
+
+ const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
+ TaskCompletionBox.popupX = left - 1.5 * width;
+ TaskCompletionBox.popupY = top - 1.5 * height;
+ TaskCompletionBox.textDisplayed = 'Document shared!';
+ TaskCompletionBox.taskCompleted = true;
+ setTimeout(
+ action(() => (TaskCompletionBox.taskCompleted = false)),
+ 2000
+ );
- this.layoutDocAcls = false;
- this.selectedUsers = null;
- }
- };
+ this.layoutDocAcls = false;
+ this.selectedUsers = null;
+ }
+ }),
+ 'share Doc'
+ );
/**
* Sorting algorithm to sort users.
@@ -542,12 +545,17 @@ export class SharingManager extends React.Component<{}> {
<b>Share </b>
{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
</p>
- <button className="share-copy-link" onClick={this.copyURL}>
- <FontAwesomeIcon title={'Copy Public URL'} icon={'copy'} size={'sm'} onClick={this.copyURL} />
+ <button
+ className="share-copy-link"
+ style={{ background: this._buttonDown ? Colors.LIGHT_BLUE : undefined }}
+ onPointerDown={action(e => (this._buttonDown = true))}
+ onPointerUp={action(e => (this._buttonDown = false))}
+ onClick={this.copyURL}>
+ <FontAwesomeIcon title="Copy Public URL" icon="copy" size="sm" />
&nbsp; Copy Public URL
</button>
- <div className={'close-button'} onClick={this.close}>
- <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
+ <div className="close-button" onClick={this.close}>
+ <FontAwesomeIcon icon="times" color="black" size="lg" />
</div>
{admin ? (
<div className="share-container">
@@ -585,6 +593,7 @@ 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>
)}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 44af51341..422d2d6d7 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -190,36 +190,24 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
}
const added = docs;
if (added.length) {
- const aclKeys = Object.keys(Doc.GetProto(this.props.Document)[DocAcl] ?? {});
-
- GetEffectiveAcl(this.rootDoc) === AclAdmin &&
- aclKeys.forEach(key =>
+ Object.keys(Doc.GetProto(this.rootDoc)[DocAcl]) // apply all collection acls (except pseudo-acl 'Me') to each added doc
+ .filter(key => key !== 'acl-Me')
+ .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));
- }
+ 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, d);
})
);
- if (effectiveAcl === AclAugment) {
+ if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
added.map(doc => {
+ doc._dragOnlyWithinContainer = undefined;
if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
- Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
- Doc.SetContainer(doc, targetDataDoc);
+ Doc.SetContainer(doc, this.rootDoc);
inheritParentAcls(targetDataDoc, doc);
});
- } else {
- added
- .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
- .map(doc => {
- doc._dragOnlyWithinContainer = undefined;
- if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.rootDoc;
- Doc.SetContainer(doc, this.rootDoc);
- inheritParentAcls(targetDataDoc, doc);
- });
const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
if (annoDocs instanceof List) annoDocs.push(...added.filter(add => !annoDocs.includes(add)));
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index b513fe245..82d2bff56 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -50,18 +50,20 @@ export class PreviewCursor extends React.Component<{}> {
PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, PreviewCursor._addDocument).then(batch.end);
} else if (re.test(plain)) {
const url = plain;
- undoBatch(() =>
- PreviewCursor._addDocument(
- Docs.Create.WebDocument(url, {
- title: url,
- _width: 500,
- _height: 300,
- data_useCors: true,
- x: newPoint[0],
- y: newPoint[1],
- })
- )
- )();
+ if (url.startsWith(window.location.href)) {
+ undoBatch(() =>
+ PreviewCursor._addDocument(
+ Docs.Create.WebDocument(url, {
+ title: url,
+ _width: 500,
+ _height: 300,
+ data_useCors: true,
+ x: newPoint[0],
+ y: newPoint[1],
+ })
+ )
+ )();
+ } else alert('cannot paste dash into itself');
} else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
const clone = plain.startsWith('__DashCloneId(');
const docids = plain.split(':');
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0daa3dd92..30bc8cbec 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -500,15 +500,15 @@ 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 aclKeys = Object.keys(Doc.GetProto(this.rootDoc)[DocAcl] ?? {});
+ aclKeys
+ .filter(key => key !== 'acl-Me')
+ .forEach(key => {
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));
- }
- });
+ distributeAcls(key, permission, tab);
+ });
this.tabMap.add(tab);
tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
};