Allow library switching in target selector
If switching from a filesEditable library to a non-filesEditable library, files are removed. If going the other direction (including if the original save was to a non-filesEditable library), the save is performed again from the beginning in order to include attachments. If switching between two filesEditable libraries, the storage directory is just moved. Addresses zotero/zotero-connectors#220
This commit is contained in:
parent
9e955bde99
commit
350b47364e
|
@ -257,7 +257,7 @@ Zotero.Attachments = new function(){
|
||||||
* @param {String} [options.referrer]
|
* @param {String} [options.referrer]
|
||||||
* @param {CookieSandbox} [options.cookieSandbox]
|
* @param {CookieSandbox} [options.cookieSandbox]
|
||||||
* @param {Object} [options.saveOptions]
|
* @param {Object} [options.saveOptions]
|
||||||
* @return {Promise<Zotero.Item|false>} - A promise for the created attachment item
|
* @return {Promise<Zotero.Item>} - A promise for the created attachment item
|
||||||
*/
|
*/
|
||||||
this.importFromURL = Zotero.Promise.coroutine(function* (options) {
|
this.importFromURL = Zotero.Promise.coroutine(function* (options) {
|
||||||
var libraryID = options.libraryID;
|
var libraryID = options.libraryID;
|
||||||
|
|
|
@ -40,22 +40,7 @@ Zotero.Server.Connector = {
|
||||||
catch (e) {
|
catch (e) {
|
||||||
let id = Zotero.Prefs.get('lastViewedFolder');
|
let id = Zotero.Prefs.get('lastViewedFolder');
|
||||||
if (id) {
|
if (id) {
|
||||||
let type = id[0];
|
({ library, collection, editable } = this.resolveTarget(id));
|
||||||
Zotero.debug(type);
|
|
||||||
id = parseInt(('' + id).substr(1));
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'L':
|
|
||||||
library = Zotero.Libraries.get(id);
|
|
||||||
editable = library.editable;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'C':
|
|
||||||
collection = Zotero.Collections.get(id);
|
|
||||||
library = collection.library;
|
|
||||||
editable = collection.editable;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +53,33 @@ Zotero.Server.Connector = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { library, collection, editable };
|
||||||
|
},
|
||||||
|
|
||||||
|
resolveTarget: function (targetID) {
|
||||||
|
var library;
|
||||||
|
var collection;
|
||||||
|
var editable;
|
||||||
|
|
||||||
|
var type = targetID[0];
|
||||||
|
var id = parseInt(('' + targetID).substr(1));
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'L':
|
||||||
|
library = Zotero.Libraries.get(id);
|
||||||
|
editable = library.editable;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
collection = Zotero.Collections.get(id);
|
||||||
|
library = collection.library;
|
||||||
|
editable = collection.editable;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported target type '${type}'`);
|
||||||
|
}
|
||||||
|
|
||||||
return { library, collection, editable };
|
return { library, collection, editable };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -80,7 +92,7 @@ Zotero.Server.Connector.SessionManager = {
|
||||||
return this._sessions.get(id);
|
return this._sessions.get(id);
|
||||||
},
|
},
|
||||||
|
|
||||||
create: function (id) {
|
create: function (id, action, requestData) {
|
||||||
// Legacy connector
|
// Legacy connector
|
||||||
if (!id) {
|
if (!id) {
|
||||||
Zotero.debug("No session id provided by client", 2);
|
Zotero.debug("No session id provided by client", 2);
|
||||||
|
@ -90,7 +102,7 @@ Zotero.Server.Connector.SessionManager = {
|
||||||
throw new Error(`Session ID ${id} exists`);
|
throw new Error(`Session ID ${id} exists`);
|
||||||
}
|
}
|
||||||
Zotero.debug("Creating connector save session " + id);
|
Zotero.debug("Creating connector save session " + id);
|
||||||
var session = new Zotero.Server.Connector.SaveSession(id);
|
var session = new Zotero.Server.Connector.SaveSession(id, action, requestData);
|
||||||
this._sessions.set(id, session);
|
this._sessions.set(id, session);
|
||||||
this.gc();
|
this.gc();
|
||||||
return session;
|
return session;
|
||||||
|
@ -110,130 +122,132 @@ Zotero.Server.Connector.SessionManager = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Zotero.Server.Connector.SaveSession = function (id) {
|
Zotero.Server.Connector.SaveSession = function (id, action, requestData) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.created = new Date();
|
this.created = new Date();
|
||||||
this._objects = {};
|
this._action = action;
|
||||||
|
this._requestData = requestData;
|
||||||
|
this._items = new Set();
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.Server.Connector.SaveSession.prototype.addItem = async function (item) {
|
Zotero.Server.Connector.SaveSession.prototype.addItem = async function (item) {
|
||||||
return this._addObjects('item', [item]);
|
return this.addItems([item]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Zotero.Server.Connector.SaveSession.prototype.addItems = async function (items) {
|
Zotero.Server.Connector.SaveSession.prototype.addItems = async function (items) {
|
||||||
return this._addObjects('item', items);
|
for (let item of items) {
|
||||||
|
this._items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the items with the current target data, in case it changed since the save began
|
||||||
|
await this._updateItems(items);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the target data for this session and update any items that have already been saved
|
* Change the target data for this session and update any items that have already been saved
|
||||||
*/
|
*/
|
||||||
Zotero.Server.Connector.SaveSession.prototype.update = async function (libraryID, collectionID, tags) {
|
Zotero.Server.Connector.SaveSession.prototype.update = async function (targetID, tags) {
|
||||||
this._currentLibraryID = libraryID;
|
var previousTargetID = this._currentTargetID;
|
||||||
this._currentCollectionID = collectionID;
|
this._currentTargetID = targetID;
|
||||||
this._currentTags = tags || "";
|
this._currentTags = tags || "";
|
||||||
|
|
||||||
// Select new destination in collections pane
|
// Select new destination in collections pane
|
||||||
var win = Zotero.getActiveZoteroPane();
|
var win = Zotero.getActiveZoteroPane();
|
||||||
if (collectionID) {
|
|
||||||
var targetID = "C" + collectionID;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var targetID = "L" + libraryID;
|
|
||||||
}
|
|
||||||
if (win && win.collectionsView) {
|
if (win && win.collectionsView) {
|
||||||
await win.collectionsView.selectByID(targetID);
|
await win.collectionsView.selectByID(targetID);
|
||||||
}
|
}
|
||||||
|
// If window is closed, select target collection re-open
|
||||||
else {
|
else {
|
||||||
Zotero.Prefs.set('lastViewedFolder', targetID);
|
Zotero.Prefs.set('lastViewedFolder', targetID);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._updateObjects(this._objects);
|
// If moving from a non-filesEditable library to a filesEditable library, resave from
|
||||||
|
// original data, since there might be files that weren't saved or were removed
|
||||||
// TODO: Update active item saver
|
if (previousTargetID && previousTargetID != targetID) {
|
||||||
|
let { library: oldLibrary } = Zotero.Server.Connector.resolveTarget(previousTargetID);
|
||||||
// If a single item was saved, select it (or its parent, if it now has one)
|
let { library: newLibrary } = Zotero.Server.Connector.resolveTarget(targetID);
|
||||||
if (win && win.collectionsView) {
|
if (oldLibrary != newLibrary && !oldLibrary.filesEditable && newLibrary.filesEditable) {
|
||||||
if (this._objects && this._objects.item) {
|
Zotero.debug("Resaving items to filesEditable library");
|
||||||
if (this._objects.item.size == 1) {
|
if (this._action == 'saveItems' || this._action == 'saveSnapshot') {
|
||||||
let item = Array.from(this._objects.item)[0];
|
// Delete old items
|
||||||
item = item.isTopLevelItem() ? item : item.parentItem;
|
for (let item of this._items) {
|
||||||
// Don't select if in trash
|
await item.eraseTx();
|
||||||
if (!item.deleted) {
|
|
||||||
await win.selectItem(item.id);
|
|
||||||
}
|
}
|
||||||
|
let actionUC = Zotero.Utilities.capitalize(this._action);
|
||||||
|
let newItems = await Zotero.Server.Connector[actionUC].prototype[this._action](
|
||||||
|
targetID, this._requestData
|
||||||
|
);
|
||||||
|
// saveSnapshot only returns a single item
|
||||||
|
if (this._action == 'saveSnapshot') {
|
||||||
|
newItems = [newItems];
|
||||||
|
}
|
||||||
|
this._items = new Set(newItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Zotero.Server.Connector.SaveSession.prototype._addObjects = async function (objectType, objects) {
|
|
||||||
if (!this._objects[objectType]) {
|
|
||||||
this._objects[objectType] = new Set();
|
|
||||||
}
|
|
||||||
for (let object of objects) {
|
|
||||||
this._objects[objectType].add(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the objects with the current target data, in case it changed since the save began
|
await this._updateItems(this._items);
|
||||||
await this._updateObjects({
|
|
||||||
[objectType]: objects
|
// If a single item was saved, select it (or its parent, if it now has one)
|
||||||
});
|
if (win && win.collectionsView && this._items.size == 1) {
|
||||||
|
let item = Array.from(this._items)[0];
|
||||||
|
item = item.isTopLevelItem() ? item : item.parentItem;
|
||||||
|
// Don't select if in trash
|
||||||
|
if (!item.deleted) {
|
||||||
|
await win.selectItem(item.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the passed objects with the current target and tags
|
* Update the passed items with the current target and tags
|
||||||
*/
|
*/
|
||||||
Zotero.Server.Connector.SaveSession.prototype._updateObjects = async function (objects) {
|
Zotero.Server.Connector.SaveSession.prototype._updateItems = Zotero.serial(async function (items) {
|
||||||
if (Object.keys(objects).every(type => objects[type].length == 0)) {
|
if (items.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var libraryID = this._currentLibraryID;
|
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(this._currentTargetID);
|
||||||
var collectionID = this._currentCollectionID;
|
var libraryID = library.libraryID;
|
||||||
|
|
||||||
var tags = this._currentTags.trim();
|
var tags = this._currentTags.trim();
|
||||||
tags = tags ? tags.split(/\s*,\s*/) : [];
|
tags = tags ? tags.split(/\s*,\s*/) : [];
|
||||||
|
|
||||||
Zotero.debug("Updating objects for connector save session " + this.id);
|
Zotero.debug("Updating items for connector save session " + this.id);
|
||||||
|
|
||||||
return Zotero.DB.executeTransaction(async function () {
|
for (let item of items) {
|
||||||
for (let objectType in objects) {
|
let newLibrary = Zotero.Libraries.get(library.libraryID);
|
||||||
for (let object of objects[objectType]) {
|
|
||||||
if (object.libraryID != libraryID) {
|
if (item.libraryID != libraryID) {
|
||||||
throw new Error("Can't move objects between libraries");
|
let newItem = await item.moveToLibrary(libraryID);
|
||||||
}
|
// Replace item in session
|
||||||
|
this._items.delete(item);
|
||||||
// Assign manual tags and collections to the item, or the parent item if it's now
|
this._items.add(newItem);
|
||||||
// a child item (e.g., from Retrieve Metadata for PDF)
|
|
||||||
if (objectType == 'item') {
|
|
||||||
let item = object.isTopLevelItem() ? object : object.parentItem;
|
|
||||||
if (!Zotero.Items.exists(item.id)) {
|
|
||||||
Zotero.debug(`Item ${item.id} in save session no longer exists`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Keep automatic tags
|
|
||||||
let originalTags = item.getTags().filter(tag => tag.type == 1);
|
|
||||||
item.setTags(originalTags.concat(tags));
|
|
||||||
item.setCollections(collectionID ? [collectionID] : []);
|
|
||||||
await item.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateRecents();
|
// If the item is now a child item (e.g., from Retrieve Metadata for PDF), update the
|
||||||
});
|
// parent item instead
|
||||||
};
|
if (!item.isTopLevelItem()) {
|
||||||
|
item = item.parentItem;
|
||||||
|
}
|
||||||
|
// Skip deleted items
|
||||||
|
if (!Zotero.Items.exists(item.id)) {
|
||||||
|
Zotero.debug(`Item ${item.id} in save session no longer exists`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Keep automatic tags
|
||||||
|
let originalTags = item.getTags().filter(tag => tag.type == 1);
|
||||||
|
item.setTags(originalTags.concat(tags));
|
||||||
|
item.setCollections(collection ? [collection.id] : []);
|
||||||
|
await item.saveTx();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateRecents();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Zotero.Server.Connector.SaveSession.prototype._updateRecents = function () {
|
Zotero.Server.Connector.SaveSession.prototype._updateRecents = function () {
|
||||||
var libraryID = this._currentLibraryID;
|
var targetID = this._currentTargetID;
|
||||||
var collectionID = this._currentCollectionID;
|
|
||||||
if (collectionID) {
|
|
||||||
var targetID = "C" + collectionID;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var targetID = "L" + libraryID;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let numRecents = 5;
|
let numRecents = 5;
|
||||||
let recents = Zotero.Prefs.get('recentSaveTargets') || '[]';
|
let recents = Zotero.Prefs.get('recentSaveTargets') || '[]';
|
||||||
|
@ -558,19 +572,24 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
||||||
* Either loads HTML into a hidden browser and initiates translation, or saves items directly
|
* Either loads HTML into a hidden browser and initiates translation, or saves items directly
|
||||||
* to the database
|
* to the database
|
||||||
*/
|
*/
|
||||||
init: Zotero.Promise.coroutine(function* (options) {
|
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||||
var data = options.data;
|
var data = requestData.data;
|
||||||
|
|
||||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||||
var libraryID = library.libraryID;
|
var libraryID = library.libraryID;
|
||||||
|
var targetID = collection ? collection.treeViewID : library.treeViewID;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
var session = Zotero.Server.Connector.SessionManager.create(
|
||||||
|
data.sessionID,
|
||||||
|
'saveItems',
|
||||||
|
requestData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||||
}
|
}
|
||||||
yield session.update(libraryID, collection ? collection.id : false);
|
yield session.update(targetID);
|
||||||
|
|
||||||
// TODO: Default to My Library root, since it's changeable
|
// TODO: Default to My Library root, since it's changeable
|
||||||
if (!library.editable) {
|
if (!library.editable) {
|
||||||
|
@ -578,26 +597,52 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
||||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Zotero.Promise((resolve) => {
|
||||||
|
try {
|
||||||
|
this.saveItems(
|
||||||
|
targetID,
|
||||||
|
requestData,
|
||||||
|
function (topLevelItems) {
|
||||||
|
resolve([201, "application/json", JSON.stringify({items: topLevelItems})]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Add items to session once all attachments have been saved
|
||||||
|
.then(function (items) {
|
||||||
|
session.addItems(items);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
resolve(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
saveItems: async function (target, requestData, onTopLevelItemsDone) {
|
||||||
|
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(target);
|
||||||
|
|
||||||
|
var data = requestData.data;
|
||||||
var cookieSandbox = data.uri
|
var cookieSandbox = data.uri
|
||||||
? new Zotero.CookieSandbox(
|
? new Zotero.CookieSandbox(
|
||||||
null,
|
null,
|
||||||
data.uri,
|
data.uri,
|
||||||
data.detailedCookies ? "" : data.cookie || "",
|
data.detailedCookies ? "" : data.cookie || "",
|
||||||
options.headers["User-Agent"]
|
requestData.headers["User-Agent"]
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
if(cookieSandbox && data.detailedCookies) {
|
if (cookieSandbox && data.detailedCookies) {
|
||||||
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var i=0; i<data.items.length; i++) {
|
for (let item of data.items) {
|
||||||
Zotero.Server.Connector.AttachmentProgressManager.add(data.items[i].attachments);
|
Zotero.Server.Connector.AttachmentProgressManager.add(item.attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
let proxy = data.proxy && new Zotero.Proxy(data.proxy);
|
var proxy = data.proxy && new Zotero.Proxy(data.proxy);
|
||||||
// save items
|
|
||||||
|
// Save items
|
||||||
var itemSaver = new Zotero.Translate.ItemSaver({
|
var itemSaver = new Zotero.Translate.ItemSaver({
|
||||||
libraryID,
|
libraryID: library.libraryID,
|
||||||
collections: collection ? [collection.id] : undefined,
|
collections: collection ? [collection.id] : undefined,
|
||||||
attachmentMode: Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD,
|
attachmentMode: Zotero.Translate.ItemSaver.ATTACHMENT_MODE_DOWNLOAD,
|
||||||
forceTagType: 1,
|
forceTagType: 1,
|
||||||
|
@ -605,35 +650,25 @@ Zotero.Server.Connector.SaveItem.prototype = {
|
||||||
cookieSandbox,
|
cookieSandbox,
|
||||||
proxy
|
proxy
|
||||||
});
|
});
|
||||||
try {
|
return itemSaver.saveItems(
|
||||||
var deferred = Zotero.Promise.defer();
|
data.items,
|
||||||
itemSaver.saveItems(
|
Zotero.Server.Connector.AttachmentProgressManager.onProgress,
|
||||||
data.items,
|
function () {
|
||||||
Zotero.Server.Connector.AttachmentProgressManager.onProgress,
|
// Remove attachments from item.attachments that aren't being saved. We have to
|
||||||
function() {
|
// clone the items so that we don't mutate the data stored in the session.
|
||||||
// Remove attachments not being saved from item.attachments
|
var savedItems = [...data.items.map(item => Object.assign({}, item))];
|
||||||
for(var i=0; i<data.items.length; i++) {
|
for (let item of savedItems) {
|
||||||
var item = data.items[i];
|
item.attachments = item.attachments
|
||||||
for(var j=0; j<item.attachments.length; j++) {
|
.filter(attachment => {
|
||||||
if(!Zotero.Server.Connector.AttachmentProgressManager.has(item.attachments[j])) {
|
return Zotero.Server.Connector.AttachmentProgressManager.has(attachment);
|
||||||
item.attachments.splice(j--, 1);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deferred.resolve([201, "application/json", JSON.stringify({items: data.items})]);
|
|
||||||
}
|
}
|
||||||
)
|
if (onTopLevelItemsDone) {
|
||||||
.then(function (items) {
|
onTopLevelItemsDone(savedItems);
|
||||||
session.addItems(items);
|
}
|
||||||
});
|
}
|
||||||
return deferred.promise;
|
);
|
||||||
}
|
}
|
||||||
catch (e) {
|
|
||||||
Zotero.logError(e);
|
|
||||||
return 500;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -656,21 +691,23 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
/**
|
/**
|
||||||
* Save snapshot
|
* Save snapshot
|
||||||
*/
|
*/
|
||||||
init: Zotero.Promise.coroutine(function* (options) {
|
init: async function (requestData) {
|
||||||
var data = options.data;
|
var data = requestData.data;
|
||||||
|
|
||||||
Zotero.Server.Connector.Data[data["url"]] = "<html>"+data["html"]+"</html>";
|
|
||||||
|
|
||||||
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
var { library, collection, editable } = Zotero.Server.Connector.getSaveTarget();
|
||||||
var libraryID = library.libraryID;
|
var targetID = collection ? collection.treeViewID : library.treeViewID;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var session = Zotero.Server.Connector.SessionManager.create(data.sessionID);
|
var session = Zotero.Server.Connector.SessionManager.create(
|
||||||
|
data.sessionID,
|
||||||
|
'saveSnapshot',
|
||||||
|
requestData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||||
}
|
}
|
||||||
yield session.update(libraryID, collection ? collection.id : false);
|
await session.update(collection ? collection.treeViewID : library.treeViewID);
|
||||||
|
|
||||||
// TODO: Default to My Library root, since it's changeable
|
// TODO: Default to My Library root, since it's changeable
|
||||||
if (!library.editable) {
|
if (!library.editable) {
|
||||||
|
@ -678,63 +715,60 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
return [500, "application/json", JSON.stringify({ libraryEditable: false })];
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine whether snapshot can be saved
|
try {
|
||||||
var filesEditable;
|
let item = await this.saveSnapshot(targetID, requestData);
|
||||||
if (libraryID) {
|
await session.addItem(item);
|
||||||
let group = Zotero.Groups.getByLibraryID(libraryID);
|
|
||||||
filesEditable = group.filesEditable;
|
|
||||||
}
|
}
|
||||||
else {
|
catch (e) {
|
||||||
filesEditable = true;
|
Zotero.logError(e);
|
||||||
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 201;
|
||||||
|
},
|
||||||
|
|
||||||
|
saveSnapshot: async function (target, requestData) {
|
||||||
|
var { library, collection, editable } = Zotero.Server.Connector.resolveTarget(target);
|
||||||
|
var libraryID = library.libraryID;
|
||||||
|
var data = requestData.data;
|
||||||
|
|
||||||
var cookieSandbox = data.url
|
var cookieSandbox = data.url
|
||||||
? new Zotero.CookieSandbox(
|
? new Zotero.CookieSandbox(
|
||||||
null,
|
null,
|
||||||
data.url,
|
data.url,
|
||||||
data.detailedCookies ? "" : data.cookie || "",
|
data.detailedCookies ? "" : data.cookie || "",
|
||||||
options.headers["User-Agent"]
|
requestData.headers["User-Agent"]
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
if(cookieSandbox && data.detailedCookies) {
|
if (cookieSandbox && data.detailedCookies) {
|
||||||
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
cookieSandbox.addCookiesFromHeader(data.detailedCookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.pdf && filesEditable) {
|
if (data.pdf && library.filesEditable) {
|
||||||
delete Zotero.Server.Connector.Data[data.url];
|
let item = await Zotero.Attachments.importFromURL({
|
||||||
|
libraryID,
|
||||||
|
url: data.url,
|
||||||
|
collections: collection ? [collection.id] : undefined,
|
||||||
|
contentType: "application/pdf",
|
||||||
|
cookieSandbox
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
// Automatically recognize PDF
|
||||||
let item = yield Zotero.Attachments.importFromURL({
|
Zotero.RecognizePDF.autoRecognizeItems([item]);
|
||||||
libraryID,
|
|
||||||
url: data.url,
|
return item;
|
||||||
collections: collection ? [collection.id] : undefined,
|
|
||||||
contentType: "application/pdf",
|
|
||||||
cookieSandbox
|
|
||||||
});
|
|
||||||
if (item) {
|
|
||||||
yield session.addItem(item);
|
|
||||||
|
|
||||||
// Automatically recognize PDF
|
|
||||||
Zotero.RecognizePDF.autoRecognizeItems([item]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 201;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.logError(e);
|
|
||||||
return 500;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
let deferred = Zotero.Promise.defer();
|
return new Zotero.Promise((resolve, reject) => {
|
||||||
|
Zotero.Server.Connector.Data[data.url] = "<html>" + data.html + "</html>";
|
||||||
Zotero.HTTP.loadDocuments(
|
Zotero.HTTP.loadDocuments(
|
||||||
["zotero://connector/" + encodeURIComponent(data.url)],
|
["zotero://connector/" + encodeURIComponent(data.url)],
|
||||||
Zotero.Promise.coroutine(function* (doc) {
|
async function (doc) {
|
||||||
delete Zotero.Server.Connector.Data[data.url];
|
delete Zotero.Server.Connector.Data[data.url];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// create new webpage item
|
// Create new webpage item
|
||||||
var item = new Zotero.Item("webpage");
|
let item = new Zotero.Item("webpage");
|
||||||
item.libraryID = libraryID;
|
item.libraryID = libraryID;
|
||||||
item.setField("title", doc.title);
|
item.setField("title", doc.title);
|
||||||
item.setField("url", data.url);
|
item.setField("url", data.url);
|
||||||
|
@ -742,29 +776,29 @@ Zotero.Server.Connector.SaveSnapshot.prototype = {
|
||||||
if (collection) {
|
if (collection) {
|
||||||
item.setCollections([collection.id]);
|
item.setCollections([collection.id]);
|
||||||
}
|
}
|
||||||
var itemID = yield item.saveTx();
|
var itemID = await item.saveTx();
|
||||||
yield session.addItem(item);
|
|
||||||
|
|
||||||
// save snapshot
|
// Save snapshot
|
||||||
if (filesEditable && !data.skipSnapshot) {
|
if (library.filesEditable && !data.skipSnapshot) {
|
||||||
yield Zotero.Attachments.importFromDocument({
|
await Zotero.Attachments.importFromDocument({
|
||||||
document: doc,
|
document: doc,
|
||||||
parentItemID: itemID
|
parentItemID: itemID
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve(201);
|
resolve(item);
|
||||||
} catch(e) {
|
|
||||||
Zotero.debug(e, 1);
|
|
||||||
deferred.resolve(500);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}),
|
catch (e) {
|
||||||
null, null, false, cookieSandbox
|
reject(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
cookieSandbox
|
||||||
);
|
);
|
||||||
return deferred.promise;
|
});
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -820,8 +854,8 @@ Zotero.Server.Connector.UpdateSession.prototype = {
|
||||||
supportedDataTypes: ["application/json"],
|
supportedDataTypes: ["application/json"],
|
||||||
permitBookmarklet: true,
|
permitBookmarklet: true,
|
||||||
|
|
||||||
init: async function (options) {
|
init: async function (requestData) {
|
||||||
var data = options.data
|
var data = requestData.data
|
||||||
|
|
||||||
if (!data.sessionID) {
|
if (!data.sessionID) {
|
||||||
return [400, "application/json", JSON.stringify({ error: "SESSION_ID_NOT_PROVIDED" })];
|
return [400, "application/json", JSON.stringify({ error: "SESSION_ID_NOT_PROVIDED" })];
|
||||||
|
@ -837,21 +871,15 @@ Zotero.Server.Connector.UpdateSession.prototype = {
|
||||||
var [type, id] = [data.target[0], parseInt(data.target.substr(1))];
|
var [type, id] = [data.target[0], parseInt(data.target.substr(1))];
|
||||||
var tags = data.tags;
|
var tags = data.tags;
|
||||||
|
|
||||||
if (type == 'L') {
|
if (type == 'C') {
|
||||||
let library = Zotero.Libraries.get(id);
|
|
||||||
await session.update(library.libraryID, null, tags);
|
|
||||||
}
|
|
||||||
else if (type == 'C') {
|
|
||||||
let collection = await Zotero.Collections.getAsync(id);
|
let collection = await Zotero.Collections.getAsync(id);
|
||||||
if (!collection) {
|
if (!collection) {
|
||||||
return [400, "application/json", JSON.stringify({ error: "COLLECTION_NOT_FOUND" })];
|
return [400, "application/json", JSON.stringify({ error: "COLLECTION_NOT_FOUND" })];
|
||||||
}
|
}
|
||||||
await session.update(collection.libraryID, collection.id, tags);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error(`Invalid identifier '${data.target}'`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await session.update(data.target, tags);
|
||||||
|
|
||||||
return [200, "application/json", JSON.stringify({})];
|
return [200, "application/json", JSON.stringify({})];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -861,7 +889,7 @@ Zotero.Server.Endpoints["/connector/delaySync"] = Zotero.Server.Connector.DelayS
|
||||||
Zotero.Server.Connector.DelaySync.prototype = {
|
Zotero.Server.Connector.DelaySync.prototype = {
|
||||||
supportedMethods: ["POST"],
|
supportedMethods: ["POST"],
|
||||||
|
|
||||||
init: async function (options) {
|
init: async function (requestData) {
|
||||||
Zotero.Sync.Runner.delaySync(10000);
|
Zotero.Sync.Runner.delaySync(10000);
|
||||||
return [204];
|
return [204];
|
||||||
}
|
}
|
||||||
|
@ -907,9 +935,9 @@ Zotero.Server.Connector.Import.prototype = {
|
||||||
supportedDataTypes: '*',
|
supportedDataTypes: '*',
|
||||||
permitBookmarklet: false,
|
permitBookmarklet: false,
|
||||||
|
|
||||||
init: async function (options) {
|
init: async function (requestData) {
|
||||||
let translate = new Zotero.Translate.Import();
|
let translate = new Zotero.Translate.Import();
|
||||||
translate.setString(options.data);
|
translate.setString(requestData.data);
|
||||||
let translators = await translate.getTranslators();
|
let translators = await translate.getTranslators();
|
||||||
if (!translators || !translators.length) {
|
if (!translators || !translators.length) {
|
||||||
return 400;
|
return 400;
|
||||||
|
@ -923,12 +951,12 @@ Zotero.Server.Connector.Import.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var session = Zotero.Server.Connector.SessionManager.create(options.query.session);
|
var session = Zotero.Server.Connector.SessionManager.create(requestData.query.session);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
return [409, "application/json", JSON.stringify({ error: "SESSION_EXISTS" })];
|
||||||
}
|
}
|
||||||
await session.update(libraryID, collection ? collection.id : false);
|
await session.update(collection ? collection.treeViewID : library.treeViewID);
|
||||||
|
|
||||||
let items = await translate.translate({
|
let items = await translate.translate({
|
||||||
libraryID,
|
libraryID,
|
||||||
|
@ -958,9 +986,11 @@ Zotero.Server.Connector.InstallStyle.prototype = {
|
||||||
supportedDataTypes: '*',
|
supportedDataTypes: '*',
|
||||||
permitBookmarklet: false,
|
permitBookmarklet: false,
|
||||||
|
|
||||||
init: Zotero.Promise.coroutine(function* (options) {
|
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||||
try {
|
try {
|
||||||
var styleName = yield Zotero.Styles.install(options.data, options.query.origin || null, true);
|
var styleName = yield Zotero.Styles.install(
|
||||||
|
requestData.data, requestData.query.origin || null, true
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return [400, "text/plain", e.message];
|
return [400, "text/plain", e.message];
|
||||||
}
|
}
|
||||||
|
@ -1041,8 +1071,6 @@ Zotero.Server.Connector.GetSelectedCollection.prototype = {
|
||||||
var originalLibraryID = library.libraryID;
|
var originalLibraryID = library.libraryID;
|
||||||
for (let library of Zotero.Libraries.getAll()) {
|
for (let library of Zotero.Libraries.getAll()) {
|
||||||
if (!library.editable) continue;
|
if (!library.editable) continue;
|
||||||
// TEMP: For now, don't allow library changing
|
|
||||||
if (library.libraryID != originalLibraryID) continue;
|
|
||||||
|
|
||||||
// Add recent: true for recent targets
|
// Add recent: true for recent targets
|
||||||
|
|
||||||
|
@ -1101,7 +1129,7 @@ Zotero.Server.Connector.GetClientHostnames.prototype = {
|
||||||
/**
|
/**
|
||||||
* Returns a 200 response to say the server is alive
|
* Returns a 200 response to say the server is alive
|
||||||
*/
|
*/
|
||||||
init: Zotero.Promise.coroutine(function* (options) {
|
init: Zotero.Promise.coroutine(function* (requestData) {
|
||||||
try {
|
try {
|
||||||
var hostnames = yield Zotero.Proxies.DNS.getHostnames();
|
var hostnames = yield Zotero.Proxies.DNS.getHostnames();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user