From cf88809ea2299395db70ce608c193df7f24f0fb2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 7 Jul 2023 15:57:51 -0400
Subject: fixed self-ownership of copied Docs created by someone else. prevent
 dash from being pasted into itself.  fixed inheritance of acls

---
 src/client/util/SharingManager.tsx | 109 ++++++++++++++++++++-----------------
 1 file changed, 59 insertions(+), 50 deletions(-)

(limited to 'src/client/util/SharingManager.tsx')

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>
                                 )}
-- 
cgit v1.2.3-70-g09d2

