aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/DocumentManager.ts40
-rw-r--r--src/client/util/DragManager.ts2
-rw-r--r--src/client/util/GroupManager.tsx17
-rw-r--r--src/client/util/LinkManager.ts2
-rw-r--r--src/client/util/SharingManager.scss4
-rw-r--r--src/client/util/SharingManager.tsx44
-rw-r--r--src/client/views/PropertiesButtons.tsx2
-rw-r--r--src/client/views/PropertiesView.tsx19
-rw-r--r--src/client/views/collections/CollectionMenu.tsx3
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx8
-rw-r--r--src/client/views/nodes/DocumentView.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx52
-rw-r--r--src/client/views/search/SearchBox.tsx2
-rw-r--r--src/fields/Doc.ts4
-rw-r--r--src/fields/util.ts35
19 files changed, 148 insertions, 106 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2d02742c5..2de1090a2 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);
}
@@ -747,6 +748,8 @@ export namespace Docs {
I.author = Doc.CurrentUserEmail;
I.rotation = 0;
I.data = new InkField(points);
+ I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add;
+ I["acl-Override"] = "unset";
return I;
}
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index c3d78a028..116883fcb 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -19,6 +19,7 @@ export class DocumentManager {
//global holds all of the nodes (regardless of which collection they're in)
@observable
public DocumentViews: DocumentView[] = [];
+ @observable LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
// singleton instance
private static _instance: DocumentManager;
@@ -32,6 +33,26 @@ export class DocumentManager {
private constructor() {
}
+ @action
+ public AddView = (view: DocumentView) => {
+ const linksList = DocListCast(view.props.Document.links);
+ linksList.forEach(link => {
+ const linkToDoc = link && LinkManager.getOppositeAnchor(link, view.props.Document);
+ linkToDoc && DocumentManager.Instance.DocumentViews.filter(dv => Doc.AreProtosEqual(dv.props.Document, linkToDoc)).forEach(dv => {
+ if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== view.props.LayoutTemplateString) {
+ this.LinkedDocumentViews.push({ a: dv, b: view, l: link });
+ }
+ });
+ });
+ this.DocumentViews.push(view);
+ }
+ public RemoveView = (view: DocumentView) => {
+ const index = this.DocumentViews.indexOf(view);
+ index !== -1 && this.DocumentViews.splice(index, 1);
+
+ this.LinkedDocumentViews.slice().forEach(action((pair, i) => pair.a === view || pair.b === view ? this.LinkedDocumentViews.splice(i, 1) : null));
+ }
+
//gets all views
public getDocumentViewsById(id: string) {
const toReturn: DocumentView[] = [];
@@ -105,25 +126,6 @@ export class DocumentManager {
return toReturn;
}
- @computed
- public get LinkedDocumentViews() {
- TraceMobx();
- const pairs = DocumentManager.Instance.DocumentViews.reduce((pairs, dv) => {
- const linksList = DocListCast(dv.props.Document.links);
- pairs.push(...linksList.reduce((pairs, link) => {
- const linkToDoc = link && LinkManager.getOppositeAnchor(link, dv.props.Document);
- linkToDoc && DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => {
- if (dv.props.Document.type !== DocumentType.LINK || dv.props.LayoutTemplateString !== docView1.props.LayoutTemplateString) {
- pairs.push({ a: dv, b: docView1, l: link });
- }
- });
- return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]));
- return pairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]);
-
- return pairs;
- }
static addRightSplit = (doc: Doc, finished?: () => void) => {
CollectionDockingView.AddSplit(doc, "right");
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 91ffab41d..0169b07fc 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -339,7 +339,6 @@ export namespace DragManager {
DragManager.Root().appendChild(dragDiv);
}
dragLabel.style.display = "";
- SnappingManager.SetIsDragging(true);
const scaleXs: number[] = [];
const scaleYs: number[] = [];
const xs: number[] = [];
@@ -410,6 +409,7 @@ export namespace DragManager {
const hideSource = options?.hideSource ? true : false;
eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = hideSource) : (ele.hidden = hideSource));
+ SnappingManager.SetIsDragging(true);
let lastX = downX;
let lastY = downY;
const xFromLeft = downX - elesCont.left;
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/LinkManager.ts b/src/client/util/LinkManager.ts
index 4f3cfcd03..694a7f3a7 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -63,7 +63,7 @@ export class LinkManager {
// finds all links that contain the given anchor
public getAllDirectLinks(anchor: Doc): Doc[] {
const related = LinkManager.Instance.getAllLinks().filter(link => link).filter(link => {
- const a1 = Cast(link.anchor1, Doc, null)
+ const a1 = Cast(link.anchor1, Doc, null);
const a2 = Cast(link.anchor2, Doc, null);
const protomatch1 = Doc.AreProtosEqual(anchor, a1);
const protomatch2 = Doc.AreProtosEqual(anchor, a2);
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index b756716bf..06458827a 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -59,6 +59,7 @@
margin-top: -17px;
margin-bottom: 10px;
font-size: 10px;
+ min-height: 40px;
input {
height: 10px;
@@ -71,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 a53ef3cde..4f2676728 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,13 +286,14 @@ 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-");
- return dropdownValues.map(permission =>
+ if (override) dropdownValues.unshift("unset");
+ return dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission =>
(
<option key={permission} value={permission}>
- {permission}
+ {permission === SharingPermissions.Add ? "Can Augment" : permission}
</option>
)
);
@@ -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,9 +580,13 @@ 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="layoutDoc-acls">
- <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
+ <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>
+ </div>}
</div>
}
<div className="main-container">
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 00754c416..462091f63 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -397,7 +397,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
</div>
</form>
{Doc.UserDoc().noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
- </div>
+ </div>;
}
@computed
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index c2c0d553b..9209cc21a 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";
@@ -323,10 +323,10 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
return <select className="permissions-select"
value={permission}
onChange={e => this.changePermissions(e, user)}>
- {dropdownValues.map(permission => {
+ {dropdownValues.filter(permission => permission !== SharingPermissions.View).map(permission => {
return (
<option key={permission} value={permission}>
- {permission}
+ {permission === SharingPermissions.Add ? "Can Augment" : permission}
</option>);
})}
</select>;
@@ -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}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 4c5a71ace..1ab104f6e 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -198,7 +198,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
initialize: (button: Doc) => {
- button['target-docFilters'] = Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters as any as ObjectField) : undefined;
+ button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ?
+ ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined;
button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
},
};
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d9c4d274a..a78923312 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -243,7 +243,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
e.stopPropagation();
return added;
}
- else if (de.complete.annoDragData) {
+ else if (de.complete.annoDragData && (!this.props.isAnnotationOverlay || de.complete.annoDragData.dragDocument === this.props.Document)) {
e.stopPropagation();
return this.addDocument(de.complete.annoDragData.dropDocument);
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e35474b81..522f46280 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -144,7 +144,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
added.forEach(d => {
for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true);
- else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true);
+ //else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true);
//else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true);
}
});
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 79a540210..011e10b88 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -122,9 +122,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
}
- @computed get renderData() {
- this._start;
- if (SnappingManager.GetIsDragging() || !this.props.A.ContentDiv || !this.props.B.ContentDiv || !this.props.LinkDocs.length) {
+ @computed.struct get renderData() {
+ this._start; SnappingManager.GetIsDragging();
+ if (!this.props.A.ContentDiv || !this.props.B.ContentDiv || !this.props.LinkDocs.length) {
return undefined;
}
this.props.A.props.ScreenToLocalTransform().transform(this.props.B.props.ScreenToLocalTransform());
@@ -132,6 +132,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const bcont = this.props.B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv);
const bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv);
+ for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
+ for (let apdiv = bdiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
const a = adiv.getBoundingClientRect();
const b = bdiv.getBoundingClientRect();
const atop = this.visibleY(adiv);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 1a2421bfd..6a1a41ca7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,4 +1,4 @@
-import { computed } from "mobx";
+import { computed, trace } from "mobx";
import { observer } from "mobx-react";
import { Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
@@ -10,12 +10,10 @@ import React = require("react");
import { Utils, emptyFunction } from "../../../../Utils";
import { DocumentType } from "../../../documents/DocumentTypes";
import { SnappingManager } from "../../../util/SnappingManager";
-import { Cast } from "../../../../fields/Types";
@observer
export class CollectionFreeFormLinksView extends React.Component {
- @computed
- get uniqueConnections() {
+ @computed get uniqueConnections() {
const connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => {
if (!drawnPairs.reduce((found, drawnPair) => {
const match1 = (connection.a === drawnPair.a && connection.b === drawnPair.b);
@@ -37,7 +35,7 @@ export class CollectionFreeFormLinksView extends React.Component {
}
render() {
- return SnappingManager.GetIsDragging() ? (null) : <div className="collectionfreeformlinksview-container">
+ return <div className="collectionfreeformlinksview-container">
<svg className="collectionfreeformlinksview-svgCanvas">
{this.uniqueConnections}
</svg>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3d3599476..32c6fce36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -208,7 +208,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this)));
if (!BoolCast(this.rootDoc.dontRegisterView, this.props.dontRegisterView)) {
- DocumentManager.Instance.DocumentViews.push(this);
+ DocumentManager.Instance.AddView(this);
}
}
@@ -234,8 +234,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._holdDisposer?.();
Doc.UnBrushDoc(this.props.Document);
if (!this.props.dontRegisterView) {
- const index = DocumentManager.Instance.DocumentViews.indexOf(this);
- index !== -1 && DocumentManager.Instance.DocumentViews.splice(index, 1);
+ DocumentManager.Instance.RemoveView(this);
}
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 1e6477f43..26902914a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -557,11 +557,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
sidebarDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction,
- () => (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%"));
+ () => this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "25%" : "0%")) !== "0%");
}
sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
const bounds = this.CurrentDiv.getBoundingClientRect();
this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
+ this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
return false;
}
@undoBatch
@@ -1518,6 +1519,32 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
}
+ return1000 = () => 1000;
+ @computed get sidebarCollection() {
+ return !this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
+ <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ PanelHeight={this.active() ? this.return1000 : this.props.PanelHeight}
+ PanelWidth={this.sidebarWidth}
+ scaleField={this.annotationKey + "-scale"}
+ annotationsKey={this.annotationKey}
+ isAnnotationOverlay={true}
+ focus={this.props.focus}
+ isSelected={this.props.isSelected}
+ select={emptyFunction}
+ active={this.annotationsActive}
+ ContentScaling={returnOne}
+ whenActiveChanged={this.whenActiveChanged}
+ removeDocument={this.removeDocument}
+ moveDocument={this.moveDocument}
+ addDocument={this.addDocument}
+ CollectionView={undefined}
+ ScreenToLocalTransform={this.sidebarScreenToLocal}
+ renderDepth={this.props.renderDepth + 1}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
+ </div>;
+ }
+
@computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
@@ -1587,28 +1614,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}}
/>
</div>
- {!this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) :
- <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- PanelHeight={active ? () => 1000 : this.props.PanelHeight}
- PanelWidth={this.sidebarWidth}
- scaleField={this.annotationKey + "-scale"}
- annotationsKey={this.annotationKey}
- isAnnotationOverlay={true}
- focus={this.props.focus}
- isSelected={this.props.isSelected}
- select={emptyFunction}
- active={this.annotationsActive}
- ContentScaling={returnOne}
- whenActiveChanged={this.whenActiveChanged}
- removeDocument={this.removeDocument}
- moveDocument={this.moveDocument}
- addDocument={this.addDocument}
- CollectionView={undefined}
- ScreenToLocalTransform={this.sidebarScreenToLocal}
- renderDepth={this.props.renderDepth + 1}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc} />
- </div>}
+ {this.sidebarCollection}
{selected ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)}
{!this.layoutDoc._showAudio ? (null) :
<div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} >
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index a6ac62ee4..b381bbfa9 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -183,7 +183,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
let newarray: Doc[] = [];
while (docs.length > 0) {
newarray = [];
- docs.forEach(d => {
+ docs.filter(d => d).forEach(d => {
const fieldKey = Doc.LayoutFieldKey(d);
const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
const data = d[annos ? fieldKey + "-annotations" : fieldKey];
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 470f9d4be..205831153 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -103,6 +103,7 @@ export const DataSym = Symbol("Data");
export const LayoutSym = Symbol("Layout");
export const FieldsSym = Symbol("Fields");
export const AclSym = Symbol("Acl");
+export const AclUnset = Symbol("AclUnset");
export const AclPrivate = Symbol("AclOwnerOnly");
export const AclReadonly = Symbol("AclReadOnly");
export const AclAddonly = Symbol("AclAddonly");
@@ -112,6 +113,7 @@ export const UpdatingFromServer = Symbol("UpdatingFromServer");
export const CachedUpdates = Symbol("Cached updates");
const AclMap = new Map<string, symbol>([
+ ["unset", AclUnset],
[SharingPermissions.None, AclPrivate],
[SharingPermissions.View, AclReadonly],
[SharingPermissions.Add, AclAddonly],
@@ -332,7 +334,7 @@ export namespace Doc {
export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult {
try {
return getField(doc[Self], key, ignoreProto);
- } catch {
+ } catch {
return doc;
}
}
diff --git a/src/fields/util.ts b/src/fields/util.ts
index fe3eea69d..fa1c47055 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,5 +1,5 @@
import { UndoManager } from "../client/util/UndoManager";
-import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto } from "./Doc";
+import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAddonly, AclSym, CachedUpdates, DataSym, DocListCast, AclAdmin, FieldsSym, HeightSym, WidthSym, fetchProto, AclUnset } from "./Doc";
import { SerializationHelper } from "../client/util/SerializationHelper";
import { ProxyField, PrefetchProxy } from "./Proxy";
import { RefField } from "./RefField";
@@ -167,11 +167,17 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number,
if (userChecked === (target.__fields?.author || target.author)) return AclAdmin;
if (currentUserGroups.includes("Admin")) return AclAdmin;
+
if (target[AclSym] && Object.keys(target[AclSym]).length) {
// if the acl is being overriden or the property being modified is one of the playground fields (which can be freely modified)
if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit;
+ // if there's an overriding acl set through the properties panel or sharing menu, that's what's returned.
+ // if it's unset, it just goes ahead
+ const override = target[AclSym]["acl-Override"];
+ if (override !== AclUnset && override !== undefined) return target[AclSym]["acl-Override"];
+
let effectiveAcl = AclPrivate;
const HierarchyMapping = new Map<symbol, number>([
[AclPrivate, 0],
@@ -223,14 +229,17 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
const dataDoc = target[DataSym];
// 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
- if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) {
+ if (GetEffectiveAcl(target) === AclAdmin && (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!)) {
target[key] = acl;
layoutDocChanged = true;
}
if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
- dataDoc[key] = acl;
- dataDocChanged = true;
+
+ if (GetEffectiveAcl(dataDoc) === AclAdmin) {
+ dataDoc[key] = acl;
+ dataDocChanged = true;
+ }
// maps over the aliases of the document
const links = DocListCast(dataDoc.links);
@@ -238,22 +247,22 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
// maps over the children of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => {
- if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
- distributeAcls(key, acl, d, inheritingFromCollection, visited);
- }
+ // if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
+ distributeAcls(key, acl, d, inheritingFromCollection, visited);
+ // }
const data = d[DataSym];
- if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
+ if (data) {// && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, data, inheritingFromCollection, visited);
}
});
// maps over the annotations of the document
DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => {
- if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
- distributeAcls(key, acl, d, inheritingFromCollection, visited);
- }
+ // if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) {
+ distributeAcls(key, acl, d, inheritingFromCollection, visited);
+ // }
const data = d[DataSym];
- if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
+ if (data) {// && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) {
distributeAcls(key, acl, data, inheritingFromCollection, visited);
}
});
@@ -271,7 +280,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
if (effectiveAcl !== AclEdit && 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;
+ if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "unset"].includes(value))) return true;
// if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Add", "Can View", "Not Shared", undefined].includes(value)) return true;
if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) {