aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/linking/LinkFollowBox.tsx11
-rw-r--r--src/server/apis/google/GooglePhotosUploadUtils.ts176
-rw-r--r--src/server/index.ts4
3 files changed, 184 insertions, 7 deletions
diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx
index 13a341543..d5ed01f53 100644
--- a/src/client/views/linking/LinkFollowBox.tsx
+++ b/src/client/views/linking/LinkFollowBox.tsx
@@ -245,22 +245,23 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
let proto = Doc.GetProto(LinkFollowBox.linkDoc);
let targetContext = await Cast(proto.targetContext, Doc);
let sourceContext = await Cast(proto.sourceContext, Doc);
+ const shouldZoom = options ? options.shouldZoom : false;
let dockingFunc = (document: Doc) => { this._addDocTab && this._addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); };
if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, async document => dockingFunc(document), undefined, targetContext);
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext);
}
else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, document => dockingFunc(sourceContext!));
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!));
}
else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, undefined, undefined,
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined,
NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page)));
}
else {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, options.shouldZoom, false, dockingFunc);
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, dockingFunc);
}
this.highlightDoc();
@@ -326,7 +327,7 @@ export class LinkFollowBox extends React.Component<FieldViewProps> {
}
//set this to be the default link behavior, can be any of the above
- public defaultLinkBehavior: (options?: any) => void = this.openLinkTab;
+ public defaultLinkBehavior: (options?: any) => void = this.jumpToLink;
@action
currentLinkBehavior = () => {
diff --git a/src/server/apis/google/GooglePhotosUploadUtils.ts b/src/server/apis/google/GooglePhotosUploadUtils.ts
new file mode 100644
index 000000000..35f986250
--- /dev/null
+++ b/src/server/apis/google/GooglePhotosUploadUtils.ts
@@ -0,0 +1,176 @@
+import request = require('request-promise');
+import { GoogleApiServerUtils } from './GoogleApiServerUtils';
+import * as fs from 'fs';
+import { Utils } from '../../../Utils';
+import * as path from 'path';
+import { Opt } from '../../../new_fields/Doc';
+import * as sharp from 'sharp';
+
+const uploadDirectory = path.join(__dirname, "../../public/files/");
+
+export namespace GooglePhotosUploadUtils {
+
+ export interface Paths {
+ uploadDirectory: string;
+ credentialsPath: string;
+ tokenPath: string;
+ }
+
+ export interface MediaInput {
+ url: string;
+ description: string;
+ }
+
+ const prepend = (extension: string) => `https://photoslibrary.googleapis.com/v1/${extension}`;
+ const headers = (type: string) => ({
+ 'Content-Type': `application/${type}`,
+ 'Authorization': Bearer,
+ });
+
+ let Bearer: string;
+ let Paths: Paths;
+
+ export const initialize = async (paths: Paths) => {
+ Paths = paths;
+ const { tokenPath, credentialsPath } = paths;
+ const token = await GoogleApiServerUtils.RetrieveAccessToken({ tokenPath, credentialsPath });
+ Bearer = `Bearer ${token}`;
+ };
+
+ export const DispatchGooglePhotosUpload = async (url: string) => {
+ const body = await request(url, { encoding: null });
+ const parameters = {
+ method: 'POST',
+ headers: {
+ ...headers('octet-stream'),
+ 'X-Goog-Upload-File-Name': path.basename(url),
+ 'X-Goog-Upload-Protocol': 'raw'
+ },
+ uri: prepend('uploads'),
+ body
+ };
+ return new Promise<any>(resolve => request(parameters, (error, _response, body) => resolve(error ? undefined : body)));
+ };
+
+ export const CreateMediaItems = (newMediaItems: any[], album?: { id: string }) => {
+ return new Promise<any>((resolve, reject) => {
+ const parameters = {
+ method: 'POST',
+ headers: headers('json'),
+ uri: prepend('mediaItems:batchCreate'),
+ body: { newMediaItems } as any,
+ json: true
+ };
+ album && (parameters.body.albumId = album.id);
+ request(parameters, (error, _response, body) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(body);
+ }
+ });
+ });
+ };
+
+}
+
+export namespace DownloadUtils {
+
+ export interface Size {
+ width: number;
+ suffix: string;
+ }
+
+ export const Sizes: { [size: string]: Size } = {
+ SMALL: { width: 100, suffix: "_s" },
+ MEDIUM: { width: 400, suffix: "_m" },
+ LARGE: { width: 900, suffix: "_l" },
+ };
+
+ const png = ".png";
+ const pngs = [".png", ".PNG"];
+ const jpgs = [".jpg", ".JPG", ".jpeg", ".JPEG"];
+ const formats = [".jpg", ".png", ".gif"];
+ const size = "content-length";
+ const type = "content-type";
+
+ export interface UploadInformation {
+ mediaPaths: string[];
+ fileNames: { [key: string]: string };
+ contentSize?: number;
+ contentType?: string;
+ }
+
+ const generate = (prefix: string, url: string) => `${prefix}upload_${Utils.GenerateGuid()}${path.extname(url).toLowerCase()}`;
+ const sanitize = (filename: string) => filename.replace(/\s+/g, "_");
+
+ export const UploadImage = async (url: string, filename?: string, prefix = ""): Promise<Opt<UploadInformation>> => {
+ const resolved = filename ? sanitize(filename) : generate(prefix, url);
+ const extension = path.extname(url) || path.extname(resolved) || png;
+ let information: UploadInformation = {
+ mediaPaths: [],
+ fileNames: { clean: resolved }
+ };
+ const { isLocal, stream, normalized } = classify(url);
+ url = normalized;
+ if (!isLocal) {
+ const metadata = (await new Promise<any>((resolve, reject) => {
+ request.head(url, async (error, res) => {
+ if (error) {
+ return reject(error);
+ }
+ resolve(res);
+ });
+ })).headers;
+ information.contentSize = parseInt(metadata[size]);
+ information.contentType = metadata[type];
+ }
+ return new Promise<UploadInformation>(async (resolve, reject) => {
+ const resizers = [
+ { resizer: sharp().rotate(), suffix: "_o" },
+ ...Object.values(Sizes).map(size => ({
+ resizer: sharp().resize(size.width, undefined, { withoutEnlargement: true }).rotate(),
+ suffix: size.suffix
+ }))
+ ];
+ if (pngs.includes(extension)) {
+ resizers.forEach(element => element.resizer = element.resizer.png());
+ } else if (jpgs.includes(extension)) {
+ resizers.forEach(element => element.resizer = element.resizer.jpeg());
+ } else if (!formats.includes(extension.toLowerCase())) {
+ return reject();
+ }
+ for (let resizer of resizers) {
+ const suffix = resizer.suffix;
+ let mediaPath: string;
+ await new Promise<void>(resolve => {
+ const filename = resolved.substring(0, resolved.length - extension.length) + suffix + extension;
+ information.mediaPaths.push(mediaPath = uploadDirectory + filename);
+ information.fileNames[suffix] = filename;
+ stream(url).pipe(resizer.resizer).pipe(fs.createWriteStream(mediaPath))
+ .on('close', resolve)
+ .on('error', reject);
+ });
+ }
+ 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
+ };
+ };
+
+ export const createIfNotExists = async (path: string) => {
+ if (await new Promise<boolean>(resolve => fs.exists(path, resolve))) {
+ return true;
+ }
+ return new Promise<boolean>(resolve => fs.mkdir(path, error => resolve(error === null)));
+ };
+
+ export const Destroy = (mediaPath: string) => new Promise<boolean>(resolve => fs.unlink(mediaPath, error => resolve(error === null)));
+} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 6472ffb93..082e9422d 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -811,8 +811,8 @@ const EndpointHandlerMap = new Map<GoogleApiServerUtils.Action, GoogleApiServerU
]);
app.post(RouteStore.googleDocs + "/:sector/:action", (req, res) => {
- let sector: any = req.params.sector;
- let action: any = req.params.action;
+ let sector: GoogleApiServerUtils.Service = req.params.sector;
+ let action: GoogleApiServerUtils.Action = req.params.action;
GoogleApiServerUtils.GetEndpoint(GoogleApiServerUtils.Service[sector], { credentials, token }).then(endpoint => {
let handler = EndpointHandlerMap.get(action);
if (endpoint && handler) {