aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/ApiManagers/DeleteManager.ts17
-rw-r--r--src/server/ApiManagers/DownloadManager.ts6
-rw-r--r--src/server/ApiManagers/GooglePhotosManager.ts9
-rw-r--r--src/server/ApiManagers/UploadManager.ts18
-rw-r--r--src/server/ApiManagers/UserManager.ts55
-rw-r--r--src/server/ApiManagers/UtilManager.ts14
-rw-r--r--src/server/DashSession/DashSessionAgent.ts4
-rw-r--r--src/server/DashUploadUtils.ts120
-rw-r--r--src/server/RouteManager.ts35
-rw-r--r--src/server/Websocket/Websocket.ts9
-rw-r--r--src/server/authentication/models/current_user_utils.ts160
-rw-r--r--src/server/updateSearch.ts12
12 files changed, 337 insertions, 122 deletions
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index 88dfa6a64..be452c0ff 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -1,5 +1,5 @@
import ApiManager, { Registration } from "./ApiManager";
-import { Method, _permission_denied } from "../RouteManager";
+import { Method, _permission_denied, PublicHandler } from "../RouteManager";
import { WebSocket } from "../Websocket/Websocket";
import { Database } from "../database";
@@ -31,6 +31,21 @@ export default class DeleteManager extends ApiManager {
}
});
+ const hi: PublicHandler = async ({ res, isRelease }) => {
+ if (isRelease) {
+ return _permission_denied(res, deletionPermissionError);
+ }
+ await Database.Instance.deleteAll('users');
+ res.redirect("/home");
+ };
+
+ // register({
+ // method: Method.GET,
+ // subscription: "/deleteUsers",
+ // onValidation: hi,
+ // onUnauthenticated: hi
+ // });
+
register({
method: Method.GET,
diff --git a/src/server/ApiManagers/DownloadManager.ts b/src/server/ApiManagers/DownloadManager.ts
index 1bb84f374..fad5e6789 100644
--- a/src/server/ApiManagers/DownloadManager.ts
+++ b/src/server/ApiManagers/DownloadManager.ts
@@ -254,11 +254,13 @@ async function writeHierarchyRecursive(file: Archiver.Archiver, hierarchy: Hiera
// and dropped in the browser and thus hosted remotely) so we upload it
// to our server and point the zip file to it, so it can bundle up the bytes
const information = await DashUploadUtils.UploadImage(result);
- path = information.serverAccessPaths[SizeSuffix.Original];
+ path = information instanceof Error ? "" : information.serverAccessPaths[SizeSuffix.Original];
}
// write the file specified by the path to the directory in the
// zip file given by the prefix.
- file.file(path, { name: documentTitle, prefix });
+ if (path) {
+ file.file(path, { name: documentTitle, prefix });
+ }
} else {
// we've hit a collection, so we have to recurse
await writeHierarchyRecursive(file, result, `${prefix}/${documentTitle}`);
diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts
index 107542ce2..1727cc5a6 100644
--- a/src/server/ApiManagers/GooglePhotosManager.ts
+++ b/src/server/ApiManagers/GooglePhotosManager.ts
@@ -88,8 +88,13 @@ export default class GooglePhotosManager extends ApiManager {
if (contents) {
const completed: Opt<DashUploadUtils.ImageUploadInformation>[] = [];
for (const item of contents.mediaItems) {
- const { contentSize, ...attributes } = await DashUploadUtils.InspectImage(item.baseUrl);
- const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize!);
+ const results = await DashUploadUtils.InspectImage(item.baseUrl);
+ if (results instanceof Error) {
+ failed++;
+ continue;
+ }
+ const { contentSize, ...attributes } = results;
+ const found: Opt<DashUploadUtils.ImageUploadInformation> = await Database.Auxiliary.QueryUploadHistory(contentSize);
if (!found) {
const upload = await DashUploadUtils.UploadInspectedImage({ contentSize, ...attributes }, item.filename, prefix).catch(error => _error(res, downloadError, error));
if (upload) {
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 74f45ae62..4d09528f4 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -40,7 +40,7 @@ export default class UploadManager extends ApiManager {
register({
method: Method.POST,
- subscription: "/upload",
+ subscription: "/uploadFormData",
secureHandler: async ({ req, res }) => {
const form = new formidable.IncomingForm();
form.uploadDir = pathToDirectory(Directory.parsed_files);
@@ -61,6 +61,19 @@ export default class UploadManager extends ApiManager {
register({
method: Method.POST,
+ subscription: "/uploadRemoteImage",
+ secureHandler: async ({ req, res }) => {
+ const { sources } = req.body;
+ if (Array.isArray(sources)) {
+ const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
+ return res.send(results);
+ }
+ res.send();
+ }
+ });
+
+ register({
+ method: Method.POST,
subscription: "/uploadDoc",
secureHandler: ({ req, res }) => {
const form = new formidable.IncomingForm();
@@ -169,8 +182,7 @@ export default class UploadManager extends ApiManager {
secureHandler: async ({ req, res }) => {
const { source } = req.body;
if (typeof source === "string") {
- const { serverAccessPaths } = await DashUploadUtils.UploadImage(source);
- return res.send(await DashUploadUtils.InspectImage(serverAccessPaths[SizeSuffix.Original]));
+ return res.send(await DashUploadUtils.InspectImage(source));
}
res.send({});
}
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index f2ef22961..b0d868918 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -2,6 +2,8 @@ import ApiManager, { Registration } from "./ApiManager";
import { Method } from "../RouteManager";
import { Database } from "../database";
import { msToTime } from "../ActionUtilities";
+import * as bcrypt from "bcrypt-nodejs";
+import { Opt } from "../../new_fields/Doc";
export const timeMap: { [id: string]: number } = {};
interface ActivityUnit {
@@ -37,6 +39,59 @@ export default class UserManager extends ApiManager {
});
register({
+ method: Method.POST,
+ subscription: '/internalResetPassword',
+ secureHandler: async ({ user, req, res }) => {
+ const result: any = {};
+ const { curr_pass, new_pass, new_confirm } = req.body;
+ // perhaps should assert whether curr password is entered correctly
+ const validated = await new Promise<Opt<boolean>>(resolve => {
+ bcrypt.compare(curr_pass, user.password, (err, passwords_match) => {
+ if (err || !passwords_match) {
+ result.error = [{ msg: "Incorrect current password" }];
+ res.send(result);
+ resolve(undefined);
+ } else {
+ resolve(passwords_match);
+ }
+ });
+ });
+
+ if (validated === undefined) {
+ return;
+ }
+
+ req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 });
+ req.assert("new_confirm", "Passwords do not match").equals(new_pass);
+ if (curr_pass === new_pass) {
+ result.error = [{ msg: "Current and new password are the same" }];
+ }
+ // was there error in validating new passwords?
+ if (req.validationErrors()) {
+ // was there error?
+ result.error = req.validationErrors();
+ }
+
+ // will only change password if there are no errors.
+ if (!result.error) {
+ user.password = new_pass;
+ user.passwordResetToken = undefined;
+ user.passwordResetExpires = undefined;
+ }
+
+ user.save(err => {
+ if (err) {
+ result.error = [{ msg: "Error while saving new password" }];
+ }
+ });
+
+ res.send(result);
+ }
+ });
+
+
+
+ register({
method: Method.GET,
subscription: "/activity",
secureHandler: ({ res }) => {
diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts
index a0d0d0f4b..dbf274e93 100644
--- a/src/server/ApiManagers/UtilManager.ts
+++ b/src/server/ApiManagers/UtilManager.ts
@@ -4,6 +4,7 @@ import { exec } from 'child_process';
import { command_line } from "../ActionUtilities";
import RouteSubscriber from "../RouteSubscriber";
import { red } from "colors";
+import { main } from "../../scraping/buxton/node_scraper";
export default class UtilManager extends ApiManager {
@@ -47,7 +48,12 @@ export default class UtilManager extends ApiManager {
const onResolved = (stdout: string) => { console.log(stdout); res.redirect("/"); };
const onRejected = (err: any) => { console.error(err.message); res.send(err); };
- const tryPython3 = () => command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+ const tryPython3 = (reason: any) => {
+ console.log("Initial scraper failed for the following reason:");
+ console.log(red(reason.Error));
+ console.log("Falling back to python3...");
+ return command_line('python3 scraper.py', cwd).then(onResolved, onRejected);
+ };
return command_line('python scraper.py', cwd).then(onResolved, tryPython3);
},
@@ -55,6 +61,12 @@ export default class UtilManager extends ApiManager {
register({
method: Method.GET,
+ subscription: "/newBuxton",
+ secureHandler: async ({ res }) => res.send(await main())
+ });
+
+ register({
+ method: Method.GET,
subscription: "/version",
secureHandler: ({ res }) => {
return new Promise<void>(resolve => {
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index c55e01243..d61e9aac1 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -25,7 +25,8 @@ export class DashSessionAgent extends AppliedSessionAgent {
* The core method invoked when the single master thread is initialized.
* Installs event hooks, repl commands and additional IPC listeners.
*/
- protected async initializeMonitor(monitor: Monitor, sessionKey: string): Promise<void> {
+ protected async initializeMonitor(monitor: Monitor): Promise<string> {
+ const sessionKey = Utils.GenerateGuid();
await this.dispatchSessionPassword(sessionKey);
monitor.addReplCommand("pull", [], () => monitor.exec("git pull"));
monitor.addReplCommand("solr", [/start|stop|index/], this.executeSolrCommand);
@@ -34,6 +35,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
monitor.on("backup", this.backup);
monitor.on("debug", async ({ to }) => this.dispatchZippedDebugBackup(to));
monitor.coreHooks.onCrashDetected(this.dispatchCrashReport);
+ return sessionKey;
}
/**
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index d9d985ca5..27c4bf854 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -1,4 +1,4 @@
-import * as fs from 'fs';
+import { unlinkSync, createWriteStream, readFileSync, rename, writeFile } from 'fs';
import { Utils } from '../Utils';
import * as path from 'path';
import * as sharp from 'sharp';
@@ -12,8 +12,9 @@ import { basename } from "path";
import { createIfNotExists } from './ActionUtilities';
import { ParsedPDF } from "../server/PdfTypes";
const parse = require('pdf-parse');
-import { Directory, serverPathToFile, clientPathToFile } from './ApiManagers/UploadManager';
+import { Directory, serverPathToFile, clientPathToFile, pathToDirectory } from './ApiManagers/UploadManager';
import { red } from 'colors';
+const requestImageSize = require("../client/util/request-image-size");
export enum SizeSuffix {
Small = "_s",
@@ -27,6 +28,10 @@ export function InjectSize(filename: string, size: SizeSuffix) {
return filename.substring(0, filename.length - extension.length) + size + extension;
}
+function isLocal() {
+ return /Dash-Web[\\\/]src[\\\/]server[\\\/]public[\\\/](.*)/;
+}
+
export namespace DashUploadUtils {
export interface Size {
@@ -92,24 +97,18 @@ export namespace DashUploadUtils {
}
async function UploadPdf(absolutePath: string) {
- const dataBuffer = fs.readFileSync(absolutePath);
+ const dataBuffer = readFileSync(absolutePath);
const result: ParsedPDF = await parse(dataBuffer);
const parsedName = basename(absolutePath);
await new Promise<void>((resolve, reject) => {
const textFilename = `${parsedName.substring(0, parsedName.length - 4)}.txt`;
- const writeStream = fs.createWriteStream(serverPathToFile(Directory.text, textFilename));
+ const writeStream = createWriteStream(serverPathToFile(Directory.text, textFilename));
writeStream.write(result.text, error => error ? reject(error) : resolve());
});
return MoveParsedFile(absolutePath, Directory.pdfs);
}
- const generate = (prefix: string, url: string) => `${prefix}upload_${Utils.GenerateGuid()}${sanitizeExtension(url)}`;
- const sanitizeExtension = (source: string) => {
- let extension = path.extname(source);
- extension = extension.toLowerCase();
- extension = extension.split("?")[0];
- return extension;
- };
+ const generate = (prefix: string, extension: string) => `${prefix}upload_${Utils.GenerateGuid()}.${extension}`;
/**
* Uploads an image specified by the @param source to Dash's /public/files/
@@ -128,18 +127,23 @@ export namespace DashUploadUtils {
* 3) the size of the image, in bytes (4432130)
* 4) the content type of the image, i.e. image/(jpeg | png | ...)
*/
- export const UploadImage = async (source: string, filename?: string, format?: string, prefix: string = ""): Promise<ImageUploadInformation> => {
+ export const UploadImage = async (source: string, filename?: string, format?: string, prefix: string = ""): Promise<ImageUploadInformation | Error> => {
const metadata = await InspectImage(source);
- return UploadInspectedImage(metadata, filename, format, prefix);
+ if (metadata instanceof Error) {
+ return metadata;
+ }
+ return UploadInspectedImage(metadata, filename || metadata.filename, format, prefix);
};
export interface InspectionResults {
- isLocal: boolean;
- stream: any;
- normalizedUrl: string;
+ source: string;
+ requestable: string;
exifData: EnrichedExifData;
- contentSize?: number;
- contentType?: string;
+ contentSize: number;
+ contentType: string;
+ nativeWidth: number;
+ nativeHeight: number;
+ filename?: string;
}
export interface EnrichedExifData {
@@ -152,31 +156,55 @@ export namespace DashUploadUtils {
return Promise.all(pending);
}
+ export interface RequestedImageSize {
+ width: number;
+ height: number;
+ type: string;
+ }
+
/**
* Based on the url's classification as local or remote, gleans
* as much information as possible about the specified image
*
* @param source is the path or url to the image in question
*/
- export const InspectImage = async (source: string): Promise<InspectionResults> => {
- const { isLocal, stream, normalized: normalizedUrl } = classify(source);
- const exifData = await parseExifData(source);
+ export const InspectImage = async (source: string): Promise<InspectionResults | Error> => {
+ let rawMatches: RegExpExecArray | null;
+ let filename: string | undefined;
+ if ((rawMatches = /^data:image\/([a-z]+);base64,(.*)/.exec(source)) !== null) {
+ const [ext, data] = rawMatches.slice(1, 3);
+ const resolved = filename = `upload_${Utils.GenerateGuid()}.${ext}`;
+ const error = await new Promise<Error | null>(resolve => {
+ writeFile(serverPathToFile(Directory.images, resolved), data, "base64", resolve);
+ });
+ if (error !== null) {
+ return error;
+ }
+ source = `http://localhost:1050${clientPathToFile(Directory.images, resolved)}`;
+ }
+ let resolvedUrl: string;
+ const matches = isLocal().exec(source);
+ if (matches === null) {
+ resolvedUrl = source;
+ } else {
+ resolvedUrl = `http://localhost:1050/${matches[1].split("\\").join("/")}`;
+ }
+ const exifData = await parseExifData(resolvedUrl);
const results = {
exifData,
- isLocal,
- stream,
- normalizedUrl
+ requestable: resolvedUrl
};
- // stop here if local, since request.head() can't handle local paths, only urls on the web
- if (isLocal) {
- return results;
- }
const { headers } = (await new Promise<any>((resolve, reject) => {
- request.head(source, (error, res) => error ? reject(error) : resolve(res));
- }));
+ request.head(resolvedUrl, (error, res) => error ? reject(error) : resolve(res));
+ }).catch(error => console.error(error)));
+ const { width: nativeWidth, height: nativeHeight }: RequestedImageSize = await requestImageSize(resolvedUrl);
return {
+ source,
contentSize: parseInt(headers[size]),
contentType: headers[type],
+ nativeWidth,
+ nativeHeight,
+ filename,
...results
};
};
@@ -185,22 +213,20 @@ export namespace DashUploadUtils {
return new Promise<{ clientAccessPath: Opt<string> }>(resolve => {
const filename = basename(absolutePath);
const destinationPath = serverPathToFile(destination, filename);
- fs.rename(absolutePath, destinationPath, error => {
+ rename(absolutePath, destinationPath, error => {
resolve({ clientAccessPath: error ? undefined : clientPathToFile(destination, filename) });
});
});
}
export const UploadInspectedImage = async (metadata: InspectionResults, filename?: string, format?: string, prefix = ""): Promise<ImageUploadInformation> => {
- const { isLocal, stream, normalizedUrl, contentSize, contentType, exifData } = metadata;
- const resolved = filename || generate(prefix, normalizedUrl);
- const extension = format || sanitizeExtension(normalizedUrl || resolved);
+ const { requestable, source, ...remaining } = metadata;
+ const extension = remaining.contentType.toLowerCase().split("/")[1]; //format || sanitizeExtension(requestable || resolved);
+ const resolved = filename || generate(prefix, extension);
const information: ImageUploadInformation = {
clientAccessPath: clientPathToFile(Directory.images, resolved),
serverAccessPaths: {},
- exifData,
- contentSize,
- contentType,
+ ...remaining
};
const { pngs, jpgs } = AcceptibleMedia;
return new Promise<ImageUploadInformation>(async (resolve, reject) => {
@@ -220,32 +246,22 @@ export namespace DashUploadUtils {
await new Promise<void>(resolve => {
const filename = InjectSize(resolved, suffix);
information.serverAccessPaths[suffix] = serverPathToFile(Directory.images, filename);
- stream(normalizedUrl).pipe(resizer).pipe(fs.createWriteStream(serverPathToFile(Directory.images, filename)))
+ request(requestable).pipe(resizer).pipe(createWriteStream(serverPathToFile(Directory.images, filename)))
.on('close', resolve)
.on('error', reject);
});
}
- if (isLocal) {
- await new Promise<boolean>(resolve => {
- fs.unlink(normalizedUrl, error => resolve(error === null));
- });
+ if (isLocal().test(source)) {
+ unlinkSync(source);
}
resolve(information);
});
};
- const classify = (url: string) => {
- const isLocal = /Dash-Web(\\|\/)src(\\|\/)server(\\|\/)public(\\|\/)files/g.test(url);
- return {
- isLocal,
- stream: isLocal ? fs.createReadStream : request,
- normalized: isLocal ? path.normalize(url) : url
- };
- };
-
const parseExifData = async (source: string): Promise<EnrichedExifData> => {
+ const image = await request.get(source, { encoding: null });
return new Promise<EnrichedExifData>(resolve => {
- new ExifImage(source, (error, data) => {
+ new ExifImage({ image }, (error, data) => {
let reason: Opt<string> = undefined;
if (error) {
reason = (error as any).code;
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index b07aef74d..63e957cd1 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,6 +1,6 @@
import RouteSubscriber from "./RouteSubscriber";
import { DashUserModel } from "./authentication/models/user_model";
-import * as express from 'express';
+import { Request, Response, Express } from 'express';
import { cyan, red, green } from 'colors';
export enum Method {
@@ -9,8 +9,8 @@ export enum Method {
}
export interface CoreArguments {
- req: express.Request;
- res: express.Response;
+ req: Request;
+ res: Response;
isRelease: boolean;
}
@@ -35,7 +35,7 @@ enum RegistrationError {
}
export default class RouteManager {
- private server: express.Express;
+ private server: Express;
private _isRelease: boolean;
private failedRegistrations: { route: string, reason: RegistrationError }[] = [];
@@ -43,7 +43,7 @@ export default class RouteManager {
return this._isRelease;
}
- constructor(server: express.Express, isRelease: boolean) {
+ constructor(server: Express, isRelease: boolean) {
this.server = server;
this._isRelease = isRelease;
}
@@ -83,9 +83,10 @@ export default class RouteManager {
* @param initializer
*/
addSupervisedRoute = (initializer: RouteInitializer): void => {
- const { method, subscription, secureHandler: onValidation, publicHandler: onUnauthenticated, errorHandler: onError } = initializer;
+ const { method, subscription, secureHandler, publicHandler, errorHandler } = initializer;
+
const isRelease = this._isRelease;
- const supervised = async (req: express.Request, res: express.Response) => {
+ const supervised = async (req: Request, res: Response) => {
let { user } = req;
const { originalUrl: target } = req;
if (process.env.DB === "MEM" && !user) {
@@ -97,19 +98,19 @@ export default class RouteManager {
await toExecute(args);
} catch (e) {
console.log(red(target), user && ("email" in user) ? "<user logged out>" : undefined);
- if (onError) {
- onError({ ...core, error: e });
+ if (errorHandler) {
+ errorHandler({ ...core, error: e });
} else {
_error(res, `The server encountered an internal error when serving ${target}.`, e);
}
}
};
if (user) {
- await tryExecute(onValidation, { ...core, user });
+ await tryExecute(secureHandler, { ...core, user });
} else {
req.session!.target = target;
- if (onUnauthenticated) {
- await tryExecute(onUnauthenticated, core);
+ if (publicHandler) {
+ await tryExecute(publicHandler, core);
if (!res.headersSent) {
res.redirect("/login");
}
@@ -178,22 +179,22 @@ export const STATUS = {
PERMISSION_DENIED: 403
};
-export function _error(res: express.Response, message: string, error?: any) {
- console.error(message);
+export function _error(res: Response, message: string, error?: any) {
+ console.error(message, error);
res.statusMessage = message;
res.status(STATUS.EXECUTION_ERROR).send(error);
}
-export function _success(res: express.Response, body: any) {
+export function _success(res: Response, body: any) {
res.status(STATUS.OK).send(body);
}
-export function _invalid(res: express.Response, message: string) {
+export function _invalid(res: Response, message: string) {
res.statusMessage = message;
res.status(STATUS.BAD_REQUEST).send();
}
-export function _permission_denied(res: express.Response, message?: string) {
+export function _permission_denied(res: Response, message?: string) {
if (message) {
res.statusMessage = message;
}
diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts
index 9a6ed8221..b4cd2dbe2 100644
--- a/src/server/Websocket/Websocket.ts
+++ b/src/server/Websocket/Websocket.ts
@@ -30,7 +30,7 @@ export namespace WebSocket {
async function preliminaryFunctions() {
}
function initialize(isRelease: boolean) {
- endpoint = io();
+ const endpoint = io();
endpoint.on("connection", function (socket: Socket) {
_socket = socket;
@@ -186,6 +186,11 @@ export namespace WebSocket {
await Database.Instance.deleteAll('newDocuments');
}
+ // export async function deleteUserDocuments() {
+ // await Database.Instance.deleteAll();
+ // await Database.Instance.deleteAll('newDocuments');
+ // }
+
export async function deleteAll() {
await Database.Instance.deleteAll();
await Database.Instance.deleteAll('newDocuments');
@@ -216,8 +221,6 @@ export namespace WebSocket {
socket.broadcast.emit(MessageStore.SetField.Message, newValue));
if (newValue.type === Types.Text) {
Search.updateDocument({ id: newValue.id, data: (newValue as any).data });
- console.log("set field");
- console.log("checking in");
}
}
diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts
index cc24cce71..db20d10f2 100644
--- a/src/server/authentication/models/current_user_utils.ts
+++ b/src/server/authentication/models/current_user_utils.ts
@@ -14,6 +14,10 @@ import { Utils } from "../../../Utils";
import { nullAudio } from "../../../new_fields/URLField";
import { DragManager } from "../../../client/util/DragManager";
import { InkingControl } from "../../../client/views/InkingControl";
+import { CollectionViewType } from "../../../client/views/collections/CollectionView";
+import { makeTemplate } from "../../../client/util/DropConverter";
+import { RichTextField } from "../../../new_fields/RichTextField";
+import { PrefetchProxy } from "../../../new_fields/Proxy";
export class CurrentUserUtils {
private static curr_id: string;
@@ -28,42 +32,48 @@ export class CurrentUserUtils {
@observable public static GuestTarget: Doc | undefined;
@observable public static GuestWorkspace: Doc | undefined;
+ @observable public static GuestMobile: Doc | undefined;
// a default set of note types .. not being used yet...
static setupNoteTypes(doc: Doc) {
return [
- Docs.Create.TextDocument({ title: "Note", backgroundColor: "yellow", isTemplateDoc: true }),
- Docs.Create.TextDocument({ title: "Idea", backgroundColor: "pink", isTemplateDoc: true }),
- Docs.Create.TextDocument({ title: "Topic", backgroundColor: "lightBlue", isTemplateDoc: true }),
- Docs.Create.TextDocument({ title: "Person", backgroundColor: "lightGreen", isTemplateDoc: true }),
- Docs.Create.TextDocument({ title: "Todo", backgroundColor: "orange", isTemplateDoc: true })
+ Docs.Create.TextDocument("", { title: "Note", backgroundColor: "yellow", isTemplateDoc: true }),
+ Docs.Create.TextDocument("", { title: "Idea", backgroundColor: "pink", isTemplateDoc: true }),
+ Docs.Create.TextDocument("", { title: "Topic", backgroundColor: "lightBlue", isTemplateDoc: true }),
+ Docs.Create.TextDocument("", { title: "Person", backgroundColor: "lightGreen", isTemplateDoc: true }),
+ Docs.Create.TextDocument("", { title: "Todo", backgroundColor: "orange", isTemplateDoc: true })
];
}
// setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
static setupCreatorButtons(doc: Doc, buttons?: string[]) {
const notes = CurrentUserUtils.setupNoteTypes(doc);
- doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", height: 75 });
+ const emptyPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, showTitle: "title", boxShadow: "0 0" });
+ const emptyCollection = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _LODdisable: true, _width: 150, _height: 100, title: "freeform" });
+ doc.noteTypes = Docs.Create.TreeDocument(notes, { title: "Note Types", _height: 75 });
doc.activePen = doc;
const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
- { title: "collection", icon: "folder", ignoreClick: true, drag: 'Docs.Create.FreeformDocument([], { nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: "freeform" })' },
- { title: "preview", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,true,[_last_])?.[0]"), { width: 250, height: 250, title: "container" })' },
+ { title: "collection", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: emptyCollection },
+ { title: "preview", icon: "expand", ignoreClick: true, drag: 'Docs.Create.DocumentDocument(ComputedField.MakeFunction("selectedDocs(this,true,[_last_])?.[0]"), { _width: 250, _height: 250, title: "container" })' },
{ title: "todo item", icon: "check", ignoreClick: true, drag: 'getCopy(this.dragFactory, true)', dragFactory: notes[notes.length - 1] },
- { title: "web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", { width: 300, height: 300, title: "New Webpage" })' },
- { title: "cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { width: 200, title: "an image of a cat" })' },
+ { title: "web page", icon: "globe-asia", ignoreClick: true, drag: 'Docs.Create.WebDocument("https://en.wikipedia.org/wiki/Hedgehog", {_width: 300, _height: 300, title: "New Webpage" })' },
+ { title: "cat image", icon: "cat", ignoreClick: true, drag: 'Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 200, title: "an image of a cat" })' },
{ title: "webcam", icon: "video", ignoreClick: true, drag: 'Docs.Create.WebCamDocument("", { width: 400, height: 400, title: "a test cam" })' },
- { title: "record", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { width: 200, title: "ready to record audio" })` },
- { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' },
- { title: "presentation", icon: "tv", ignoreClick: true, drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List<Doc>(), { width: 200, height: 500, title: "a presentation trail" })' },
- { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' },
+ { title: "buxton", icon: "faObjectGroup", ignoreClick: true, drag: "Docs.Create.Buxton()" },
+ { title: "record", icon: "microphone", ignoreClick: true, drag: `Docs.Create.AudioDocument("${nullAudio}", { _width: 200, title: "ready to record audio" })` },
+ { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: "Button" })' },
+ { title: "presentation", icon: "tv", click: 'openOnRight(Doc.UserDoc().curPresentation = getCopy(this.dragFactory, true))', drag: `Doc.UserDoc().curPresentation = getCopy(this.dragFactory,true)`, dragFactory: emptyPresentation },
+ { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", _width: 400, _height: 400 })' },
+ { title: "mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' },
{ title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
{ title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
{ title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
{ title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc },
{ title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
];
return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({
- nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen,
backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
@@ -91,22 +101,73 @@ export class CurrentUserUtils {
}
}
+ static setupMobileButtons(doc: Doc, buttons?: string[]) {
+ const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+ { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" },
+ { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc },
+ { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc },
+ { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc },
+ ];
+ return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({
+ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
+ onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined,
+ ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen,
+ backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
+ }));
+ }
+
+ static setupThumbButtons(doc: Doc) {
+ const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
+ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc },
+ ];
+ return docProtoData.map(data => Docs.Create.FontIconDocument({
+ _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick,
+ onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+ clipboard: data.clipboard,
+ onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+ ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true,
+ backgroundColor: data.backgroundColor, removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory,
+ }));
+ }
+
+ static setupThumbDoc(userDoc: Doc) {
+ if (!userDoc.thumbDoc) {
+ userDoc.thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
+ _width: 100, _height: 50, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5, isExpanded: true, backgroundColor: "white"
+ });
+ }
+ return userDoc.thumbDoc;
+ }
+
+ static setupMobileDoc(userDoc: Doc) {
+ return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), {
+ columnWidth: 100, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons", _autoHeight: true, _yMargin: 5
+ });
+ }
+
// setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
static setupToolsPanel(sidebarContainer: Doc, doc: Doc) {
// setup a masonry view of all he creators
const dragCreators = Docs.Create.MasonryDocument(CurrentUserUtils.setupCreatorButtons(doc), {
- width: 500, autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons",
- dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), yMargin: 5
+ _width: 500, _autoHeight: true, columnWidth: 35, ignoreClick: true, lockedPosition: true, _chromeStatus: "disabled", title: "buttons",
+ dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _yMargin: 5
});
// setup a color picker
const color = Docs.Create.ColorDocument({
- title: "color picker", width: 400, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
+ title: "color picker", _width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List<string>(["dropAction", "forceActive"])
});
return Docs.Create.ButtonDocument({
- width: 35, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Tools", fontSize: 10, targetContainer: sidebarContainer,
+ _width: 35, _height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Tools", fontSize: 10, targetContainer: sidebarContainer,
+ letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
sourcePanel: Docs.Create.StackingDocument([dragCreators, color], {
- width: 500, height: 800, lockedPosition: true, chromeStatus: "disabled", title: "tools stack"
+ _width: 500, lockedPosition: true, _chromeStatus: "disabled", title: "tools stack"
}),
onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"),
});
@@ -116,22 +177,23 @@ export class CurrentUserUtils {
static setupLibraryPanel(sidebarContainer: Doc, doc: Doc) {
// setup workspaces library item
doc.workspaces = Docs.Create.TreeDocument([], {
- title: "WORKSPACES", height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, backgroundColor: "#eeeeee"
+ title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, backgroundColor: "#eeeeee"
});
doc.documents = Docs.Create.TreeDocument([], {
- title: "DOCUMENTS", height: 42, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee"
+ title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee"
});
// setup Recently Closed library item
doc.recentlyClosed = Docs.Create.TreeDocument([], {
- title: "RECENTLY CLOSED", height: 75, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee"
+ title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", preventTreeViewOpen: true, lockedPosition: true, backgroundColor: "#eeeeee"
});
return Docs.Create.ButtonDocument({
- width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Library", fontSize: 10,
+ _width: 50, _height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Library", fontSize: 10,
+ letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc], {
- title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true
+ title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true, boxShadow: "0 0",
}),
targetContainer: sidebarContainer,
onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;")
@@ -141,7 +203,8 @@ export class CurrentUserUtils {
// setup the Search button which will display the search panel.
static setupSearchPanel(sidebarContainer: Doc) {
return Docs.Create.ButtonDocument({
- width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Search", fontSize: 10,
+ _width: 50, _height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Search", fontSize: 10,
+ letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)",
sourcePanel: Docs.Create.QueryDocument({
title: "search stack", ignoreClick: true
}),
@@ -154,7 +217,7 @@ export class CurrentUserUtils {
// setup the list of sidebar mode buttons which determine what is displayed in the sidebar
static setupSidebarButtons(doc: Doc) {
doc.sidebarContainer = new Doc();
- (doc.sidebarContainer as Doc).chromeStatus = "disabled";
+ (doc.sidebarContainer as Doc)._chromeStatus = "disabled";
(doc.sidebarContainer as Doc).onClick = ScriptField.MakeScript("freezeSidebar()");
doc.ToolsBtn = this.setupToolsPanel(doc.sidebarContainer as Doc, doc);
@@ -163,21 +226,37 @@ export class CurrentUserUtils {
// Finally, setup the list of buttons to display in the sidebar
doc.sidebarButtons = Docs.Create.StackingDocument([doc.SearchBtn as Doc, doc.LibraryBtn as Doc, doc.ToolsBtn as Doc], {
- width: 500, height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true,
- backgroundColor: "lightgrey", chromeStatus: "disabled", title: "library stack"
+ _width: 500, _height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true,
+ backgroundColor: "rgb(100, 100, 100)", _chromeStatus: "disabled", title: "library stack",
+ _yMargin: 10,
});
}
/// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
static setupExpandingButtons(doc: Doc) {
+ const slideTemplate = Docs.Create.StackingDocument(
+ [
+ Docs.Create.MulticolumnDocument([], { title: "images", _height: 200, _xMargin: 10, _yMargin: 10 }),
+ Docs.Create.TextDocument("", { title: "contents", _height: 100 })
+ ],
+ { _width: 400, _height: 300, title: "slide", _chromeStatus: "disabled", backgroundColor: "lightGray", _autoHeight: true });
+ slideTemplate.isTemplateDoc = makeTemplate(slideTemplate);
+
+ const iconDoc = Docs.Create.TextDocument("", { title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onClick: ScriptField.MakeScript("setNativeView(this)") });
+ Doc.GetProto(iconDoc).data = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', "");
+ doc.isTemplateDoc = makeTemplate(iconDoc);
+ doc.iconView = new PrefetchProxy(iconDoc);
+
doc.undoBtn = Docs.Create.FontIconDocument(
- { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("undo()"), removeDropProperties: new List<string>(["dropAction"]), title: "undo button", icon: "undo-alt" });
+ { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("undo()"), removeDropProperties: new List<string>(["dropAction"]), title: "undo button", icon: "undo-alt" });
doc.redoBtn = Docs.Create.FontIconDocument(
- { nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("redo()"), removeDropProperties: new List<string>(["dropAction"]), title: "redo button", icon: "redo-alt" });
-
- doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc], {
- title: "expanding buttons", gridGap: 5, xMargin: 5, yMargin: 5, height: 42, width: 100, boxShadow: "0 0",
+ { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onClick: ScriptField.MakeScript("redo()"), removeDropProperties: new List<string>(["dropAction"]), title: "redo button", icon: "redo-alt" });
+ doc.slidesBtn = Docs.Create.FontIconDocument(
+ { _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), dragFactory: slideTemplate, removeDropProperties: new List<string>(["dropAction"]), title: "presentation slide", icon: "sticky-note" });
+ doc.expandingButtons = Docs.Create.LinearDocument([doc.undoBtn as Doc, doc.redoBtn as Doc, doc.slidesBtn as Doc], {
+ title: "expanding buttons", _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0",
backgroundColor: "black", preventTreeViewOpen: true, forceActive: true, lockedPosition: true,
+
dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
});
}
@@ -185,13 +264,13 @@ export class CurrentUserUtils {
// sets up the default set of documents to be shown in the Overlay layer
static setupOverlays(doc: Doc) {
doc.overlays = Docs.Create.FreeformDocument([], { title: "Overlays", backgroundColor: "#aca3a6" });
- doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, width: 500, height: 370, title: "Link Follower" });
+ doc.linkFollowBox = Docs.Create.LinkFollowBoxDocument({ x: 250, y: 20, _width: 500, _height: 370, title: "Link Follower" });
Doc.AddDocToList(doc.overlays as Doc, "data", doc.linkFollowBox as Doc);
}
// the initial presentation Doc to use
static setupDefaultPresentation(doc: Doc) {
- doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", boxShadow: "0 0" });
+ doc.curPresentation = Docs.Create.PresDocument(new List<Doc>(), { title: "Presentation", _viewType: CollectionViewType.Stacking, showTitle: "title", boxShadow: "0 0" });
}
static setupMobileUploads(doc: Doc) {
@@ -229,6 +308,15 @@ export class CurrentUserUtils {
return doc;
}
+ public static IsDocPinned(doc: Doc) {
+ //add this new doc to props.Document
+ const curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc;
+ if (curPres) {
+ return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
+ }
+ return false;
+ }
+
public static async loadCurrentUser() {
return rp.get(Utils.prepend("/getCurrentUser")).then(response => {
if (response) {
diff --git a/src/server/updateSearch.ts b/src/server/updateSearch.ts
index 83094d36a..dd2067c87 100644
--- a/src/server/updateSearch.ts
+++ b/src/server/updateSearch.ts
@@ -107,11 +107,15 @@ async function update() {
color: cyan
});
try {
- const { status } = JSON.parse(result).responseHeader;
- console.log(status ? red(`Failed with status code (${status})`) : green("Success!"));
- } catch {
+ if (result) {
+ const { status } = JSON.parse(result).responseHeader;
+ console.log(status ? red(`Failed with status code (${status})`) : green("Success!"));
+ } else {
+ console.log(red("Solr is likely not running!"));
+ }
+ } catch (e) {
console.log(red("Error:"));
- console.log(result);
+ console.log(e);
console.log("\n");
}
await cursor?.close();