Switch back to SingleFile from SingleFileZ (#1904)

Our SingleFileZ integration would save images inside directories following the
SingleFileZ format. However, Zotero does not support syncing sub-directories of
attachments. This commit switch back to a single HTML file with base64 encoded
resources. We think that the 33% increase in resources will be offset by the
compression of HTML and removal of JavaScript and unused CSS.

This commit does not fix past snapshots that were saved using SingleFileZ.
This commit is contained in:
fletcherhaz 2020-10-23 17:39:07 -06:00 committed by GitHub
parent 3a684308cd
commit 76ae5d9f59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 404 additions and 418 deletions

View file

@ -772,8 +772,7 @@ describe("Connector Server", function () {
connectorServerPath + "/connector/saveSnapshot",
{
headers: {
"Content-Type": "application/json",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
@ -792,33 +791,16 @@ describe("Connector Server", function () {
// Promise for attachment save
promise = waitForItemEvent('add');
let body = new FormData();
let uuid = 'binary-' + Zotero.Utilities.randomString();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: await Zotero.File.getContentsAsync(indexPath),
resources: {
images: [
{
name: "img.gif",
content: uuid,
binary: true
}
]
}
}
})));
let imagePath = OS.Path.join(testDataDirectory, 'snapshot', 'img.gif');
body.append(uuid, await File.createFromFileName(imagePath));
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: await Zotero.File.getContentsAsync(indexPath)
}));
await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body
}
@ -840,13 +822,6 @@ describe("Connector Server", function () {
let contents = await Zotero.File.getContentsAsync(path);
let expectedContents = await Zotero.File.getContentsAsync(indexPath);
assert.equal(contents, expectedContents);
// Check attachment binary file
path = OS.Path.join(attachmentDirectory, 'img.gif');
assert.isTrue(await OS.File.exists(path));
contents = await Zotero.File.getBinaryContentsAsync(path);
expectedContents = await Zotero.File.getBinaryContentsAsync(imagePath);
assert.equal(contents, expectedContents);
});
it("should save a webpage item with /saveItems", async function () {
@ -907,6 +882,88 @@ describe("Connector Server", function () {
let testDataDirectory = getTestDataDirectory().path;
let indexPath = OS.Path.join(testDataDirectory, 'snapshot', 'index.html');
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: await Zotero.File.getContentsAsync(indexPath)
}));
req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "application/json"
},
body
}
);
assert.equal(req.status, 201);
// Await attachment save
let attachmentIDs = await promise;
// Check attachment
assert.lengthOf(attachmentIDs, 1);
item = Zotero.Items.get(attachmentIDs[0]);
assert.isTrue(item.isImportedAttachment());
assert.equal(item.getField('title'), 'Snapshot');
// Check attachment html file
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
let path = OS.Path.join(attachmentDirectory, 'attachment.html');
assert.isTrue(await OS.File.exists(path));
let contents = await Zotero.File.getContentsAsync(path);
let expectedContents = await Zotero.File.getContentsAsync(indexPath);
assert.equal(contents, expectedContents);
});
it("should override SingleFileZ from old connector in /saveSnapshot", async function () {
Components.utils.import("resource://gre/modules/FileUtils.jsm");
var collection = await createDataObject('collection');
await waitForItemsLoad(win);
// Promise for item save
let promise = waitForItemEvent('add');
let testDataDirectory = getTestDataDirectory().path;
let indexPath = OS.Path.join(testDataDirectory, 'snapshot', 'index.html');
let prefix = '/' + Zotero.Utilities.randomString() + '/';
let uri = OS.Path.join(getTestDataDirectory().path, 'snapshot');
httpd.registerDirectory(prefix, new FileUtils.File(uri));
let title = Zotero.Utilities.randomString();
let sessionID = Zotero.Utilities.randomString();
let payload = {
sessionID,
url: testServerPath + prefix + 'index.html',
title,
singleFile: true
};
await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSnapshot",
{
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
// Await item save
let parentIDs = await promise;
// Check parent item
assert.lengthOf(parentIDs, 1);
var item = Zotero.Items.get(parentIDs[0]);
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'webpage');
assert.isTrue(collection.hasItem(item.id));
assert.equal(item.getField('title'), title);
// Promise for attachment save
promise = waitForItemEvent('add');
let body = new FormData();
let uuid = 'binary-' + Zotero.Utilities.randomString();
body.append("payload", JSON.stringify(Object.assign(payload, {
@ -924,8 +981,113 @@ describe("Connector Server", function () {
}
})));
let imagePath = OS.Path.join(testDataDirectory, 'snapshot', 'img.gif');
body.append(uuid, await File.createFromFileName(imagePath));
await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
},
body
}
);
// Await attachment save
let attachmentIDs = await promise;
// Check attachment
assert.lengthOf(attachmentIDs, 1);
item = Zotero.Items.get(attachmentIDs[0]);
assert.isTrue(item.isImportedAttachment());
assert.equal(item.getField('title'), title);
// Check attachment html file
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
assert.isTrue(await OS.File.exists(path));
let contents = await Zotero.File.getContentsAsync(path);
assert.match(contents, /^<html style><!--\n Page saved with SingleFile \n url:/);
});
it("should override SingleFileZ from old connector in /saveItems", async function () {
let collection = await createDataObject('collection');
await waitForItemsLoad(win);
let prefix = '/' + Zotero.Utilities.randomString() + '/';
let uri = OS.Path.join(getTestDataDirectory().path, 'snapshot');
httpd.registerDirectory(prefix, new FileUtils.File(uri));
let title = Zotero.Utilities.randomString();
let sessionID = Zotero.Utilities.randomString();
let payload = {
sessionID: sessionID,
items: [
{
itemType: "newspaperArticle",
title: title,
creators: [
{
firstName: "First",
lastName: "Last",
creatorType: "author"
}
],
attachments: [
{
title: "Snapshot",
url: testServerPath + prefix + 'index.html',
mimeType: "text/html",
singleFile: true
}
]
}
],
uri: "http://example.com"
};
let promise = waitForItemEvent('add');
let req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveItems",
{
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
assert.equal(req.status, 201);
// Check parent item
let itemIDs = await promise;
assert.lengthOf(itemIDs, 1);
let item = Zotero.Items.get(itemIDs[0]);
assert.equal(Zotero.ItemTypes.getName(item.itemTypeID), 'newspaperArticle');
assert.isTrue(collection.hasItem(item.id));
// Promise for attachment save
promise = waitForItemEvent('add');
let testDataDirectory = getTestDataDirectory().path;
let indexPath = OS.Path.join(testDataDirectory, 'snapshot', 'index.html');
let body = new FormData();
let uuid = 'binary-' + Zotero.Utilities.randomString();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: 'Foobar content',
resources: {
images: [
{
name: "img.gif",
content: uuid,
binary: true
}
]
}
}
})));
req = await Zotero.HTTP.request(
'POST',
@ -951,18 +1113,10 @@ describe("Connector Server", function () {
// Check attachment html file
let attachmentDirectory = Zotero.Attachments.getStorageDirectory(item).path;
let path = OS.Path.join(attachmentDirectory, 'attachment.html');
let path = OS.Path.join(attachmentDirectory, item.attachmentFilename);
assert.isTrue(await OS.File.exists(path));
let contents = await Zotero.File.getContentsAsync(path);
let expectedContents = await Zotero.File.getContentsAsync(indexPath);
assert.equal(contents, expectedContents);
// Check attachment binary file
path = OS.Path.join(attachmentDirectory, 'img.gif');
assert.isTrue(await OS.File.exists(path));
contents = await Zotero.File.getBinaryContentsAsync(path);
expectedContents = await Zotero.File.getBinaryContentsAsync(imagePath);
assert.equal(contents, expectedContents);
assert.match(contents, /^<html style><!--\n Page saved with SingleFile \n url:/);
});
});
@ -1573,28 +1727,22 @@ describe("Connector Server", function () {
connectorServerPath + "/connector/saveSnapshot",
{
headers: {
"Content-Type": "application/json",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
let body = new FormData();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: '<html><head><title>Title</title><body>Body',
resources: {}
}
})));
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: '<html><head><title>Title</title><body>Body'
}));
let req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body
}
@ -1701,8 +1849,7 @@ describe("Connector Server", function () {
connectorServerPath + "/connector/saveSnapshot",
{
headers: {
"Content-Type": "application/json",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
@ -1739,21 +1886,16 @@ describe("Connector Server", function () {
assert.equal(item2.libraryID, group.libraryID);
assert.equal(item2.numAttachments(), 0);
let body = new FormData();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: '<html><head><title>Title</title><body>Body',
resources: {}
}
})));
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: '<html><head><title>Title</title><body>Body'
}));
req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body
}
@ -1854,28 +1996,22 @@ describe("Connector Server", function () {
connectorServerPath + "/connector/saveItems",
{
headers: {
"Content-Type": "application/json",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
);
let body = new FormData();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: '<html><head><title>Title</title><body>Body',
resources: {}
}
})));
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: '<html><head><title>Title</title><body>Body'
}));
let req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body
}
@ -2001,8 +2137,7 @@ describe("Connector Server", function () {
connectorServerPath + "/connector/saveItems",
{
headers: {
"Content-Type": "application/json",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
}
@ -2039,21 +2174,16 @@ describe("Connector Server", function () {
assert.equal(item2.libraryID, group.libraryID);
assert.equal(item2.numAttachments(), 0);
let body = new FormData();
body.append("payload", JSON.stringify(Object.assign(payload, {
pageData: {
content: '<html><head><title>Title</title><body>Body',
resources: {}
}
})));
let body = JSON.stringify(Object.assign(payload, {
snapshotContent: '<html><head><title>Title</title><body>Body'
}));
req = await Zotero.HTTP.request(
'POST',
connectorServerPath + "/connector/saveSingleFile",
{
headers: {
"Content-Type": "multipart/form-data",
"zotero-allowed-request": "true"
"Content-Type": "application/json"
},
body
}