From 2d0741800ed626ac3db2a8ac551904cbc0f7848e Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Tue, 21 Jul 2020 09:38:29 -0400
Subject: fixed up autoscrollin a bit.  scrolls the correct collection and
 waits 250ms to start scrolling.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 41 ++++++----------------
 1 file changed, 10 insertions(+), 31 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 01b0c81d8..412f91417 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -85,8 +85,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _lastY: number = 0;
     private _downX: number = 0;
     private _downY: number = 0;
-    private _lastClientY: number | undefined = 0;
-    private _lastClientX: number | undefined = 0;
     private _inkToTextStartX: number | undefined;
     private _inkToTextStartY: number | undefined;
     private _wordPalette: Map<string, string> = new Map<string, string>();
@@ -582,7 +580,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     @action
     onPointerUp = (e: PointerEvent): void => {
-        this._lastClientY = this._lastClientX = undefined;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return;
 
         document.removeEventListener("pointermove", this.onPointerMove);
@@ -1152,16 +1149,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             (elements) => this._layoutElements = elements || [],
             { fireImmediately: true, name: "doLayout" });
 
-        const handler = (e: any) => this.handleDragging(e, (e as CustomEvent<DragEvent>).detail);
-
-        document.addEventListener("dashDragging", handler);
+        this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
     }
 
     componentWillUnmount() {
         this._layoutComputeReaction?.();
-
-        const handler = (e: any) => this.handleDragging(e, (e as CustomEvent<DragEvent>).detail);
-        document.removeEventListener("dashDragging", handler);
+        this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
     }
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
@@ -1176,39 +1169,25 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     // <div ref={this._marqueeRef}>
 
     @action
-    handleDragging = (e: CustomEvent<React.DragEvent>, de: DragEvent) => {
-        if ((e as any).handlePan) return;
+    onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
+        if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
         (e as any).handlePan = true;
-        this._lastClientY = e.detail.clientY;
-        this._lastClientX = e.detail.clientX;
 
         if (this._marqueeRef?.current) {
             const dragX = e.detail.clientX;
             const dragY = e.detail.clientY;
             const bounds = this._marqueeRef.current?.getBoundingClientRect();
 
-            const deltaX = dragX - bounds.left < 25 ? -2 : bounds.right - dragX < 25 ? 2 : 0;
-            const deltaY = dragY - bounds.top < 25 ? -2 : bounds.bottom - dragY < 25 ? 2 : 0;
-            (deltaX !== 0 || deltaY !== 0) && this.continuePan(deltaX, deltaY);
+            const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0;
+            const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0;
+            if (deltaX !== 0 || deltaY !== 0) {
+                this.Document._panY = NumCast(this.Document._panY) + deltaY / 2;
+                this.Document._panX = NumCast(this.Document._panX) + deltaX / 2;
+            }
         }
         e.stopPropagation();
     }
 
-    continuePan = (deltaX: number, deltaY: number) => {
-        setTimeout(action(() => {
-            const dragY = this._lastClientY;
-            const dragX = this._lastClientX;
-            if (dragY !== undefined && dragX !== undefined && this._marqueeRef.current) {
-                const bounds = this._marqueeRef.current.getBoundingClientRect();
-                this.Document._panY = NumCast(this.Document._panY) + deltaY;
-                this.Document._panX = NumCast(this.Document._panX) + deltaX;
-                if (dragY - bounds.top < 25 || bounds.bottom - dragY < 25 || dragX - bounds.left < 25 || bounds.right - dragX < 25) {
-                    this.continuePan(deltaX, deltaY);
-                }
-            } else this._lastClientY !== undefined && this._lastClientX !== undefined && this.continuePan(deltaX, deltaY);
-        }), 50);
-    }
-
     promoteCollection = undoBatch(action(() => {
         const childDocs = this.childDocs.slice();
         childDocs.forEach(doc => {
-- 
cgit v1.2.3-70-g09d2


From 8f95d52ed2b93930716ab6ed0ba9aab5c438876c Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 22 Jul 2020 20:16:53 -0400
Subject: fixed document download/import to capture links as well.

---
 package-lock.json                                  |  56 ++++++++++++
 package.json                                       |   3 +
 src/client/views/GlobalKeyHandler.ts               |   2 +-
 src/client/views/PreviewCursor.tsx                 |   2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   7 ++
 src/client/views/nodes/DocHolderBox.tsx            |   2 +-
 src/client/views/nodes/DocumentView.tsx            |  12 +--
 src/fields/Doc.ts                                  | 101 ++++++++++++++++-----
 src/server/ApiManagers/UploadManager.ts            |   2 +
 9 files changed, 155 insertions(+), 32 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/package-lock.json b/package-lock.json
index 1b39905cf..698bd60cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -711,6 +711,12 @@
                 "express-validator": "*"
             }
         },
+        "@types/file-saver": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.1.tgz",
+            "integrity": "sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==",
+            "dev": true
+        },
         "@types/formidable": {
             "version": "1.0.31",
             "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
@@ -6069,6 +6075,11 @@
                 }
             }
         },
+        "file-saver": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
+            "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
+        },
         "file-uri-to-path": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -7280,6 +7291,11 @@
                 }
             }
         },
+        "immediate": {
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+            "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+        },
         "import-fresh": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@@ -8160,6 +8176,33 @@
                 "promise": "^7.0.1"
             }
         },
+        "jszip": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
+            "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
+            "requires": {
+                "lie": "~3.3.0",
+                "pako": "~1.0.2",
+                "readable-stream": "~2.3.6",
+                "set-immediate-shim": "~1.0.1"
+            },
+            "dependencies": {
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "core-util-is": "~1.0.0",
+                        "inherits": "~2.0.3",
+                        "isarray": "~1.0.0",
+                        "process-nextick-args": "~2.0.0",
+                        "safe-buffer": "~5.1.1",
+                        "string_decoder": "~1.1.1",
+                        "util-deprecate": "~1.0.1"
+                    }
+                }
+            }
+        },
         "jwa": {
             "version": "1.4.1",
             "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
@@ -8300,6 +8343,14 @@
                 }
             }
         },
+        "lie": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+            "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+            "requires": {
+                "immediate": "~3.0.5"
+            }
+        },
         "lines-and-columns": {
             "version": "1.1.6",
             "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
@@ -15142,6 +15193,11 @@
             "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
             "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
         },
+        "set-immediate-shim": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+            "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+        },
         "set-value": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
diff --git a/package.json b/package.json
index cb083020f..6c466825e 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
         "@types/express-flash": "0.0.0",
         "@types/express-session": "^1.15.16",
         "@types/express-validator": "^3.0.0",
+        "@types/file-saver": "^2.0.1",
         "@types/formidable": "^1.0.31",
         "@types/google-maps-react": "^2.0.5",
         "@types/jquery": "^3.5.0",
@@ -154,6 +155,7 @@
         "express-session": "^1.17.0",
         "express-validator": "^5.3.1",
         "expressjs": "^1.0.1",
+        "file-saver": "^2.0.2",
         "find-in-files": "^0.5.0",
         "fit-curve": "^0.1.7",
         "flexlayout-react": "^0.3.11",
@@ -172,6 +174,7 @@
         "image-size-stream": "^1.1.0",
         "js-datepicker": "^4.6.6",
         "jsonschema": "^1.2.5",
+        "jszip": "^3.5.0",
         "libxmljs": "^0.19.7",
         "lodash": "^4.17.15",
         "material-ui": "^0.20.2",
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b63537b5f..086085db5 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -315,7 +315,7 @@ export default class KeyManager {
                         list.push(doc);
                     }
                     if (count === docids.length) {
-                        const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => clone ? await Doc.MakeClone(d) : d));
+                        const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => clone ? (await Doc.MakeClone(d)).clone : d));
                         if (added.length) {
                             added.map(doc => doc.context = targetDataDoc);
                             undoBatch(() => {
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 2d51403d7..b4116e980 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -73,7 +73,7 @@ export class PreviewCursor extends React.Component<{}> {
                         count++;
                         if (doc instanceof Doc) {
                             i === 1 && (first = doc);
-                            const alias = clone ? await Doc.MakeClone(doc) : doc;
+                            const alias = clone ? (await Doc.MakeClone(doc)).clone : doc;
                             const deltaX = NumCast(doc.x) - NumCast(first!.x) - ptx;
                             const deltaY = NumCast(doc.y) - NumCast(first!.y) - pty;
                             alias.x = newPoint[0] + deltaX;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 412f91417..bee9e7009 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -46,6 +46,8 @@ import "./CollectionFreeFormView.scss";
 import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import { SearchUtil } from "../../../util/SearchUtil";
+import { LinkManager } from "../../../util/LinkManager";
 
 library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
 
@@ -1276,6 +1278,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                                     const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
                                     doc.x = xx, doc.y = yy;
                                     this.props.addDocument?.(doc);
+                                    setTimeout(() => {
+                                        SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => {
+                                            docs.docs.forEach(d => LinkManager.Instance.addLink(d));
+                                        })
+                                    }, 2000); // need to give solr some time to update so that this query will find any link docs we've added.
                                 }
                             }
                         }
diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx
index 0cf5505cc..0c4242172 100644
--- a/src/client/views/nodes/DocHolderBox.tsx
+++ b/src/client/views/nodes/DocHolderBox.tsx
@@ -180,7 +180,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent<FieldViewProps, Do
     render() {
         const containedDoc = Cast(this.dataDoc[this.fieldKey], Doc, null);
         TraceMobx();
-        return <div className="documentBox-container" ref={this._contRef}
+        return !containedDoc ? (null) : <div className="documentBox-container" ref={this._contRef}
             onContextMenu={this.specificContextMenu}
             onPointerDown={this.onPointerDown} onClick={this.onClick}
             style={{
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 12d9890c9..3b46b70ea 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -3,7 +3,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import * as rp from "request-promise";
 import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym, AclPrivate, AclEdit } from "../../../fields/Doc";
 import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
@@ -794,11 +793,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
             }
             moreItems.push({
                 description: "Download document", icon: "download", event: async () => {
-                    const a = document.createElement("a");
-                    const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
-                    a.href = url;
-                    a.download = `DocExport-${this.props.Document[Id]}.zip`;
-                    a.click();
+                    Doc.Zip(this.props.Document);
+                    // const a = document.createElement("a");
+                    // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+                    // a.href = url;
+                    // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+                    // a.click();
                 }
             });
             moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 16ade5912..917a6853c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1,6 +1,6 @@
 import { action, computed, observable, ObservableMap, runInAction, untracked } from "mobx";
 import { computedFn } from "mobx-utils";
-import { alias, map, serializable } from "serializr";
+import { alias, map, serializable, list } from "serializr";
 import { DocServer } from "../client/DocServer";
 import { DocumentType } from "../client/documents/DocumentTypes";
 import { Scripting, scriptingGlobal } from "../client/util/Scripting";
@@ -14,12 +14,16 @@ import { ObjectField } from "./ObjectField";
 import { PrefetchProxy, ProxyField } from "./Proxy";
 import { FieldId, RefField } from "./RefField";
 import { RichTextField } from "./RichTextField";
+import { ImageField, VideoField, WebField, AudioField, PdfField } from "./URLField";
+import { DateField } from "./DateField";
 import { listSpec } from "./Schema";
 import { ComputedField } from "./ScriptField";
 import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction, GetEffectiveAcl } from "./util";
 import { LinkManager } from "../client/util/LinkManager";
 import { SharingPermissions } from "../client/util/SharingManager";
+import JSZip = require("jszip");
+import { saveAs } from "file-saver";
 
 export namespace Field {
     export function toKeyValueString(doc: Doc, key: string): string {
@@ -93,6 +97,7 @@ export const WidthSym = Symbol("Width");
 export const HeightSym = Symbol("Height");
 export const DataSym = Symbol("Data");
 export const LayoutSym = Symbol("Layout");
+export const FieldsSym = Symbol("Fields");
 export const AclSym = Symbol("Acl");
 export const AclPrivate = Symbol("AclOwnerOnly");
 export const AclReadonly = Symbol("AclReadOnly");
@@ -180,7 +185,6 @@ export class Doc extends RefField {
     }
 
     @observable
-    //{ [key: string]: Field | FieldWaiting | undefined }
     private ___fields: any = {};
 
     private [UpdatingFromServer]: boolean = false;
@@ -191,6 +195,7 @@ export class Doc extends RefField {
 
     private [Self] = this;
     private [SelfProxy]: any;
+    public [FieldsSym] = () => this.___fields;
     public [AclSym]: { [key: string]: symbol };
     public [WidthSym] = () => NumCast(this[SelfProxy]._width);
     public [HeightSym] = () => NumCast(this[SelfProxy]._height);
@@ -483,27 +488,28 @@ export namespace Doc {
         return alias;
     }
 
-    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[]): Promise<Doc> {
+    export async function makeClone(doc: Doc, cloneMap: Map<string, Doc>, rtfs: { copy: Doc, key: string, field: RichTextField }[], exclusions: string[], dontCreate: boolean): Promise<Doc> {
         if (Doc.IsBaseProto(doc)) return doc;
         if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!;
-        const copy = new Doc(undefined, true);
+        const copy = dontCreate ? doc : new Doc(undefined, true);
         cloneMap.set(doc[Id], copy);
         if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy);
         const filter = Cast(doc.cloneFieldFilter, listSpec("string"), exclusions);
-        Object.keys(doc).forEach(async key => {
+        await Promise.all(Object.keys(doc).map(async key => {
             if (filter.includes(key)) return;
+            const assignKey = (val: any) => !dontCreate && (copy[key] = val);
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
             const field = ProxyField.WithoutProxy(() => doc[key]);
             const copyObjectField = async (field: ObjectField) => {
                 const list = await Cast(doc[key], listSpec(Doc));
                 const docs = list && (await DocListCastAsync(list))?.filter(d => d instanceof Doc);
                 if (docs !== undefined && docs.length) {
-                    const clones = docs.map(async d => await Doc.makeClone(d as Doc, cloneMap, rtfs, exclusions));
-                    copy[key] = new List<Doc>(await Promise.all(clones));
+                    const clones = await Promise.all(docs.map(async d => await Doc.makeClone(d as Doc, cloneMap, rtfs, exclusions, dontCreate)));
+                    !dontCreate && assignKey(new List<Doc>(clones));
                 } else if (doc[key] instanceof Doc) {
-                    copy[key] = key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions); // reference documents except copy documents that are expanded teplate fields 
+                    assignKey(key.includes("layout[") ? undefined : key.startsWith("layout") ? doc[key] as Doc : await Doc.makeClone(doc[key] as Doc, cloneMap, rtfs, exclusions, dontCreate)); // reference documents except copy documents that are expanded teplate fields 
                 } else {
-                    copy[key] = ObjectField.MakeCopy(field);
+                    assignKey(ObjectField.MakeCopy(field));
                     if (field instanceof RichTextField) {
                         if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) {
                             rtfs.push({ copy, key, field });
@@ -513,32 +519,34 @@ export namespace Doc {
             };
             if (key === "proto") {
                 if (doc[key] instanceof Doc) {
-                    copy[key] = await Doc.makeClone(doc[key]!, cloneMap, rtfs, exclusions);
+                    assignKey(await Doc.makeClone(doc[key]!, cloneMap, rtfs, exclusions, dontCreate));
                 }
             } else {
                 if (field instanceof RefField) {
-                    copy[key] = field;
+                    assignKey(field);
                 } else if (cfield instanceof ComputedField) {
-                    copy[key] = ComputedField.MakeFunction(cfield.script.originalScript);
-                    (key === "links" && field instanceof ObjectField) && copyObjectField(field);
+                    !dontCreate && assignKey(ComputedField.MakeFunction(cfield.script.originalScript));
+                    (key === "links" && field instanceof ObjectField) && await copyObjectField(field);
                 } else if (field instanceof ObjectField) {
-                    copyObjectField(field);
+                    await copyObjectField(field);
                 } else if (field instanceof Promise) {
                     debugger; //This shouldn't happend...
                 } else {
-                    copy[key] = field;
+                    assignKey(field);
                 }
             }
-        });
-        Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true);
-        copy.cloneOf = doc;
-        cloneMap.set(doc[Id], copy);
+        }));
+        if (!dontCreate) {
+            Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true);
+            copy.cloneOf = doc;
+            cloneMap.set(doc[Id], copy);
+        }
         return copy;
     }
-    export async function MakeClone(doc: Doc): Promise<Doc> {
+    export async function MakeClone(doc: Doc, dontCreate: boolean = false) {
         const cloneMap = new Map<string, Doc>();
         const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = [];
-        const copy = await Doc.makeClone(doc, cloneMap, rtfMap, ["context", "annotationOn", "cloneOf"]);
+        const copy = await Doc.makeClone(doc, cloneMap, rtfMap, ["context", "annotationOn", "cloneOf"], dontCreate);
         rtfMap.map(({ copy, key, field }) => {
             const replacer = (match: any, attr: string, id: string, offset: any, string: any) => {
                 const mapped = cloneMap.get(id);
@@ -552,9 +560,56 @@ export namespace Doc {
             const re = new RegExp(regex, "g");
             copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text);
         });
-        return copy;
-    }
+        return { clone: copy, map: cloneMap };
+    }
+
+    export async function Zip(doc: Doc) {
+        const { clone, map } = await Doc.MakeClone(doc, true);
+        function replacer(key: any, value: any) {
+            console.log("Checkin: " + key);
+            if (["cloneOf", "context", "cursors"].includes(key)) return undefined;
+            else if (value instanceof Doc) {
+                if (key !== "field" && Number.isNaN(Number(key))) {
+                    const __fields = value[FieldsSym]();
+                    return { id: value[Id], __type: "Doc", fields: __fields };
+                } else {
+                    return { fieldId: value[Id], __type: "proxy" };
+                }
+            }
+            else if (value instanceof RichTextField) return { Data: value.Data, Text: value.Text, __type: "RichTextField" };
+            else if (value instanceof ImageField) return { url: value.url.href, __type: "image" };
+            else if (value instanceof PdfField) return { url: value.url.href, __type: "pdf" };
+            else if (value instanceof AudioField) return { url: value.url.href, __type: "audio" };
+            else if (value instanceof VideoField) return { url: value.url.href, __type: "video" };
+            else if (value instanceof WebField) return { url: value.url.href, __type: "web" };
+            else if (value instanceof DateField) return { date: value.toString(), __type: "date" };
+            else if (value instanceof ProxyField) return { fieldId: value.fieldId, __type: "proxy" };
+            else if (value instanceof Array && key !== "fields") return { fields: value, __type: "list" };
+            else if (value instanceof ComputedField) return { script: value.script, __type: "computed" };
+            else return value;
+        }
+
+        const docs: { [id: string]: any } = {};
+        Array.from(map.entries()).forEach(f => docs[f[0]] = f[1]);
+        const docString = JSON.stringify({ id: doc[Id], docs }, replacer);
+
+        var zip = new JSZip();
 
+        zip.file("doc.json", docString);
+
+        // // Generate a directory within the Zip file structure
+        // var img = zip.folder("images");
+
+        // // Add a file to the directory, in this case an image with data URI as contents
+        // img.file("smile.gif", imgData, {base64: true});
+
+        // Generate the zip file asynchronously
+        zip.generateAsync({ type: "blob" })
+            .then((content: any) => {
+                // Force down of the Zip file
+                saveAs(content, "download.zip");
+            });
+    }
     //
     // Determines whether the layout needs to be expanded (as a template).
     // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 0b9e999ac..4455d11eb 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -16,6 +16,7 @@ const imageDataUri = require('image-data-uri');
 import { isWebUri } from "valid-url";
 import { launch } from "puppeteer";
 import { Opt } from "../../fields/Doc";
+import { SolrManager } from "./SearchManager";
 
 export enum Directory {
     parsed_files = "parsed_files",
@@ -204,6 +205,7 @@ export default class UploadManager extends ApiManager {
                                 } catch (e) { console.log(e); }
                                 unlink(path_2, () => { });
                             }
+                            SolrManager.update();
                             res.send(JSON.stringify(id ? getId(id) : "error"));
                         } catch (e) { console.log(e); }
                         resolve();
-- 
cgit v1.2.3-70-g09d2


From 49e1dccee78af835ef48723bb708f1c2b47c9228 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Wed, 22 Jul 2020 21:09:20 -0400
Subject: fixed highlighting of text based on author.  made download /import
 document novice menu items.

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 69 +++++++++++-----------
 src/client/views/nodes/DocumentView.tsx            | 20 +++----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 15 ++---
 src/client/views/nodes/formattedText/marks_rts.ts  | 10 ++--
 4 files changed, 58 insertions(+), 56 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bee9e7009..dc32ecb07 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1258,41 +1258,44 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (!Doc.UserDoc().noviceMode) {
             optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
             optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" });
-            optionItems.push({
-                description: "Import document", icon: "upload", event: ({ x, y }) => {
-                    const input = document.createElement("input");
-                    input.type = "file";
-                    input.accept = ".zip";
-                    input.onchange = async _e => {
-                        const upload = Utils.prepend("/uploadDoc");
-                        const formData = new FormData();
-                        const file = input.files && input.files[0];
-                        if (file) {
-                            formData.append('file', file);
-                            formData.append('remap', "true");
-                            const response = await fetch(upload, { method: "POST", body: formData });
-                            const json = await response.json();
-                            if (json !== "error") {
-                                const doc = await DocServer.GetRefField(json);
-                                if (doc instanceof Doc) {
-                                    const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
-                                    doc.x = xx, doc.y = yy;
-                                    this.props.addDocument?.(doc);
-                                    setTimeout(() => {
-                                        SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => {
-                                            docs.docs.forEach(d => LinkManager.Instance.addLink(d));
-                                        })
-                                    }, 2000); // need to give solr some time to update so that this query will find any link docs we've added.
-                                }
-                            }
-                        }
-                    };
-                    input.click();
-                }
-            });
+
         }
         !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
-
+        const mores = ContextMenu.Instance.findByDescription("More...");
+        const moreItems = mores && "subitems" in mores ? mores.subitems : [];
+        moreItems.push({
+            description: "Import document", icon: "upload", event: ({ x, y }) => {
+                const input = document.createElement("input");
+                input.type = "file";
+                input.accept = ".zip";
+                input.onchange = async _e => {
+                    const upload = Utils.prepend("/uploadDoc");
+                    const formData = new FormData();
+                    const file = input.files && input.files[0];
+                    if (file) {
+                        formData.append('file', file);
+                        formData.append('remap', "true");
+                        const response = await fetch(upload, { method: "POST", body: formData });
+                        const json = await response.json();
+                        if (json !== "error") {
+                            const doc = await DocServer.GetRefField(json);
+                            if (doc instanceof Doc) {
+                                const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y);
+                                doc.x = xx, doc.y = yy;
+                                this.props.addDocument?.(doc);
+                                setTimeout(() => {
+                                    SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => {
+                                        docs.docs.forEach(d => LinkManager.Instance.addLink(d));
+                                    })
+                                }, 2000); // need to give solr some time to update so that this query will find any link docs we've added.
+                            }
+                        }
+                    }
+                };
+                input.click();
+            }
+        });
+        !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" });
     }
     @observable showTimeline = false;
 
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 3b46b70ea..748938699 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -782,6 +782,16 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
 
         const more = cm.findByDescription("More...");
         const moreItems = more && "subitems" in more ? more.subitems : [];
+        moreItems.push({
+            description: "Download document", icon: "download", event: async () => {
+                Doc.Zip(this.props.Document);
+                // const a = document.createElement("a");
+                // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
+                // a.href = url;
+                // a.download = `DocExport-${this.props.Document[Id]}.zip`;
+                // a.click();
+            }
+        });
         if (!Doc.UserDoc().noviceMode) {
             moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
             moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
@@ -791,16 +801,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
                 moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
                 moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
             }
-            moreItems.push({
-                description: "Download document", icon: "download", event: async () => {
-                    Doc.Zip(this.props.Document);
-                    // const a = document.createElement("a");
-                    // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`);
-                    // a.href = url;
-                    // a.download = `DocExport-${this.props.Document[Id]}.zip`;
-                    // a.click();
-                }
-            });
             moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" });
         }
         GetEffectiveAcl(this.props.Document) === AclEdit && moreItems.push({ description: "Delete", event: this.deleteClicked, icon: "trash" });
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7906e2533..e703a81e2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -425,16 +425,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { background: "moccasin" });
         }
         if (FormattedTextBox._highlights.indexOf("Todo Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "todo", { outline: "black solid 1px" });
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "todo", { outline: "black solid 1px" });
         }
         if (FormattedTextBox._highlights.indexOf("Important Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "important", { "font-size": "larger" });
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "important", { "font-size": "larger" });
         }
         if (FormattedTextBox._highlights.indexOf("Disagree Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "disagree", { "text-decoration": "line-through" });
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "disagree", { "text-decoration": "line-through" });
         }
         if (FormattedTextBox._highlights.indexOf("Ignore Items") !== -1) {
-            addStyleSheetRule(FormattedTextBox._userStyleSheet, "userTag-" + "ignore", { "font-size": "1" });
+            addStyleSheetRule(FormattedTextBox._userStyleSheet, "UT-" + "ignore", { "font-size": "1" });
         }
         if (FormattedTextBox._highlights.indexOf("By Recent Minute") !== -1) {
             addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" });
@@ -1303,9 +1303,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (e.key === "Tab" || e.key === "Enter") {
             e.preventDefault();
         }
-        const mark = e.key !== " " && this._lastTimedMark ? this._lastTimedMark : schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
-        this._lastTimedMark = mark;
-        // this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
+        if (e.key === " " || this._lastTimedMark?.attrs.userid !== Doc.CurrentUserEmail) {
+            const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
+            this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(mark));
+        }
 
         if (!this._undoTyping) {
             this.startUndoTypingBatch();
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 3d7d71b14..f95f46104 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -258,9 +258,7 @@ export const marks: { [index: string]: MarkSpec } = {
         },
         parseDOM: [{ style: 'background: yellow' }],
         toDOM(node: any) {
-            return ['span', {
-                style: `background: ${node.attrs.selected ? "orange" : "yellow"}`
-            }];
+            return ['span', { style: `background: ${node.attrs.selected ? "orange" : "yellow"}` }];
         }
     },
 
@@ -277,8 +275,8 @@ export const marks: { [index: string]: MarkSpec } = {
             const min = Math.round(node.attrs.modified / 12);
             const hr = Math.round(min / 60);
             const day = Math.round(hr / 60 / 24);
-            const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " userMark-remote" : "";
-            return ['span', { class: "userMark-" + uid + remote + " userMark-min-" + min + " userMark-hr-" + hr + " userMark-day-" + day }, 0];
+            const remote = node.attrs.userid !== Doc.CurrentUserEmail ? " UM-remote" : "";
+            return ['span', { class: "UM-" + uid + remote + " UM-min-" + min + " UM-hr-" + hr + " UM-day-" + day }, 0];
         }
     },
     // the id of the user who entered the text
@@ -292,7 +290,7 @@ export const marks: { [index: string]: MarkSpec } = {
         inclusive: false,
         toDOM(node: any) {
             const uid = node.attrs.userid.replace(".", "").replace("@", "");
-            return ['span', { class: "userTag-" + uid + " userTag-" + node.attrs.tag }, 0];
+            return ['span', { class: "UT-" + uid + " UT-" + node.attrs.tag }, 0];
         }
     },
 
-- 
cgit v1.2.3-70-g09d2

