Files
standardnotes-app-web/app/assets/javascripts/services/archiveManager.js
2020-04-10 08:42:35 -05:00

150 lines
4.2 KiB
JavaScript

import { EncryptionIntents, ProtectedAction } from 'snjs';
export class ArchiveManager {
constructor(application) {
this.application = application;
}
/** @public */
async downloadBackup(encrypted) {
return this.downloadBackupOfItems(this.application.modelManager.allItems, encrypted);
}
/** @public */
async downloadBackupOfItems(items, encrypted) {
const run = async () => {
// download in Standard Notes format
const intent = encrypted
? EncryptionIntents.FileEncrypted
: EncryptionIntents.FileDecrypted;
this.itemsData(items, intent).then((data) => {
const modifier = encrypted ? "Encrypted" : "Decrypted";
this.downloadData(
data,
`Standard Notes ${modifier} Backup - ${this.formattedDate()}.txt`
);
// download as zipped plain text files
if (!encrypted) {
this.downloadZippedItems(items);
}
});
};
if (await this.application.privilegesService.actionRequiresPrivilege(ProtectedAction.ManageBackups)) {
this.application.presentPrivilegesModal(ProtectedAction.ManageBackups, () => {
run();
});
} else {
run();
}
}
/** @private */
formattedDate() {
var string = `${new Date()}`;
// Match up to the first parenthesis, i.e do not include '(Central Standard Time)'
var matches = string.match(/^(.*?) \(/);
if (matches.length >= 2) {
return matches[1];
}
return string;
}
/** @private */
async itemsData(items, intent) {
const data = await this.application.createBackupFile({
subItems: items,
intent: intent
});
const blobData = new Blob([data], { type: 'text/json' });
return blobData;
}
/** @private */
async loadZip() {
if (window.zip) {
return;
}
var scriptTag = document.createElement('script');
scriptTag.src = "/assets/zip/zip.js";
scriptTag.async = false;
var headTag = document.getElementsByTagName('head')[0];
headTag.appendChild(scriptTag);
return new Promise((resolve, reject) => {
scriptTag.onload = function () {
zip.workerScriptsPath = "assets/zip/";
resolve();
};
});
}
/** @private */
async downloadZippedItems(items) {
await this.loadZip();
zip.createWriter(new zip.BlobWriter("application/zip"), (zipWriter) => {
var index = 0;
const nextFile = () => {
var item = items[index];
var name, contents;
if (item.content_type === "Note") {
name = item.content.title;
contents = item.content.text;
} else {
name = item.content_type;
contents = JSON.stringify(item.content, null, 2);
}
if (!name) {
name = "";
}
const blob = new Blob([contents], { type: 'text/plain' });
let filePrefix = name.replace(/\//g, "").replace(/\\+/g, "");
const fileSuffix = `-${item.uuid.split("-")[0]}.txt`;
// Standard max filename length is 255. Slice the note name down to allow filenameEnd
filePrefix = filePrefix.slice(0, (255 - fileSuffix.length));
const fileName = `${item.content_type}/${filePrefix}${fileSuffix}`;
zipWriter.add(fileName, new zip.BlobReader(blob), () => {
index++;
if (index < items.length) {
nextFile();
} else {
zipWriter.close((blob) => {
this.downloadData(blob, `Standard Notes Backup - ${this.formattedDate()}.zip`);
zipWriter = null;
});
}
});
};
nextFile();
}, onerror);
}
/** @private */
hrefForData(data) {
// If we are replacing a previously generated file we need to
// manually revoke the object URL to avoid memory leaks.
if (this.textFile !== null) {
window.URL.revokeObjectURL(this.textFile);
}
this.textFile = window.URL.createObjectURL(data);
// returns a URL you can use as a href
return this.textFile;
}
/** @private */
downloadData(data, fileName) {
var link = document.createElement('a');
link.setAttribute('download', fileName);
link.href = this.hrefForData(data);
document.body.appendChild(link);
link.click();
link.remove();
}
}