diff options
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/documents/Documents.ts | 1 | ||||
-rw-r--r-- | src/client/util/GroupManager.tsx | 17 | ||||
-rw-r--r-- | src/client/util/SharingManager.scss | 3 | ||||
-rw-r--r-- | src/client/util/SharingManager.tsx | 35 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 15 |
5 files changed, 45 insertions, 26 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2d02742c5..0e134b6d0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -603,6 +603,7 @@ export namespace Docs { viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc); viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; + viewDoc["acl-Override"] = dataDoc["acl-Override"] = "unset"; return Doc.assign(viewDoc, delegateProps, true); } diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index cb15b5081..cb512bca8 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -278,21 +278,24 @@ export class GroupManager extends React.Component<{}> { */ @action createGroup = () => { - if (!this.inputRef.current?.value) { + const { value } = this.inputRef.current!; + if (!value) { alert("Please enter a group name"); return; } - if (this.inputRef.current.value.toLowerCase() === "admin" && this.getGroup("Admin")) { - alert("You cannot override the Admin group"); - return; + if (["admin", "public", "override"].includes(value.toLowerCase())) { + if (value.toLowerCase() !== "admin" || (value.toLowerCase() === "admin" && this.getGroup("Admin"))) { + alert(`You cannot override the ${value.charAt(0).toUpperCase() + value.slice(1)} group`); + return; + } } - if (this.getGroup(this.inputRef.current.value)) { + if (this.getGroup(value)) { alert("Please select a unique group name"); return; } - this.createGroupDoc(this.inputRef.current.value, this.selectedUsers?.map(user => user.value)); + this.createGroupDoc(value, this.selectedUsers?.map(user => user.value)); this.selectedUsers = null; - this.inputRef.current.value = ""; + this.inputRef.current!.value = ""; this.buttonColour = "#979797"; const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect(); diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 39d364fcb..06458827a 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -72,7 +72,8 @@ } } - .layoutDoc-acls { + .layoutDoc-acls, + .myDocs-acls { display: flex; flex-direction: column; float: right; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 169a1e5a4..886a63525 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -21,6 +21,7 @@ import { GroupMemberView } from "./GroupMemberView"; import "./SharingManager.scss"; import { SelectionManager } from "./SelectionManager"; import { intersection } from "lodash"; +import { SearchBox } from "../views/search/SearchBox"; export interface User { email: string; @@ -75,6 +76,7 @@ export class SharingManager extends React.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 myDocAcls: boolean = false; // private get linkVisible() { // return this.sharingDoc ? this.sharingDoc[PublicKey] !== SharingPermissions.None : false; @@ -158,7 +160,7 @@ export class SharingManager extends React.Component<{}> { docs.forEach(doc => { doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc); - GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc); + distributeAcls(acl, permission as SharingPermissions, doc); if (group instanceof Doc) { const members: string[] = JSON.parse(StrCast(group.members)); @@ -189,7 +191,7 @@ export class SharingManager extends React.Component<{}> { * Called from the properties sidebar to change permissions of a user. */ shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => { - if (shareWith !== "Public") { + 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); @@ -198,7 +200,7 @@ export class SharingManager extends React.Component<{}> { } else { docs.forEach(doc => { - if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls("acl-Public", permission, doc); + if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc); }); } } @@ -246,12 +248,11 @@ export class SharingManager extends React.Component<{}> { const key = user.email.replace('.', '_'); const acl = `acl-${key}`; - const docs = SelectionManager.SelectedDocuments().length < 2 ? [target] : SelectionManager.SelectedDocuments().map(docView => docView.props.Document); docs.forEach(doc => { doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmail.replace(".", "_")}`] && distributeAcls(`acl-${Doc.CurrentUserEmail.replace(".", "_")}`, SharingPermissions.Admin, doc); - GetEffectiveAcl(doc) === AclAdmin && distributeAcls(acl, permission as SharingPermissions, doc); + distributeAcls(acl, permission as SharingPermissions, doc); if (permission !== SharingPermissions.None) Doc.IndexOf(doc, DocListCast(notificationDoc[storage])) === -1 && Doc.AddDocToList(notificationDoc, storage, doc); else GetEffectiveAcl(doc, undefined, user.email) === AclPrivate && Doc.IndexOf((doc.aliasOf as Doc || doc), DocListCast(notificationDoc[storage])) !== -1 && Doc.RemoveDocFromList(notificationDoc, storage, (doc.aliasOf as Doc || doc)); @@ -285,9 +286,10 @@ export class SharingManager extends React.Component<{}> { /** * Returns the SharingPermissions (Admin, Can Edit etc) access that's used to share */ - private sharingOptions(uniform: boolean) { + private sharingOptions(uniform: boolean, override?: boolean) { const dropdownValues: string[] = Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift("-multiple-"); + if (override) dropdownValues.unshift("unset"); return dropdownValues.map(permission => ( <option key={permission} value={permission}> @@ -415,14 +417,20 @@ export class SharingManager extends React.Component<{}> { 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 - const docs = SelectionManager.SelectedDocuments().length < 2 ? + let docs = SelectionManager.SelectedDocuments().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.SelectedDocuments().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]); + if (this.myDocAcls) { + const newDocs: Doc[] = []; + SearchBox.foreachRecursiveDoc(docs, doc => newDocs.push(doc)); + docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin); + } + const targetDoc = docs[0]; // tslint:disable-next-line: no-unnecessary-callback-wrapper - const admin = docs.map(doc => GetEffectiveAcl(doc)).every(acl => acl === AclAdmin); // if the user has admin access to all selected docs + const admin = this.myDocAcls ? Boolean(docs.length) : docs.map(doc => GetEffectiveAcl(doc)).every(acl => acl === AclAdmin); // if the user has admin access to all selected docs // users in common between all docs const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym]))); @@ -440,7 +448,7 @@ export class SharingManager extends React.Component<{}> { > <span className={"padding"}>{user.email}</span> <div className="edit-actions"> - {admin ? ( + {admin || this.myDocAcls ? ( <select className={"permissions-dropdown"} value={permissions} @@ -496,7 +504,7 @@ export class SharingManager extends React.Component<{}> { // the list of groups shared with const groupListMap: (Doc | { groupName: string })[] = groups.filter(({ groupName }) => docs.length > 1 ? commonKeys.includes(`acl-${StrCast(groupName).replace('.', '_')}`) : true); - groupListMap.unshift({ groupName: "Public" }); + groupListMap.unshift({ groupName: "Public" }, { groupName: "Override" }); const groupListContents = groupListMap.map(group => { const groupKey = `acl-${StrCast(group.groupName)}`; const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]); @@ -514,13 +522,13 @@ export class SharingManager extends React.Component<{}> { </div>) : (null)} <div className="edit-actions"> - {admin ? ( + {admin || this.myDocAcls ? ( <select className={"permissions-dropdown"} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)} > - {this.sharingOptions(uniform)} + {this.sharingOptions(uniform, group.groupName === "Override")} </select> ) : ( <div className={"permissions-dropdown"}> @@ -572,6 +580,9 @@ export class SharingManager extends React.Component<{}> { <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label> <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label> </div> + <div className="myDocs-acls"> + <input type="checkbox" onChange={action(() => this.myDocAcls = !this.myDocAcls)} checked={this.myDocAcls} /> <label>My Docs</label> + </div> {Doc.UserDoc().noviceMode ? (null) : <div className="layoutDoc-acls"> <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label> diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index c2c0d553b..bc787fc03 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -4,7 +4,7 @@ import { Checkbox, Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc"; +import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { ComputedField } from "../../fields/ScriptField"; @@ -361,7 +361,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { /** * @returns a row of the permissions panel */ - sharingItem(name: string, admin: boolean, permission: string) { + sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) { return <div className="propertiesView-sharingTable-item" key={name + permission} // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }} // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)} @@ -370,7 +370,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {/* {name !== "Me" ? this.notifyIcon : null} */} <div className="propertiesView-sharingTable-item-permission"> {admin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission} - {permission === "Owner" ? this.expansionIcon : null} + {permission === "Owner" || showExpansionIcon ? this.expansionIcon : null} </div> </div>; } @@ -380,6 +380,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ @computed get sharingTable() { const AclMap = new Map<symbol, string>([ + [AclUnset, "unset"], [AclPrivate, SharingPermissions.None], [AclReadonly, SharingPermissions.View], [AclAddonly, SharingPermissions.Add], @@ -408,7 +409,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { for (const key of commonKeys) { const name = key.substring(4).replace("_", "."); const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]); - if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) { + if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) { tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-")); } } @@ -421,10 +422,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { // } // }) + const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author); // shifts the current user, owner, public to the top of the doc. + tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "unset") : "-multiple-")); tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Public"] === docs[0]["acl-Public"]) ? (AclMap.get(target[AclSym]?.["acl-Public"]) || SharingPermissions.None) : "-multiple-")); - tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-")); - if (Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author)) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner")); + tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-", !ownerSame)); + if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner")); return <div className="propertiesView-sharingTable"> {tableEntries} |