Switch to cache files for image annotations
Instead of embedded-image attachments Create with Zotero.Annotations.saveCacheImage({ libraryID, key }, blob)
This commit is contained in:
parent
8c464a7a8b
commit
226fc90308
4 changed files with 108 additions and 34 deletions
|
@ -32,8 +32,75 @@ Zotero.Annotations = new function () {
|
||||||
Zotero.defineProperty(this, 'ANNOTATION_TYPE_IMAGE', { value: 3 });
|
Zotero.defineProperty(this, 'ANNOTATION_TYPE_IMAGE', { value: 3 });
|
||||||
|
|
||||||
|
|
||||||
|
this.getCacheImagePath = function ({ libraryID, key }) {
|
||||||
|
var file = this._getLibraryCacheDirectory(libraryID);
|
||||||
|
return OS.Path.join(file, key + '.png');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.saveCacheImage = async function ({ libraryID, key }, blob) {
|
||||||
|
var item = await Zotero.Items.getByLibraryAndKey(libraryID, key);
|
||||||
|
if (!item) {
|
||||||
|
throw new Error(`Item not found`);
|
||||||
|
}
|
||||||
|
if (item.itemType != 'annotation' || item.annotationType != 'image') {
|
||||||
|
throw new Error("Item must be an image annotation item");
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheDir = Zotero.DataDirectory.getSubdirectory('cache', true);
|
||||||
|
var file = this._getLibraryCacheDirectory(item.libraryID);
|
||||||
|
await Zotero.File.createDirectoryIfMissingAsync(file, { from: cacheDir });
|
||||||
|
|
||||||
|
file = OS.Path.join(file, item.key + '.png');
|
||||||
|
await Zotero.File.putContentsAsync(file, blob);
|
||||||
|
await Zotero.File.setNormalFilePermissions(file);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.removeCacheImage = async function ({ libraryID, key }) {
|
||||||
|
var path = this.getCacheImagePath({ libraryID, key });
|
||||||
|
await OS.File.remove(path, { ignoreAbsent: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove cache files that are no longer in use
|
||||||
|
*/
|
||||||
|
this.removeOrphanedCacheFiles = async function () {
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all cache files for a given library
|
||||||
|
*/
|
||||||
|
this.removeLibraryCacheFiles = async function (libraryID) {
|
||||||
|
var path = this._getLibraryCacheDirectory(libraryID);
|
||||||
|
await OS.File.removeDir(path, { ignoreAbsent: true, ignorePermissions: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this._getLibraryCacheDirectory = function (libraryID) {
|
||||||
|
var parts = [Zotero.DataDirectory.getSubdirectory('cache')];
|
||||||
|
var library = Zotero.Libraries.get(libraryID);
|
||||||
|
if (library.libraryType == 'user') {
|
||||||
|
parts.push('library');
|
||||||
|
}
|
||||||
|
else if (library.libraryType == 'group') {
|
||||||
|
parts.push('groups', library.groupID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error(`Unexpected library type '${library.libraryType}'`);
|
||||||
|
}
|
||||||
|
return OS.Path.join(...parts);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.toJSON = async function (item) {
|
this.toJSON = async function (item) {
|
||||||
var o = {};
|
var o = {};
|
||||||
|
o.libraryID = item.libraryID;
|
||||||
o.key = item.key;
|
o.key = item.key;
|
||||||
o.type = item.annotationType;
|
o.type = item.annotationType;
|
||||||
o.isAuthor = !item.createdByUserID || item.createdByUserID == Zotero.Users.getCurrentUserID();
|
o.isAuthor = !item.createdByUserID || item.createdByUserID == Zotero.Users.getCurrentUserID();
|
||||||
|
@ -44,12 +111,9 @@ Zotero.Annotations = new function () {
|
||||||
o.text = item.annotationText;
|
o.text = item.annotationText;
|
||||||
}
|
}
|
||||||
else if (o.type == 'image') {
|
else if (o.type == 'image') {
|
||||||
var attachments = item.getAttachments();
|
let file = this.getCacheImagePath(item);
|
||||||
if (attachments.length) {
|
if (await OS.File.exists(file)) {
|
||||||
let imageAttachment = Zotero.Items.get(attachments[0]);
|
o.image = await Zotero.File.generateDataURI(file, 'image/png');
|
||||||
if (imageAttachment) {
|
|
||||||
o.image = await imageAttachment.attachmentDataURI;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o.comment = item.annotationComment;
|
o.comment = item.annotationComment;
|
||||||
|
|
|
@ -4530,6 +4530,12 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
|
||||||
// Zotero.Sync.EventListeners.ChangeListener needs to know if this was a storage file
|
// Zotero.Sync.EventListeners.ChangeListener needs to know if this was a storage file
|
||||||
env.notifierData[this.id].storageDeleteLog = this.isStoredFileAttachment();
|
env.notifierData[this.id].storageDeleteLog = this.isStoredFileAttachment();
|
||||||
}
|
}
|
||||||
|
// Delete cached file for image annotation
|
||||||
|
else if (this.isAnnotation()) {
|
||||||
|
if (this.isImageAnnotation()) {
|
||||||
|
yield Zotero.Annotations.removeCacheImage(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Regular item
|
// Regular item
|
||||||
else {
|
else {
|
||||||
let sql = "SELECT itemID FROM itemNotes WHERE parentItemID=?1 UNION "
|
let sql = "SELECT itemID FROM itemNotes WHERE parentItemID=?1 UNION "
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
describe("Zotero.Annotations", function() {
|
describe("Zotero.Annotations", function() {
|
||||||
var exampleHighlight = {
|
var exampleHighlight = {
|
||||||
|
"libraryID": null,
|
||||||
"key": "92JLMCVT",
|
"key": "92JLMCVT",
|
||||||
"type": "highlight",
|
"type": "highlight",
|
||||||
"isAuthor": true,
|
"isAuthor": true,
|
||||||
|
@ -32,6 +33,7 @@ describe("Zotero.Annotations", function() {
|
||||||
var exampleHighlightAlt = jsonPositionToString(exampleHighlight);
|
var exampleHighlightAlt = jsonPositionToString(exampleHighlight);
|
||||||
|
|
||||||
var exampleNote = {
|
var exampleNote = {
|
||||||
|
"libraryID": null,
|
||||||
"key": "5TKU34XX",
|
"key": "5TKU34XX",
|
||||||
"type": "note",
|
"type": "note",
|
||||||
"isAuthor": true,
|
"isAuthor": true,
|
||||||
|
@ -50,6 +52,7 @@ describe("Zotero.Annotations", function() {
|
||||||
var exampleNoteAlt = jsonPositionToString(exampleNote);
|
var exampleNoteAlt = jsonPositionToString(exampleNote);
|
||||||
|
|
||||||
var exampleImage = {
|
var exampleImage = {
|
||||||
|
"libraryID": null,
|
||||||
"key": "QD32MQJF",
|
"key": "QD32MQJF",
|
||||||
"type": "image",
|
"type": "image",
|
||||||
"isAuthor": true,
|
"isAuthor": true,
|
||||||
|
@ -71,6 +74,7 @@ describe("Zotero.Annotations", function() {
|
||||||
var exampleImageAlt = jsonPositionToString(exampleImage);
|
var exampleImageAlt = jsonPositionToString(exampleImage);
|
||||||
|
|
||||||
var exampleGroupHighlight = {
|
var exampleGroupHighlight = {
|
||||||
|
"libraryID": null,
|
||||||
"key": "PE57YAYH",
|
"key": "PE57YAYH",
|
||||||
"type": "highlight",
|
"type": "highlight",
|
||||||
"isAuthor": false,
|
"isAuthor": false,
|
||||||
|
@ -111,8 +115,12 @@ describe("Zotero.Annotations", function() {
|
||||||
before(async function () {
|
before(async function () {
|
||||||
item = await createDataObject('item');
|
item = await createDataObject('item');
|
||||||
attachment = await importFileAttachment('test.pdf', { parentID: item.id });
|
attachment = await importFileAttachment('test.pdf', { parentID: item.id });
|
||||||
|
exampleHighlight.libraryID = item.libraryID;
|
||||||
|
exampleNote.libraryID = item.libraryID;
|
||||||
|
exampleImage.libraryID = item.libraryID;
|
||||||
|
|
||||||
group = await getGroup();
|
group = await getGroup();
|
||||||
|
exampleGroupHighlight.libraryID = group.libraryID;
|
||||||
groupItem = await createDataObject('item', { libraryID: group.libraryID });
|
groupItem = await createDataObject('item', { libraryID: group.libraryID });
|
||||||
groupAttachment = await importFileAttachment(
|
groupAttachment = await importFileAttachment(
|
||||||
'test.pdf',
|
'test.pdf',
|
||||||
|
@ -195,10 +203,7 @@ describe("Zotero.Annotations", function() {
|
||||||
array[i] = imageData.charCodeAt(i);
|
array[i] = imageData.charCodeAt(i);
|
||||||
}
|
}
|
||||||
var blob = new Blob([array], { type: 'image/png' });
|
var blob = new Blob([array], { type: 'image/png' });
|
||||||
var imageAttachment = await Zotero.Attachments.importEmbeddedImage({
|
var file = await Zotero.Annotations.saveCacheImage(annotation, blob);
|
||||||
blob,
|
|
||||||
parentItemID: annotation.id
|
|
||||||
});
|
|
||||||
|
|
||||||
var json = await Zotero.Annotations.toJSON(annotation);
|
var json = await Zotero.Annotations.toJSON(annotation);
|
||||||
|
|
||||||
|
@ -275,7 +280,7 @@ describe("Zotero.Annotations", function() {
|
||||||
it("should create an item from an image", async function () {
|
it("should create an item from an image", async function () {
|
||||||
var annotation = await Zotero.Annotations.saveFromJSON(attachment, exampleImage);
|
var annotation = await Zotero.Annotations.saveFromJSON(attachment, exampleImage);
|
||||||
|
|
||||||
// Note: Image is created separately using Zotero.Attachments.importEmbeddedImage()
|
// Note: Image is created separately using Zotero.Annotations.saveCacheImage()
|
||||||
|
|
||||||
assert.equal(annotation.key, exampleImage.key);
|
assert.equal(annotation.key, exampleImage.key);
|
||||||
for (let prop of ['comment', 'color', 'pageLabel', 'sortIndex', 'position']) {
|
for (let prop of ['comment', 'color', 'pageLabel', 'sortIndex', 'position']) {
|
||||||
|
|
|
@ -1294,35 +1294,15 @@ describe("Zotero.Item", function () {
|
||||||
await annotation.saveTx();
|
await annotation.saveTx();
|
||||||
|
|
||||||
var blob = new Blob([array], { type: 'image/png' });
|
var blob = new Blob([array], { type: 'image/png' });
|
||||||
await Zotero.Attachments.importEmbeddedImage({
|
await Zotero.Annotations.saveCacheImage(annotation, blob);
|
||||||
blob,
|
|
||||||
parentItemID: annotation.id
|
|
||||||
});
|
|
||||||
|
|
||||||
var attachments = annotation.getAttachments();
|
var imagePath = Zotero.Annotations.getCacheImagePath(annotation);
|
||||||
assert.lengthOf(attachments, 1);
|
|
||||||
var imageAttachment = Zotero.Items.get(attachments[0]);
|
|
||||||
var imagePath = await imageAttachment.getFilePathAsync();
|
|
||||||
assert.ok(imagePath);
|
assert.ok(imagePath);
|
||||||
assert.equal(OS.Path.basename(imagePath), 'image.png');
|
assert.equal(OS.Path.basename(imagePath), annotation.key + '.png');
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await Zotero.File.getBinaryContentsAsync(imagePath),
|
await Zotero.File.getBinaryContentsAsync(imagePath),
|
||||||
imageData
|
imageData
|
||||||
);
|
);
|
||||||
assert.equal(imageAttachment.attachmentContentType, 'image/png');
|
|
||||||
|
|
||||||
var blob2 = await new Zotero.Promise((resolve) => {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.addEventListener("load", function () {
|
|
||||||
resolve(reader.result);
|
|
||||||
}, false);
|
|
||||||
reader.readAsDataURL(blob);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
await imageAttachment.attachmentDataURI,
|
|
||||||
blob2
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1555,6 +1535,25 @@ describe("Zotero.Item", function () {
|
||||||
skipEditCheck: true
|
skipEditCheck: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should remove cached image for an annotation item", async function () {
|
||||||
|
var attachment = await importFileAttachment('test.pdf');
|
||||||
|
var annotation = await createAnnotation('image', attachment);
|
||||||
|
|
||||||
|
// Get Blob from file and attach it
|
||||||
|
var path = OS.Path.join(getTestDataDirectory().path, 'test.png');
|
||||||
|
var imageData = await Zotero.File.getBinaryContentsAsync(path);
|
||||||
|
var array = new Uint8Array(imageData.length);
|
||||||
|
for (let i = 0; i < imageData.length; i++) {
|
||||||
|
array[i] = imageData.charCodeAt(i);
|
||||||
|
}
|
||||||
|
var blob = new Blob([array], { type: 'image/png' });
|
||||||
|
var file = await Zotero.Annotations.saveCacheImage(annotation, blob);
|
||||||
|
|
||||||
|
assert.isTrue(await OS.File.exists(file));
|
||||||
|
await annotation.eraseTx();
|
||||||
|
assert.isFalse(await OS.File.exists(file));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue