aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortschicke-brown <tyler_schicke@brown.edu>2019-07-16 14:38:34 -0400
committerGitHub <noreply@github.com>2019-07-16 14:38:34 -0400
commit98c3a06256d2cbd720b2c193e5aa282c5dc25350 (patch)
tree5bb7e5a0fa44183bd3824ce94971d2715631a445 /src
parentc6b933c6e89d5f438c434e8676cce9a6a577edbc (diff)
parent520fbe435330c8e426e2d504585ba1559300eadb (diff)
Merge branch 'master' into search_virt
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/util/TooltipTextMenu.tsx1
-rw-r--r--src/client/views/Main.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx4
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionVideoView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx16
-rw-r--r--src/client/views/nodes/LinkMenuItem.tsx5
-rw-r--r--src/client/views/nodes/VideoBox.tsx134
-rw-r--r--src/client/views/nodes/WebBox.tsx2
-rw-r--r--src/client/views/search/SearchBox.tsx13
-rw-r--r--src/new_fields/Doc.ts6
-rw-r--r--src/server/authentication/controllers/user_controller.ts19
-rw-r--r--src/server/authentication/models/user_model.ts4
-rw-r--r--src/server/index.ts4
16 files changed, 123 insertions, 99 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ada9f3610..5bbfe1c49 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -98,7 +98,7 @@ export namespace Docs {
type LayoutSource = { LayoutString: () => string };
type CollectionLayoutSource = { LayoutString: (fieldStr: string, fieldExt?: string) => string };
- type CollectionViewType = [CollectionLayoutSource, string, string?]
+ type CollectionViewType = [CollectionLayoutSource, string, string?];
type PrototypeTemplate = {
layout: {
view: LayoutSource,
@@ -235,7 +235,7 @@ export namespace Docs {
let title = prototypeId.toUpperCase().replace(upper, `_${upper}`);
// synthesize the default options, the type and title from computed values and
// whatever options pertain to this specific prototype
- let options = { title: title, type: type, ...defaultOptions, ...(template.options || {}) };
+ let options = { title: title, type: type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
let primary = layout.view.LayoutString();
let collectionView = layout.collectionView;
if (collectionView) {
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx
index fe7e0b97f..3e804651c 100644
--- a/src/client/util/TooltipTextMenu.tsx
+++ b/src/client/util/TooltipTextMenu.tsx
@@ -239,6 +239,7 @@ export class TooltipTextMenu {
this.linkDrag.onpointerdown = (e: PointerEvent) => {
let dragData = new DragManager.LinkDragData(this.editorProps.Document);
dragData.dontClearTextBox = true;
+ e.stopPropagation();
DragManager.StartLinkDrag(this.linkDrag!, dragData, e.clientX, e.clientY,
{
handlers: {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 80399e24b..86578af3e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -26,7 +26,7 @@ let swapDocs = async () => {
}
CurrentUserUtils.UserDocument.linkManagerDoc = undefined;
}
-}
+};
(async () => {
const info = await CurrentUserUtils.loadCurrentUser();
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1069ebbdb..a193ff677 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -301,7 +301,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
this._flush = false;
setTimeout(() => {
CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig());
- this.stateChanged()
+ this.stateChanged();
}, 10);
}
}
@@ -606,7 +606,7 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
addDocTab={this.addDocTab}
ContainingCollectionView={undefined}
zoomToScale={emptyFunction}
- getScale={returnOne} />
+ getScale={returnOne} />;
}
@computed get content() {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 0196fecff..c212cc97c 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -153,7 +153,7 @@ class TreeView extends React.Component<TreeViewProps> {
let docList = Cast(this.resolvedDataDoc[this.fieldKey], listSpec(Doc));
let doc = Cast(this.resolvedDataDoc[this.fieldKey], Doc);
let isDoc = doc instanceof Doc || docList;
- let c
+ let c;
return <div className="bullet" onClick={action(() => this._collapsed = !this._collapsed)} style={{ color: StrCast(this.props.document.color, "black"), opacity: 0.4 }}>
{<FontAwesomeIcon icon={this._collapsed ? (isDoc ? "caret-square-right" : "caret-right") : (isDoc ? "caret-square-down" : "caret-down")} />}
</div>;
diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index f731c4cef..096e7e9d1 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -88,14 +88,14 @@ export class CollectionVideoView extends React.Component<FieldViewProps> {
canvas.width = 640;
canvas.height = 640 * NumCast(this.props.Document.nativeHeight) / NumCast(this.props.Document.nativeWidth);
var ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
- ctx && ctx.drawImage(this._videoBox!.player!, 0, 0, canvas.width, canvas.height);
+ this._videoBox!.player && ctx && ctx.drawImage(this._videoBox!.player, 0, 0, canvas.width, canvas.height);
//convert to desired file format
var dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, "");
- SearchBox.convertDataUri(dataUrl, filename).then((returnedFilename) => {
+ SearchBox.convertDataUri(dataUrl, filename).then(returnedFilename => {
if (returnedFilename) {
let url = DocServer.prepend(returnedFilename);
let imageSummary = Docs.Create.ImageDocument(url, {
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a4a6881f8..b765517a2 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -147,7 +147,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
PreviewCursor.Visible = false;
this.cleanupInteractions(true);
if (e.button === 2 || (e.button === 0 && e.altKey)) {
- if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
document.addEventListener("pointermove", this.onPointerMove, true);
document.addEventListener("pointerup", this.onPointerUp, true);
document.addEventListener("keydown", this.marqueeCommand, true);
@@ -181,6 +180,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@action
onPointerUp = (e: PointerEvent): void => {
+ if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]);
// console.log("pointer up!");
if (this._visible) {
// console.log("visible");
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 73ae8955d..4c5ad7a7d 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -22,6 +22,7 @@ import "./ImageBox.scss";
import React = require("react");
import { RouteStore } from '../../../server/RouteStore';
import { Docs } from '../../documents/Documents';
+import { DocServer } from '../../DocServer';
var requestImageSize = require('../../util/request-image-size');
var path = require('path');
@@ -33,7 +34,7 @@ export const pageSchema = createSchema({
curPage: "number",
});
-interface window {
+interface Window {
MediaRecorder: MediaRecorder;
}
@@ -157,8 +158,15 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
}).then(function (stream) {
gumStream = stream;
recorder = new MediaRecorder(stream);
- recorder.ondataavailable = function (e: any) {
- var url = URL.createObjectURL(e.data);
+ recorder.ondataavailable = async function (e: any) {
+ const formData = new FormData();
+ formData.append("file", e.data);
+ const res = await fetch(DocServer.prepend(RouteStore.upload), {
+ method: 'POST',
+ body: formData
+ });
+ const files = await res.json();
+ const url = DocServer.prepend(files[0]);
// upload to server with known URL
let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", x: NumCast(self.props.Document.x), y: NumCast(self.props.Document.y), width: 200, height: 32 });
audioDoc.embed = true;
@@ -174,7 +182,7 @@ export class ImageBox extends DocComponent<FieldViewProps, ImageDocument>(ImageD
recorder.stop();
gumStream.getAudioTracks()[0].stop();
- }, 1000);
+ }, 5000);
});
}
diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx
index 9728671c0..6a18a4e7b 100644
--- a/src/client/views/nodes/LinkMenuItem.tsx
+++ b/src/client/views/nodes/LinkMenuItem.tsx
@@ -7,7 +7,7 @@ import { undoBatch } from "../../util/UndoManager";
import './LinkMenu.scss';
import React = require("react");
import { Doc } from '../../../new_fields/Doc';
-import { StrCast, Cast, BoolCast, FieldValue } from '../../../new_fields/Types';
+import { StrCast, Cast, BoolCast, FieldValue, NumCast } from '../../../new_fields/Types';
import { observable, action } from 'mobx';
import { LinkManager } from '../../util/LinkManager';
import { DragLinkAsDocument } from '../../util/DragManager';
@@ -38,7 +38,8 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
jumpToDoc = pdfDoc;
}
if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey);
+ let self = this;
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page)));
} else {
CollectionDockingView.Instance.AddRightSplit(jumpToDoc, undefined);
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 66844cdd6..972e6875d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,5 +1,5 @@
import React = require("react");
-import { action, IReactionDisposer, observable, reaction, trace, computed } from "mobx";
+import { action, IReactionDisposer, observable, reaction, trace, computed, runInAction, untracked } from "mobx";
import { observer } from "mobx-react";
import { makeInterface } from "../../../new_fields/Schema";
import { Cast, FieldValue, NumCast } from "../../../new_fields/Types";
@@ -24,6 +24,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
private _youtubeReactionDisposer?: IReactionDisposer;
private _youtubePlayer: any = undefined;
private _videoRef: HTMLVideoElement | null = null;
+ private _youtubeIframeId: number = -1;
+ private _youtubeContentCreated = false;
+ static _youtubeIframeCounter: number = 0;
+ @observable _forceCreateYouTubeIFrame = false;
+ @observable static _showControls: boolean;
@observable _playTimer?: NodeJS.Timeout = undefined;
@observable _fullScreen = false;
@observable public Playing: boolean = false;
@@ -44,29 +49,29 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
}
- @action public Play = () => {
+ @action public Play = (update: boolean = true) => {
this.Playing = true;
- this.player && this.player.play();
- this._youtubePlayer && this._youtubePlayer.playVideo();
+ update && this.player && this.player.play();
+ console.log("PLAYING = " + update);
+ update && this._youtubePlayer && this._youtubePlayer.playVideo();
!this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
- this._youtubeSeekTo = false;
this.updateTimecode();
}
@action public Seek(time: number) {
- if (this._youtubePlayer && !this.Playing) {
- this._youtubeSeekTo = true;
- this._youtubePlayer.seekTo(time);
- }
+ console.log("Seeking " + time);
+ //if (this._youtubePlayer && this._youtubePlayer.getPlayerState() === 5) return;
+ this._youtubePlayer && this._youtubePlayer.seekTo(Math.round(time), true);
}
- @action public Pause = () => {
+ @action public Pause = (update: boolean = true) => {
this.Playing = false;
- this.player && this.player.pause();
- this._youtubePlayer && this._youtubePlayer.pauseVideo();
+ console.log("PAUSING = " + update);
+ update && this.player && this.player.pause();
+ update && this._youtubePlayer && this._youtubePlayer.pauseVideo();
this._playTimer && clearInterval(this._playTimer);
this._playTimer = undefined;
- this._youtubeSeekTo = false;
+ this.updateTimecode();
}
@action public FullScreen() {
@@ -80,12 +85,11 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
this.player && (this.props.Document.curPage = this.player.currentTime);
this._youtubePlayer && (this.props.Document.curPage = this._youtubePlayer.getCurrentTime());
}
+
componentDidMount() {
if (this.props.setVideoBox) this.props.setVideoBox(this);
- let field = Cast(this.Document[this.props.fieldKey], VideoField);
- let videoid = field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
- if (videoid) {
+ if (this.youtubeVideoId) {
let youtubeaspect = 400 / 315;
var nativeWidth = FieldValue(this.Document.nativeWidth, 0);
var nativeHeight = FieldValue(this.Document.nativeHeight, 0);
@@ -94,47 +98,6 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
this.Document.nativeHeight = this.Document.nativeWidth / youtubeaspect;
this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect;
}
- this._youtubePlayer = new YT.Player(`${videoid}-player`, {
- height: `${NumCast(this.props.Document.height)}`,
- width: `${NumCast(this.props.Document.width)}`,
- videoId: videoid.toString(),
- playerVars: { 'controls': VideoBox._showControls ? 1 : 0 },
- events: {
- 'onStateChange': this.onYoutubePlayerStateChange,
- 'onReady': this.onYoutubePlayerReady,
- }
- });
- this._reactionDisposer = reaction(() => this.props.Document.curPage, () => this.Seek(this.Document.curPage || 0), { fireImmediately: true });
- this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
- let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
- this._youtubePlayer.getIframe().style.pointerEvents = interactive ? "all" : "none";
- }, { fireImmediately: true })
- // let iframe = $(document.getElementById(`${videoid}-player`)!);
- // iframe.on("load", function () {
- // iframe.contents().find("head")
- // .append($("<style type='text/css'> .ytp-pause-overlay, .ytp-scroll-min { opacity : 0 !important; } </style>"));
- // })
- }
- }
-
- @action
- onYoutubePlayerStateChange = (event: any) => {
- console.log("event.data = " + event.data);
- this.Playing = event.data == YT.PlayerState.PLAYING;
- if (this._youtubeSeekTo && this.Playing) {
- this._youtubePlayer.pauseVideo();
- this._youtubeSeekTo = false;
- } else this.Playing && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 500));
- event.data === YT.PlayerState.PAUSED && this._playTimer && clearInterval(this._playTimer);
- }
- _youtubeSeekTo = false;
- @action
- onYoutubePlayerReady = (event: any) => {
- this.Playing = false;
- this._youtubePlayer && (this._youtubePlayer.getIframe().style.pointerEvents = "none");
- if (this.Document.curPage) {
- this.Seek(this.Document.curPage);
- this._youtubeSeekTo = true;
}
}
@@ -156,18 +119,13 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
}
}
- @observable static _showControls: boolean;
-
- @computed get youtubeVideoId() {
- let field = Cast(this.Document[this.props.fieldKey], VideoField);
- return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
- }
-
specificContextMenu = (e: React.MouseEvent): void => {
let field = Cast(this.Document[this.props.fieldKey], VideoField);
if (field) {
let subitems: ContextMenuProps[] = [];
subitems.push({ description: "Toggle Show Controls", event: action(() => VideoBox._showControls = !VideoBox._showControls), icon: "expand-arrows-alt" });
+ subitems.push({ description: "GOTO 3", event: action(() => this.Seek(3)), icon: "expand-arrows-alt" });
+ subitems.push({ description: "PLAY", event: action(() => this.Play()), icon: "expand-arrows-alt" });
ContextMenu.Instance.addItem({ description: "Video Funcs...", subitems: subitems });
}
}
@@ -177,15 +135,59 @@ export class VideoBox extends DocComponent<FieldViewProps, VideoDocument>(VideoD
let interactive = InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive";
let style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div>Loading</div> :
- <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls} onPlay={this.Play} onSeeked={this.updateTimecode} onPause={this.Pause}>
+ <video className={`${style}`} ref={this.setVideoRef} onCanPlay={this.videoLoad} controls={VideoBox._showControls}
+ onPlay={() => this.Play()} onSeeked={this.updateTimecode} onPause={() => this.Pause()}>
<source src={field.url.href} type="video/mp4" />
Not supported.
</video>;
}
+ @computed get youtubeVideoId() {
+ let field = Cast(this.Document[this.props.fieldKey], VideoField);
+ return field && field.url.href.indexOf("youtube") !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split("/")) : "";
+ }
+
+ @action youtubeIframeLoaded = (e: any) => {
+ if (!this._youtubeContentCreated) {
+ this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
+ return;
+ }
+ else this._youtubeContentCreated = false;
+
+ let iframe = e.target;
+ let onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+ console.log("Event " + event.data);
+ if (event.data === YT.PlayerState.PLAYING && !this.Playing) this.Play(false);
+ if (event.data === YT.PlayerState.PAUSED && this.Playing) this.Pause(false);
+ });
+ let onYoutubePlayerReady = (event: any) => {
+ console.log("READY!");
+ this._reactionDisposer && this._reactionDisposer();
+ this._youtubeReactionDisposer && this._youtubeReactionDisposer();
+ this._reactionDisposer = reaction(() => this.props.Document.curPage, () => !this.Playing && this.Seek(this.Document.curPage || 0));
+ this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => {
+ let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting;
+ iframe.style.pointerEvents = interactive ? "all" : "none";
+ }, { fireImmediately: true });
+ };
+ this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
+ events: {
+ 'onReady': onYoutubePlayerReady,
+ 'onStateChange': onYoutubePlayerStateChange,
+ }
+ });
+
+ }
+
@computed get youtubeContent() {
+ this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
+ this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
let style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
- return <div id={`${this.youtubeVideoId}-player`} className={`${style}`} />;
+ let start = untracked(() => Math.round(NumCast(this.props.Document.curPage)));
+ return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+ onLoad={this.youtubeIframeLoaded} className={`${style}`} width="640" height="390"
+ src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`}
+ ></iframe>;
}
render() {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 96b972a1c..f0a9ec6d8 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -19,7 +19,7 @@ export function onYouTubeIframeAPIReady() {
});
}
// must cast as any to set property on window
-const _global = (window /* browser */ || global /* node */) as any
+const _global = (window /* browser */ || global /* node */) as any;
_global.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
function onPlayerReady(event: any) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 661c9c10b..ba68ccd54 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -6,7 +6,7 @@ import "./FilterBox.scss";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { SetupDrag } from '../../util/DragManager';
import { Docs } from '../../documents/Documents';
-import { NumCast } from '../../../new_fields/Types';
+import { NumCast, Cast } from '../../../new_fields/Types';
import { Doc } from '../../../new_fields/Doc';
import { SearchItem } from './SearchItem';
import { DocServer } from '../../DocServer';
@@ -120,7 +120,7 @@ export class SearchBox extends React.Component {
private get filterQuery() {
const types = FilterBox.Instance.filterTypes;
- return "proto_i:*" + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : "");
+ return "NOT baseProto_b:true" + (types ? ` AND (${types.map(type => `({!join from=id to=proto_i}type_t:"${type}" AND NOT type_t:*) OR type_t:"${type}"`).join(" ")})` : "");
}
@@ -131,15 +131,18 @@ export class SearchBox extends React.Component {
}
this.lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
- this._curRequest = SearchUtil.Search(query, this.filterQuery, true, this._maxSearchIndex, 10).then(action((res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, this.filterQuery, true, this._maxSearchIndex, 10).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
this._numTotalResults = res.numFound;
}
- let filteredDocs = FilterBox.Instance.filterDocsByType(res.docs);
- this._results.push(...filteredDocs);
+ const docs = await Promise.all(res.docs.map(doc => Cast(doc.extendsDoc, Doc, doc as any)));
+ let filteredDocs = FilterBox.Instance.filterDocsByType(docs);
+ runInAction(() => {
+ this._results.push(...filteredDocs);
+ });
this._curRequest = undefined;
}));
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index c5f9e7adf..0d9fa540f 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -312,16 +312,20 @@ export namespace Doc {
}
export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) {
- if (doc[fieldKey + "_ext"] === undefined) {
+ let extensionDoc = doc[fieldKey + "_ext"];
+ if (extensionDoc === undefined) {
setTimeout(() => {
let docExtensionForField = new Doc(doc[Id] + fieldKey, true);
docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey;
+ docExtensionForField.extendsDoc = doc;
let proto: Doc | undefined = doc;
while (proto && !Doc.IsPrototype(proto)) {
proto = proto.proto;
}
(proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField;
}, 0);
+ } else if (extensionDoc instanceof Doc && extensionDoc.extendsDoc === undefined) {
+ setTimeout(() => (extensionDoc as Doc).extendsDoc = doc, 0);
}
}
export function MakeAlias(doc: Doc) {
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index ca4fc171c..0e431f1e6 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -12,6 +12,9 @@ import * as nodemailer from 'nodemailer';
import c = require("crypto");
import { RouteStore } from "../../RouteStore";
import { Utils } from "../../../Utils";
+import { Schema } from "mongoose";
+import { Opt } from "../../../new_fields/Doc";
+import { MailOptions } from "nodemailer/lib/stream-transport";
/**
* GET /signup
@@ -45,21 +48,23 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
return res.redirect(RouteStore.signup);
}
- const email = req.body.email;
+ const email = req.body.email as String;
const password = req.body.password;
- const user = new User({
+ const model = {
email,
password,
userDocumentId: Utils.GenerateGuid()
- });
+ } as Partial<DashUserModel>;
+
+ const user = new User(model);
User.findOne({ email }, (err, existingUser) => {
if (err) { return next(err); }
if (existingUser) {
return res.redirect(RouteStore.login);
}
- user.save((err) => {
+ user.save((err: any) => {
if (err) { return next(err); }
req.logIn(user, (err) => {
if (err) { return next(err); }
@@ -188,8 +193,8 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
- };
- smtpTransport.sendMail(mailOptions, function (err) {
+ } as MailOptions;
+ smtpTransport.sendMail(mailOptions, function (err: Error | null) {
// req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(null, err, 'done');
});
@@ -259,7 +264,7 @@ export let postReset = function (req: Request, res: Response) {
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
- };
+ } as MailOptions;
smtpTransport.sendMail(mailOptions, function (err) {
done(null, err);
});
diff --git a/src/server/authentication/models/user_model.ts b/src/server/authentication/models/user_model.ts
index ee85e1c05..45fbf23b1 100644
--- a/src/server/authentication/models/user_model.ts
+++ b/src/server/authentication/models/user_model.ts
@@ -16,7 +16,7 @@ mongoose.connection.on('disconnected', function () {
console.log('connection closed');
});
export type DashUserModel = mongoose.Document & {
- email: string,
+ email: String,
password: string,
passwordResetToken?: string,
passwordResetExpires?: Date,
@@ -42,7 +42,7 @@ export type AuthToken = {
};
const userSchema = new mongoose.Schema({
- email: { type: String, unique: true },
+ email: String,
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
diff --git a/src/server/index.ts b/src/server/index.ts
index 1c0dec05b..2cca7a35b 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -58,7 +58,7 @@ clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"
fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8");
const mongoUrl = 'mongodb://localhost:27017/Dash';
-mongoose.connect(mongoUrl);
+mongoose.connection.readyState === 0 && mongoose.connect(mongoUrl);
mongoose.connection.on('connected', () => console.log("connected"));
// SESSION MANAGEMENT AND AUTHENTICATION MIDDLEWARE
@@ -110,7 +110,7 @@ function addSecureRoute(method: Method,
if (req.user) {
handler(req.user, res, req);
} else {
- req.session!.target = `http://localhost:${port}${req.originalUrl}`;
+ req.session!.target = `${req.headers.host}${req.originalUrl}`;
onRejection(res, req);
}
};