aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/SharingManager.scss8
-rw-r--r--src/client/util/SharingManager.tsx126
-rw-r--r--src/client/views/DocumentDecorations.tsx60
-rw-r--r--src/client/views/PropertiesView.scss7
-rw-r--r--src/client/views/PropertiesView.tsx124
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx18
-rw-r--r--src/fields/Doc.ts7
-rw-r--r--src/fields/util.ts47
9 files changed, 292 insertions, 107 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 3e89c8347..f5510d7e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -12,7 +12,7 @@ import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
import { ComputedField, ScriptField } from '../../fields/ScriptField';
import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
-import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util';
import { Upload } from '../../server/SharedMediaTypes';
import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
import { YoutubeBox } from '../apis/youtube/YoutubeBox';
diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss
index 932e94664..f47c48805 100644
--- a/src/client/util/SharingManager.scss
+++ b/src/client/util/SharingManager.scss
@@ -6,7 +6,7 @@
transform: translate(-20px, -20px);
}
- select {
+ .select {
text-align: justify;
text-align-last: end
}
@@ -212,9 +212,9 @@
cursor: pointer;
}
- &:hover .padding {
- white-space: unset;
- }
+ // &:hover .padding {
+ // white-space: unset;
+ // }
.padding {
padding: 0 10px 0 0;
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 3c05af4bb..5e61f6d3c 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -12,6 +12,7 @@ import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types';
import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
import { Utils } from '../../Utils';
import { DocServer } from '../DocServer';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
import { CollectionView } from '../views/collections/CollectionView';
import { DictationOverlay } from '../views/DictationOverlay';
import { MainViewModal } from '../views/MainViewModal';
@@ -157,21 +158,42 @@ export class SharingManager extends React.Component<{}> {
const acl = `acl-${normalizeEmail(user.email)}`;
const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
+
+ // var docs: Doc[] = [];
+ // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+ // const dashboard = dashboardList[0]
+ // docs.push(dashboard!)
+ // const tabs = DocListCast(dashboard?.data)
+ // tabs.forEach(tab => {
+ // docs.push(tab)
+ // var newDocs = DocListCast(tab.data)
+ // newDocs.forEach(newDoc =>
+ // docs.push(newDoc))
+ // })
+
+ // if (!docs){
+ // return null;
+ // }
const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
+
+ //var docs: Doc[] = [];
+ docs.push(targetDoc!)
+
+ // ! ensures it returns true if document has been shared successfully, false otherwise
return !docs
.map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
.map(doc => {
doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
+
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;
}
+ doc = targetDoc
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);
else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
@@ -190,8 +212,18 @@ export class SharingManager extends React.Component<{}> {
const acl = `acl-${key}`;
const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
- const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-
+ var docs: Doc[] = [];
+ const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+ const dashboard = dashboardList[0]
+ const tabs = DocListCast(dashboard?.data)
+ tabs.forEach(tab => {
+ var newDocs = DocListCast(tab.data)
+ newDocs.forEach(newDoc => docs.push(newDoc))
+ })
+
+ if (!docs){
+ return null;
+ }
// ! ensures it returns true if document has been shared successfully, false otherwise
return !docs
.map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
@@ -334,13 +366,20 @@ export class SharingManager extends React.Component<{}> {
// targetDoc["acl-" + PublicKey] = permission;
// }
- // private get sharingUrl() {
- // if (!this.targetDoc) {
- // return undefined;
- // }
- // const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
- // return `${baseUrl}?sharing=true`;
- // }
+ private get sharingUrl() {
+ if (!this.targetDoc) {
+ return undefined;
+ }
+ const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]);
+ return `${baseUrl}?sharing=true`;
+ }
+
+ /**
+ * Copies the Public sharing url to the user's clipboard.
+ */
+ private copyURL = (e: any) => {
+ Utils.CopyText(this.sharingUrl!)
+ }
// copy = action(() => {
// if (this.sharingUrl) {
@@ -357,7 +396,7 @@ export class SharingManager extends React.Component<{}> {
if (!uniform) dropdownValues.unshift('-multiple-');
if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
return dropdownValues
- .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any))
+ .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)))
.map(permission => (
<option key={permission} value={permission}>
{permission}
@@ -422,6 +461,7 @@ export class SharingManager extends React.Component<{}> {
this.selectedUsers.forEach(user => {
if (user.value.includes(indType)) {
this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
+
} else {
this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
}
@@ -466,6 +506,7 @@ export class SharingManager extends React.Component<{}> {
if (!this.targetDoc) return null;
TraceMobx();
const groupList = GroupManager.Instance?.allGroups || [];
+
const sortedUsers = this.users
.slice()
.sort(this.sortUsers)
@@ -505,13 +546,48 @@ export class SharingManager extends React.Component<{}> {
// users in common between all docs
const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).map(doc => doc?.[AclSym] && Object.keys(doc[AclSym])));
+ // including all users on dashboard
+ // SharingManager.Instance.users.forEach(eachUser => {
+ // var userOnDashboard = true;
+ // if(Doc.ActiveDashboard){
+ // if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+ // userOnDashboard = false;
+ // }
+ // }
+ // if (userOnDashboard && !commonKeys.includes(`acl-${normalizeEmail(eachUser.user.email)}`)){
+ // users.push(eachUser.user);
+ // }
+ // });
+
+
// the list of users shared with
const userListContents: (JSX.Element | null)[] = users
- .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+ // .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 dashboardList = SelectionManager.Views().length < 2 ? [targetDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+ // const dashboard = dashboardList[0]
+ const dashboard = Doc.ActiveDashboard
+ var docToUse = dashboard
+
+ // const tabs = DocListCast(dashboard?.data)
+ // tabs.forEach(tab => {
+ // if (tab.title == targetDoc.title){
+ // docToUse = tab
+ // }
+ // var newdocsList = DocListCast(tab.data)
+ // newdocsList.forEach(newDoc => {
+ // if (newDoc.title == targetDoc.title){
+ // docToUse = newDoc
+ // }
+ // })
+ // })
+
+ docToUse = Doc.GetProto(this.targetDoc!)
+
const userKey = `acl-${normalizeEmail(user.email)}`;
const uniform = docs.map(doc => (this.layoutDocAcls ? doc : doc[DataSym])).every(doc => doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey]);
- const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+ const permissions = uniform ? StrCast(docToUse?.[userKey]) : '-multiple-';
return !permissions ? null : (
<div key={userKey} className={'container'}>
@@ -551,7 +627,7 @@ export class SharingManager extends React.Component<{}> {
</div>
) : null
);
-
+
// the list of groups shared with
const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
@@ -560,11 +636,27 @@ export class SharingManager extends React.Component<{}> {
const uniform = docs
.map(doc => (this.layoutDocAcls ? doc : doc[DataSym]))
.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
- const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+ const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-';
return !permissions ? null : (
<div key={groupKey} className={'container'}>
- <div className={'padding'}>{StrCast(group.title)}</div>
+ <div className={'padding'} >{StrCast(group.title)}</div>
+ <div>
+ {StrCast(group.title)==='Public' ? (
+ <div title={"Copy Public URL"} onClick={this.copyURL}>
+ {/* <IconButton
+ size={Size.SMALL}
+ isCircle={true}
+ hoverStyle="gray"
+ onClick={() => this.copyURL}
+ icon={<FontAwesomeIcon icon="copy" />}
+ /> */}
+ <FontAwesomeIcon icon={'copy'} size={'sm'} />
+ </div>
+ ) : (
+ <div/>
+ )}
+ </div>
{group instanceof Doc ? (
<div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
<FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b2b18b245..3e0a1f7a4 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -6,12 +6,12 @@ import { action, computed, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { FaUndo } from 'react-icons/fa';
import { DateField } from '../../fields/DateField';
-import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
import { Document } from '../../fields/documentSchemas';
import { InkField } from '../../fields/InkField';
import { ScriptField } from '../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../fields/Types';
-import { GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util';
import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils';
import { Docs } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
@@ -157,27 +157,35 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
};
@action onContainerDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(
- this,
- e,
- e => this.onBackgroundMove(true, e),
- e => {},
- emptyFunction
- );
+ const first = SelectionManager.Views()[0];
+ const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+ if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+ setupMoveUpEvents(
+ this,
+ e,
+ e => this.onBackgroundMove(true, e),
+ e => {},
+ emptyFunction
+ );
+ }
};
@action onTitleDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(
- this,
- e,
- e => this.onBackgroundMove(true, e),
- e => {},
- action(e => {
- !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
- this._editingTitle = true;
- this._keyinput.current && setTimeout(this._keyinput.current.focus);
- })
- );
+ const first = SelectionManager.Views()[0];
+ const effectiveAcl = GetEffectiveAcl(first.rootDoc)
+ if (effectiveAcl==AclAdmin || effectiveAcl==AclEdit || effectiveAcl==AclAugment){
+ setupMoveUpEvents(
+ this,
+ e,
+ e => this.onBackgroundMove(true, e),
+ e => {},
+ action(e => {
+ !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('#') ? this.selectionTitle : this._titleControlString);
+ this._editingTitle = true;
+ this._keyinput.current && setTimeout(this._keyinput.current.focus);
+ })
+ );
+ }
};
onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
@@ -485,6 +493,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
const first = SelectionManager.Views()[0];
+ 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 };
var fixedAspect = Doc.NativeAspect(first.layoutDoc);
@@ -737,7 +747,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
// sharing
- const docShareMode = seldocview.rootDoc["acl-Public"];
+ const docShareMode = Doc.GetProto(seldocview.rootDoc)["acl-Public"];
const shareMode = StrCast(docShareMode);
var shareSymbolIcon = null;
switch(shareMode){
@@ -781,10 +791,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
seldocview.props.hideDeleteButton ||
seldocview.rootDoc.hideDeleteButton ||
SelectionManager.Views().some(docView => {
- const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
- return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+ // const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+ // return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+ const effectiveAcl = GetEffectiveAcl(Doc.GetProto(seldocview.rootDoc))
+ return docView.rootDoc.stayInCollection || (effectiveAcl !== AclAdmin && effectiveAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
});
-
const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
<Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
<div
@@ -852,7 +863,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
<div className={'documentDecorations-share'}>
<div className={`documentDecorations-share${shareMode}`}>
<span>{shareSymbolIcon + " " + shareMode}</span>
- {/* <span>{shareMode}</span> */}
</div>
</div> ) : (<div/>
);
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index 3edc7fea8..50bf6ce74 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -384,17 +384,14 @@
padding: 5px; // remove when adding buttons
border-radius: 6px; // remove when adding buttons
margin-right: 10px; // remove when adding buttons
- // width: 100%;
- // display: inline-table;
background-color: #ececec;
max-height: 130px;
width: 92%;
.propertiesView-sharingTable-item {
display: flex;
- // padding: 5px;
padding: 3px;
- align-items: center;
+ align-items: right;
border-bottom: 0.5px solid grey;
&:hover .propertiesView-sharingTable-item-name {
@@ -429,7 +426,7 @@
background: inherit;
border: none;
background: inherit;
- width: 87px;
+ // width: 100%;
text-align: justify; // for Edge
text-align-last: end;
&:hover {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index dfc43e6c8..a6a5347ad 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -13,7 +13,7 @@ import { InkField } from '../../fields/InkField';
import { List } from '../../fields/List';
import { ComputedField } from '../../fields/ScriptField';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
-import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { denormalizeEmail, GetEffectiveAcl, normalizeEmail, SharingPermissions } from '../../fields/util';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
import { DocumentType } from '../documents/DocumentTypes';
import { DocumentManager } from '../util/DocumentManager';
@@ -34,6 +34,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector
import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
import './PropertiesView.scss';
import { DefaultStyleProvider } from './StyleProvider';
+import { SourceMapDevToolPlugin } from 'webpack';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
@@ -366,11 +367,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@undoBatch
changePermissions = (e: any, user: string) => {
+ const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
if (user=="Public"){
- this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions;
+ docs[0]['acl-' +user] = e.currentTarget.value as SharingPermissions;
}
else{
- const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym]));
+
SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
}
};
@@ -381,15 +383,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
getPermissionsSelect(user: string, permission: string) {
const dropdownValues: string[] = Object.values(SharingPermissions);
if (permission === '-multiple-') dropdownValues.unshift(permission);
- if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+ if (user !== 'Override') {
+ dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+ // dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1);
+ }
return (
<select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
{dropdownValues
- .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+ .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)))
.map(permission => (
<option className="propertiesView-permisssions-select" key={permission} value={permission}>
- {' '}
- {permission}{' '}
+ {' '}{permission}{' '}
</option>
))}
</select>
@@ -432,6 +436,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
* @returns a row of the permissions panel
*/
sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
+ if (name==Doc.CurrentUserEmail){
+ name = 'Me';
+ }
return (
<div
className="propertiesView-sharingTable-item"
@@ -445,7 +452,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' || showExpansionIcon ? this.expansionIcon : null}
+ {(permission === 'Owner' && name=='Me')|| showExpansionIcon ? this.expansionIcon : null}
</div>
</div>
);
@@ -470,6 +477,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
dropDownText = "▲ ";
break;
}
+
return (
<div>
<div className={'propertiesView-shareDropDown'}>
@@ -490,7 +498,24 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
*/
@computed get sharingTable() {
// const docToUse = this.selectedDocumentView?.rootDoc;
+
+ // const dashboard = Doc.ActiveDashboard
+ // var docToUse = dashboard
+ // const tabs = DocListCast(dashboard?.data)
+ // tabs.forEach(tab => {
+ // if (tab.title == this.selectedDoc?.title){
+ // docToUse = tab
+ // }
+ // var newdocsList = DocListCast(tab.data)
+ // newdocsList.forEach(newDoc => {
+ // if (newDoc.title == this.selectedDoc?.title){
+ // docToUse = newDoc
+ // }
+ // })
+ // })
+
const docToUse = this.selectedDoc;
+
if (!docToUse){
return null;
}
@@ -501,44 +526,69 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
const target = docs[0];
// tslint:disable-next-line: no-unnecessary-callback-wrapper
- const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
- const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+ // const effectiveAcls = docs.map(doc => GetEffectiveAcl(doc));
+ // const effectiveAcls = GetEffectiveAcl(docToUse)
+ const effectiveAcls = GetEffectiveAcl(target)
+
+ // const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
+ const showAdmin= effectiveAcls==AclAdmin || docToUse!['acl-'+normalizeEmail(Doc.CurrentUserEmail)]=='Owner';
// users in common between all docs
const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me')));
const tableEntries = [];
+ const usersAdded: string[] = []; // all shared users being added - organized by denormalized email
+ const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
+
+ usersAdded.push(target.author);
+ SharingManager.Instance.users.forEach(eachUser => {
+ var userOnDashboard = true;
+ var permission = StrCast(target[`acl-${normalizeEmail(eachUser.user.email)}`])
+ if(Doc.ActiveDashboard){
+ if(Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(eachUser.user.email)]==undefined){
+ userOnDashboard = false;
+ }
+ }
+ if (userOnDashboard && !usersAdded.includes(eachUser.user.email) && eachUser.user.email!='Public'){
+ tableEntries.unshift(this.sharingItem(eachUser.user.email, showAdmin, permission, false)); // adds each user
+ usersAdded.push(eachUser.user.email);
+ }
+ });
- // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => {
- // if (commonKeys.length) {
- // for (const key of commonKeys) {
- // const name = denormalizeEmail(key.substring(4));
- // const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]);
- // if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
- // tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-'));
+ // commonKeys.forEach(user => {
+ // const userEmail = user.slice(4)
+ // var userOnDashboard = true;
+ // if(Doc.ActiveDashboard){
+ // if(Doc.ActiveDashboard[user]=='' || Doc.ActiveDashboard[user]==undefined){
+ // userOnDashboard = false;
// }
// }
- // }
- 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"]) || "None") : "-multiple-"));
- if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
- // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['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]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-',
- // !ownerSame
- // )
- // );
-
- SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false)));
-
- docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true)));
+ // if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
+ // tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(docToUse![`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+ // usersAdded.push(denormalizeEmail(userEmail));
+ // }
+ // })
+
+ // current user
+ const userEmail = Doc.CurrentUserEmail
+ var userOnDashboard = true;
+ if(Doc.ActiveDashboard){
+ if(Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]=='' || Doc.ActiveDashboard['acl-'+normalizeEmail(userEmail)]==undefined){
+ userOnDashboard = false;
+ }
+ }
+ if (userOnDashboard && !usersAdded.includes(denormalizeEmail(userEmail)) && userEmail!='Public'){
+ tableEntries.unshift(this.sharingItem(denormalizeEmail(userEmail), showAdmin, StrCast(target[`acl-${normalizeEmail((userEmail))}`]), false)); // adds each user
+ usersAdded.push(denormalizeEmail(userEmail));
+ }
+
+ // if (ownerSame ) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
+ tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'), false); // shift owner to top
return (
- <div> Sharing Mode
- <div>{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}</div>
+ <div > Sharing Mode
+ <div>{
+ this.publicACLDropDown(true, StrCast(target['acl-Public']), false)}
+ </div>
<div> <br></br> Who has access to the Dashboard? </div>
<div className="propertiesView-sharingTable">{
<div> {tableEntries}</div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a2c1195cb..0e4330fa5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -19,7 +19,7 @@ import { RichTextField } from '../../../../fields/RichTextField';
import { RichTextUtils } from '../../../../fields/RichTextUtils';
import { ComputedField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
+import { GetEffectiveAcl, normalizeEmail, TraceMobx } from '../../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
import { DocServer } from '../../../DocServer';
@@ -295,11 +295,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
const json = JSON.stringify(state.toJSON());
- const effectiveAcl = GetEffectiveAcl(dataDoc);
-
+ // const effectiveAcl = GetEffectiveAcl(dataDoc);
+ const effectiveAcl = GetEffectiveAcl(this.rootDoc);
+
const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
-
- if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
+
+ if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
const accumTags = [] as string[];
state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -1689,7 +1690,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
e.stopPropagation();
for (var i = state.selection.from; i <= state.selection.to; i++) {
const node = state.doc.resolve(i);
- if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
+ if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail) && [AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
e.preventDefault();
}
}
@@ -1708,7 +1709,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
default:
if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
case ' ':
- [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) &&
+ if (e.code == "Space"){
+ break;
+ }
+ [AclEdit, AclAugment, AclSelfEdit, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
}
this.startUndoTypingBatch();
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 4d82551db..5e2e40979 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -135,7 +135,6 @@ export const ReverseHierarchyMap: Map<string, { level: aclLevel; acl: symbol }>
// this recursively updates all protos as well.
export function updateCachedAcls(doc: Doc) {
if (!doc) return;
-
const target = (doc as any)?.__fields ?? doc;
const permissions: { [key: string]: symbol } = !target.author || target.author === Doc.CurrentUserEmail ? { 'acl-Me': AclAdmin } : {};
Object.keys(target).filter(key => key.startsWith('acl') && (permissions[key] = ReverseHierarchyMap.get(StrCast(target[key]))!.acl));
@@ -1117,7 +1116,7 @@ export namespace Doc {
target[targetKey] = new PrefetchProxy(templateDoc);
} else {
titleTarget && (Doc.GetProto(target).title = titleTarget);
- const setDoc = [AclAdmin, AclEdit].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
+ const setDoc = [AclAdmin, AclEdit, AclAugment].includes(GetEffectiveAcl(Doc.GetProto(target))) ? Doc.GetProto(target) : target;
setDoc[targetKey] = new PrefetchProxy(templateDoc);
}
}
@@ -1295,7 +1294,9 @@ export namespace Doc {
}
// don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
export function IsBrushedDegreeUnmemoized(doc: Doc) {
- if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
+ if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) {
+ return DocBrushStatus.unbrushed;
+ }
const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
if (status === DocBrushStatus.unbrushed) {
const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index eca4d1351..c1e1a7111 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -9,9 +9,11 @@ import { returnZero } from '../Utils';
import CursorField from './CursorField';
import {
AclAdmin,
+ AclAugment,
AclEdit,
aclLevel,
AclPrivate,
+ AclReadonly,
AclSelfEdit,
AclSym,
DataSym,
@@ -37,6 +39,8 @@ import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
import { ComputedField } from './ScriptField';
import { ScriptCast, StrCast } from './Types';
+import { SharingManager } from '../client/util/SharingManager';
+import { PropertiesView } from '../client/views/PropertiesView';
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -82,8 +86,8 @@ 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 === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
- const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
+ const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
+ const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAugment || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
if (writeToDoc) {
if (value === undefined) {
@@ -139,13 +143,13 @@ export function denormalizeEmail(email: string) {
* Copies parent's acl fields to the child
*/
export function inheritParentAcls(parent: Doc, child: Doc) {
- return;
const dataDoc = parent[DataSym];
for (const key of Object.keys(dataDoc)) {
// if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
key.startsWith('acl') && distributeAcls(key, permission, child);
}
+ return;
}
/**
@@ -182,6 +186,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
* Calculates the effective access right to a document for the current user.
*/
export function GetEffectiveAcl(target: any, user?: string): symbol {
+ target = Doc.GetProto(target)
if (!target) return AclPrivate;
if (target[UpdatingFromServer]) return AclAdmin;
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)
@@ -205,8 +210,24 @@ export function SetCachedGroups(groups: string[]) {
}
function getEffectiveAcl(target: any, user?: string): symbol {
const targetAcls = target[AclSym];
- if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName('Admin')) return AclAdmin;
-
+ if (targetAcls?.['acl-Me'] === AclAdmin || GetCachedGroupByName(SharingPermissions.Admin)) return AclAdmin;
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]){
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Admin){
+ return AclAdmin
+ }
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Edit){
+ return AclEdit
+ }
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.Augment){
+ return AclAugment
+ }
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.View){
+ return AclReadonly
+ }
+ if (target['acl-'+normalizeEmail(Doc.CurrentUserEmail)] == SharingPermissions.None){
+ return AclPrivate
+ }
+ }
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) {
let effectiveAcl = AclPrivate;
@@ -225,13 +246,22 @@ function getEffectiveAcl(target: any, user?: string): symbol {
//const override = targetAcls['acl-Override'];
// if (override !== AclUnset && override !== undefined) effectiveAcl = override;
- // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's 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.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
if (targetAuthor && targetAuthor !== userChecked) return AclPrivate;
return AclAdmin;
+ let acl = AclPrivate
+ if (user){
+ acl = target['acl-'+user]
+ }
+ else{
+ acl = target['acl-'+normalizeEmail(Doc.CurrentUserEmail)]
+ }
+ console.log(target['acl-'+normalizeEmail(Doc.CurrentUserEmail)])
+ return DocServer?.Control?.isReadOnly?.() && HierarchyMapping.get(acl)!.level < aclLevel.editable ? AclEdit : acl;
}
/**
* Recursively distributes the access right for a user across the children of a document and its annotations.
@@ -247,9 +277,11 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
if ((target._viewType === CollectionViewType.Docking && visited.length > 1) || Doc.GetProto(visited[0]) !== Doc.GetProto(target)) {
target[key] = acl;
+ Doc.GetProto(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;
}
@@ -260,7 +292,6 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
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));
@@ -299,7 +330,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);
- if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
+ if (effectiveAcl !== AclEdit && effectiveAcl !== AclAugment && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) 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;