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:
Dan Stillman 2018-03-31 08:22:02 -04:00
parent 9e955bde99
commit 350b47364e
2 changed files with 245 additions and 217 deletions

View File

@ -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;

View File

@ -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) {