- Closes #1832, Connectors should be able to retrieve translator data and code from server in the absence of Zotero Standalone
- Closes #1831, Connectors should be able to save via API in the absence of Zotero Standalone - Fixes Zotero.Utilities.deepCopy() for arrays - Fixes some circumstances where an error would not be saved for future error reporting - Fixes connector status checking
This commit is contained in:
parent
2bee3ecf1e
commit
b114266fb3
13 changed files with 508 additions and 307 deletions
|
@ -30,42 +30,49 @@
|
||||||
/**
|
/**
|
||||||
* @namespace
|
* @namespace
|
||||||
*/
|
*/
|
||||||
if(!Zotero.Connector) Zotero.Connector = {};
|
Zotero.Connector_Types = new function() {
|
||||||
Zotero.Connector.Types = new function() {
|
|
||||||
/**
|
/**
|
||||||
* Initializes types
|
* Initializes types
|
||||||
* @param {Object} typeSchema typeSchema generated by Zotero.Connector.GetData#_generateTypeSchema
|
* @param {Object} typeSchema typeSchema generated by Zotero.Connector.GetData#_generateTypeSchema
|
||||||
*/
|
*/
|
||||||
this.init = function(typeSchema) {
|
this.init = function() {
|
||||||
const schemaTypes = ["itemTypes", "creatorTypes", "fields"];
|
const schemaTypes = ["itemTypes", "creatorTypes", "fields"];
|
||||||
|
|
||||||
// attach IDs and make referenceable by either ID or name
|
// attach IDs and make referenceable by either ID or name
|
||||||
for(var i=0; i<schemaTypes.length; i++) {
|
for(var i=0; i<schemaTypes.length; i++) {
|
||||||
var schemaType = schemaTypes[i];
|
var schemaType = schemaTypes[i];
|
||||||
this[schemaType] = typeSchema[schemaType];
|
this[schemaType] = Zotero.Utilities.deepCopy(Zotero.Connector_Types.schema[schemaType]);
|
||||||
for(var id in this[schemaType]) {
|
for(var id in this[schemaType]) {
|
||||||
var entry = this[schemaType][id];
|
var entry = this[schemaType][id];
|
||||||
entry.id = id;
|
entry.id = parseInt(id);
|
||||||
this[schemaType][entry.name] = entry;
|
this[schemaType][entry.name] = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passes schema to a callback
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
this.getSchema = function(callback) {
|
||||||
|
callback(Zotero.Connector_Types.schema);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.CachedTypes = function() {
|
Zotero.CachedTypes = function() {
|
||||||
this.getID = function(idOrName) {
|
this.getID = function(idOrName) {
|
||||||
if(!Zotero.Connector.Types[this.schemaType][idOrName]) return false;
|
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
|
||||||
return Zotero.Connector.Types[this.schemaType][idOrName].id;
|
return Zotero.Connector_Types[this.schemaType][idOrName].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getName = function(idOrName) {
|
this.getName = function(idOrName) {
|
||||||
if(!Zotero.Connector.Types[this.schemaType][idOrName]) return false;
|
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
|
||||||
return Zotero.Connector.Types[this.schemaType][idOrName].name;
|
return Zotero.Connector_Types[this.schemaType][idOrName].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getLocalizedString = function(idOrName) {
|
this.getLocalizedString = function(idOrName) {
|
||||||
if(!Zotero.Connector.Types[this.schemaType][idOrName]) return false;
|
if(!Zotero.Connector_Types[this.schemaType][idOrName]) return false;
|
||||||
return Zotero.Connector.Types[this.schemaType][idOrName].localizedString;
|
return Zotero.Connector_Types[this.schemaType][idOrName].localizedString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,14 +81,14 @@ Zotero.ItemTypes = new function() {
|
||||||
Zotero.CachedTypes.call(this);
|
Zotero.CachedTypes.call(this);
|
||||||
|
|
||||||
this.getImageSrc = function(idOrName) {
|
this.getImageSrc = function(idOrName) {
|
||||||
if(!Zotero.Connector.Types["itemTypes"][idOrName]) return false;
|
if(!Zotero.Connector_Types["itemTypes"][idOrName]) return false;
|
||||||
|
|
||||||
if(Zotero.isFx) {
|
if(Zotero.isFx) {
|
||||||
return "chrome://zotero/skin/"+Zotero.Connector.Types["itemTypes"][idOrName].icon;
|
return "chrome://zotero/skin/"+Zotero.Connector_Types["itemTypes"][idOrName].icon;
|
||||||
} else if(Zotero.isChrome) {
|
} else if(Zotero.isChrome) {
|
||||||
return chrome.extension.getURL("images/"+Zotero.Connector.Types["itemTypes"][idOrName].icon);
|
return chrome.extension.getURL("images/"+Zotero.Connector_Types["itemTypes"][idOrName].icon);
|
||||||
} else if(Zotero.isSafari) {
|
} else if(Zotero.isSafari) {
|
||||||
return safari.extension.baseURI+"images/itemTypes/"+Zotero.Connector.Types["itemTypes"][idOrName].icon;
|
return safari.extension.baseURI+"images/itemTypes/"+Zotero.Connector_Types["itemTypes"][idOrName].icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +98,11 @@ Zotero.CreatorTypes = new function() {
|
||||||
Zotero.CachedTypes.call(this);
|
Zotero.CachedTypes.call(this);
|
||||||
|
|
||||||
this.getTypesForItemType = function(idOrName) {
|
this.getTypesForItemType = function(idOrName) {
|
||||||
if(!Zotero.Connector.Types["itemTypes"][idOrName]) return false;
|
if(!Zotero.Connector_Types["itemTypes"][idOrName]) return false;
|
||||||
var itemType = Zotero.Connector.Types["itemTypes"][idOrName];
|
var itemType = Zotero.Connector_Types["itemTypes"][idOrName];
|
||||||
var creatorTypes = [];
|
var creatorTypes = [];
|
||||||
for(var i=0; i<itemType.creatorTypes.length; i++) {
|
for(var i=0; i<itemType.creatorTypes.length; i++) {
|
||||||
creatorTypes.push(Zotero.Connector.Types["creatorTypes"][itemType.creatorTypes[i]]);
|
creatorTypes.push(Zotero.Connector_Types["creatorTypes"][itemType.creatorTypes[i]]);
|
||||||
}
|
}
|
||||||
return creatorTypes;
|
return creatorTypes;
|
||||||
}
|
}
|
||||||
|
@ -107,10 +114,29 @@ Zotero.ItemFields = new function() {
|
||||||
|
|
||||||
this.isValidForType = function(fieldIdOrName, typeIdOrName) {
|
this.isValidForType = function(fieldIdOrName, typeIdOrName) {
|
||||||
// mimics itemFields.js
|
// mimics itemFields.js
|
||||||
if(!Zotero.Connector.Types["fields"][fieldIdOrName]
|
if(!Zotero.Connector_Types["fields"][fieldIdOrName]
|
||||||
|| !Zotero.Connector.Types["itemTypes"][typeIdOrName]) throw "Invalid field or type ID";
|
|| !Zotero.Connector_Types["itemTypes"][typeIdOrName]) throw "Invalid field or type ID";
|
||||||
|
|
||||||
return Zotero.Connector.Types["itemTypes"][typeIdOrName].fields.indexOf(
|
return Zotero.Connector_Types["itemTypes"][typeIdOrName].fields.indexOf(
|
||||||
Zotero.Connector.Types["fields"][fieldIdOrName].id) !== -1;
|
Zotero.Connector_Types["fields"][fieldIdOrName].id) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getFieldIDFromTypeAndBase = function(itemType, baseField) {
|
||||||
|
if(!Zotero.Connector_Types["fields"][baseField]
|
||||||
|
|| !Zotero.Connector_Types["itemTypes"][itemType]) throw "Invalid field or type ID";
|
||||||
|
|
||||||
|
// get as ID
|
||||||
|
baseField = Zotero.Connector_Types["fields"][baseField].id;
|
||||||
|
|
||||||
|
|
||||||
|
// loop through base fields for item type
|
||||||
|
var baseFields = Zotero.Connector_Types["itemTypes"][itemType]["baseFields"];
|
||||||
|
for(var i in baseFields) {
|
||||||
|
if(baseFields[i] === baseField) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,63 +27,16 @@ Zotero.Connector = new function() {
|
||||||
const CONNECTOR_URI = "http://127.0.0.1:23119/";
|
const CONNECTOR_URI = "http://127.0.0.1:23119/";
|
||||||
const CONNECTOR_SERVER_API_VERSION = 1;
|
const CONNECTOR_SERVER_API_VERSION = 1;
|
||||||
|
|
||||||
this.isOnline = true;
|
this.isOnline = null;
|
||||||
this.haveRefreshedData = false;
|
|
||||||
this.data = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to initialize Zotero
|
|
||||||
*/
|
|
||||||
this.init = function() {
|
|
||||||
Zotero.Connector.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if Zotero is online and passes current status to callback
|
* Checks if Zotero is online and passes current status to callback
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
this.checkIsOnline = function(callback) {
|
this.checkIsOnline = function(callback) {
|
||||||
if(Zotero.Connector.isOnline) {
|
Zotero.Connector.callMethod("ping", {}, function(status) {
|
||||||
callback(true);
|
callback(status !== false);
|
||||||
} else {
|
});
|
||||||
Zotero.Connector.getData(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getDataFile() {
|
|
||||||
var dataFile = Zotero.getZoteroDirectory();
|
|
||||||
dataFile.append("connector.json");
|
|
||||||
return dataFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes the Zotero.Connector.data object to localStorage/preferences
|
|
||||||
* @param {String} [json] The
|
|
||||||
*/
|
|
||||||
this.serializeData = function(json) {
|
|
||||||
if(!json) json = JSON.stringify(Zotero.Connector.data);
|
|
||||||
|
|
||||||
if(Zotero.isFx) {
|
|
||||||
Zotero.File.putContents(_getDataFile(), json);
|
|
||||||
} else {
|
|
||||||
localStorage.data = json;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unserializes the Zotero.Connector.data object from localStorage/preferences
|
|
||||||
*/
|
|
||||||
this.unserializeData = function() {
|
|
||||||
var data = null;
|
|
||||||
|
|
||||||
if(Zotero.isFx) {
|
|
||||||
var dataFile = _getDataFile();
|
|
||||||
if(dataFile.exists()) data = Zotero.File.getContents(dataFile);
|
|
||||||
} else {
|
|
||||||
if(localStorage.data) data = localStorage.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data) Zotero.Connector.data = JSON.parse(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// saner descriptions of some HTTP error codes
|
// saner descriptions of some HTTP error codes
|
||||||
|
@ -96,25 +49,17 @@ Zotero.Connector = new function() {
|
||||||
this.EXCEPTION_CODES = [0, 400, 404, 412, 500, 501];
|
this.EXCEPTION_CODES = [0, 400, 404, 412, 500, 501];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates Zotero's status depending on the success or failure of a request
|
* Sends the XHR to execute an RPC call.
|
||||||
*
|
*
|
||||||
* @param {Boolean} isOnline Whether or not Zotero was online
|
* @param {String} method RPC method. See documentation above.
|
||||||
* @param {Function} successCallback Function to be called after loading new data if
|
* @param {Object} data RPC data. See documentation above.
|
||||||
* Zotero is online
|
* @param {Function} callback Function to be called when requests complete.
|
||||||
* @param {Function} failureCallback Function to be called if Zotero is offline
|
|
||||||
*
|
|
||||||
* Calls Zotero.Connector.Browser.onStateChange(isOnline, method, context) if status has changed
|
|
||||||
*/
|
*/
|
||||||
function _checkState(isOnline, callback) {
|
this.callMethod = function(method, data, callback) {
|
||||||
if(isOnline) {
|
var newCallback = function(req) {
|
||||||
if(Zotero.Connector.haveRefreshedData) {
|
try {
|
||||||
if(callback) callback(true);
|
var isOnline = req.status !== Zotero.Connector.EXCEPTION_NOT_AVAILABLE
|
||||||
} else {
|
&& req.status !== Zotero.Connector.EXCEPTION_INCOMPATIBLE_VERSION;
|
||||||
Zotero.Connector.getData(callback);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(callback) callback(false, this.EXCEPTION_NOT_AVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Zotero.Connector.isOnline !== isOnline) {
|
if(Zotero.Connector.isOnline !== isOnline) {
|
||||||
Zotero.Connector.isOnline = isOnline;
|
Zotero.Connector.isOnline = isOnline;
|
||||||
|
@ -123,77 +68,11 @@ Zotero.Connector = new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return isOnline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads list of translators and other relevant data from local Zotero instance
|
|
||||||
*
|
|
||||||
* @param {Function} successCallback Function to be called after loading new data if
|
|
||||||
* Zotero is online
|
|
||||||
* @param {Function} failureCallback Function to be called if Zotero is offline
|
|
||||||
*/
|
|
||||||
this.getData = function(callback) {
|
|
||||||
Zotero.HTTP.doPost(CONNECTOR_URI+"connector/getData",
|
|
||||||
JSON.stringify({"browser":Zotero.browser, "apiVersion":CONNECTOR_SERVER_API_VERSION}),
|
|
||||||
function(req) {
|
|
||||||
var isOnline = req.status !== 0 && req.status !== 412;
|
|
||||||
|
|
||||||
if(isOnline) {
|
|
||||||
// if request succeded, update data
|
|
||||||
Zotero.Connector.haveRefreshedData = true;
|
|
||||||
Zotero.Connector.serializeData(req.responseText);
|
|
||||||
Zotero.Connector.data = JSON.parse(req.responseText);
|
|
||||||
} else {
|
|
||||||
// if request failed, unserialize saved data
|
|
||||||
Zotero.Connector.unserializeData();
|
|
||||||
}
|
|
||||||
Zotero.Connector.Types.init(Zotero.Connector.data.schema);
|
|
||||||
|
|
||||||
// update online state. this shouldn't loop, since haveRefreshedData should
|
|
||||||
// be true if isOnline is true.
|
|
||||||
_checkState(isOnline, callback);
|
|
||||||
}, {"Content-Type":"application/json"});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gives callback an object containing schema and preferences from Zotero.Connector.data
|
|
||||||
*/
|
|
||||||
this.getSchemaAndPreferences = function(callback) {
|
|
||||||
if(Zotero.Connector.data) {
|
|
||||||
callback({"schema":Zotero.Connector.data["schema"],
|
|
||||||
"preferences":Zotero.Connector.data["preferences"]});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getData(function(success) {
|
|
||||||
if(success) {
|
|
||||||
callback({"schema":Zotero.Connector.data["schema"],
|
|
||||||
"preferences":Zotero.Connector.data["preferences"]});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
callback(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the XHR to execute an RPC call.
|
|
||||||
*
|
|
||||||
* @param {String} method RPC method. See documentation above.
|
|
||||||
* @param {Object} data RPC data. See documentation above.
|
|
||||||
* @param {Function} successCallback Function to be called if request succeeded.
|
|
||||||
* @param {Function} failureCallback Function to be called if request failed.
|
|
||||||
*/
|
|
||||||
this.callMethod = function(method, data, callback) {
|
|
||||||
Zotero.HTTP.doPost(CONNECTOR_URI+"connector/"+method, JSON.stringify(data),
|
|
||||||
function(req) {
|
|
||||||
_checkState(req.status !== this.EXCEPTION_NOT_AVAILABLE
|
|
||||||
&& req.status !== this.EXCEPTION_INCOMPATIBLE_VERSION, function() {
|
|
||||||
if(!callback) return;
|
|
||||||
|
|
||||||
if(Zotero.Connector.EXCEPTION_CODES.indexOf(req.status) !== -1) {
|
if(Zotero.Connector.EXCEPTION_CODES.indexOf(req.status) !== -1) {
|
||||||
callback(false, req.status);
|
Zotero.debug("Connector: Method "+method+" failed");
|
||||||
|
if(callback) callback(false, req.status);
|
||||||
} else {
|
} else {
|
||||||
|
Zotero.debug("Connector: Method "+method+" succeeded");
|
||||||
var val = null;
|
var val = null;
|
||||||
if(req.responseText) {
|
if(req.responseText) {
|
||||||
if(req.getResponseHeader("Content-Type") === "application/json") {
|
if(req.getResponseHeader("Content-Type") === "application/json") {
|
||||||
|
@ -202,9 +81,16 @@ Zotero.Connector = new function() {
|
||||||
val = req.responseText;
|
val = req.responseText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback(val, req.status);
|
if(callback) callback(val, req.status);
|
||||||
}
|
}
|
||||||
});
|
} catch(e) {
|
||||||
}, {"Content-Type":"application/json"});
|
Zotero.logError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var uri = CONNECTOR_URI+"connector/"+method;
|
||||||
|
|
||||||
|
Zotero.HTTP.doPost(uri, JSON.stringify(data),
|
||||||
|
newCallback, {"Content-Type":"application/json"});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -81,6 +81,6 @@ Zotero.Connector_Debug = new function() {
|
||||||
|
|
||||||
var reportID = reported[0].getAttribute('reportID');
|
var reportID = reported[0].getAttribute('reportID');
|
||||||
callback(true, reportID);
|
callback(true, reportID);
|
||||||
}, {"Content-Type":"application/octet-stream"});
|
}, {"Content-Type":"text/plain"});
|
||||||
}
|
}
|
||||||
}
|
}
|
170
chrome/content/zotero/xpcom/connector/repo.js
Normal file
170
chrome/content/zotero/xpcom/connector/repo.js
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2011 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TRANSLATOR_CODE_PREFIX = "translatorCode-";
|
||||||
|
Zotero.Repo = new function() {
|
||||||
|
var _nextCheck;
|
||||||
|
var _timeoutID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to retrieve translator metadata from Zotero Standalone and initialize repository check
|
||||||
|
* timer
|
||||||
|
*/
|
||||||
|
this.init = function() {
|
||||||
|
// get time of next check
|
||||||
|
_nextCheck = Zotero.Prefs.get("connector.repo.lastCheck.localTime")
|
||||||
|
+ZOTERO_CONFIG.REPOSITORY_CHECK_INTERVAL*1000;
|
||||||
|
|
||||||
|
// update from standalone, but only cascade to repo if we are overdue
|
||||||
|
_updateFromStandalone(_nextCheck <= Date.now());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all translators and code
|
||||||
|
*/
|
||||||
|
this.reset = function(callback) {
|
||||||
|
Zotero.Prefs.set("connector.repo.lastCheck.repoTime", 0);
|
||||||
|
this.update(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force updating translators
|
||||||
|
*/
|
||||||
|
var update = this.update = function(reset, callback) {
|
||||||
|
_updateFromStandalone(true, reset);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get translator code from repository
|
||||||
|
* @param {String} translatorID ID of the translator to retrieve code for
|
||||||
|
* @param {Function} callback Callback to pass code when retreived
|
||||||
|
*/
|
||||||
|
this.getTranslatorCode = function(translatorID, callback) {
|
||||||
|
// we might have code in localstorage
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
var localCode = localStorage[TRANSLATOR_CODE_PREFIX+translatorID];
|
||||||
|
if(localCode) {
|
||||||
|
callback(localCode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, try standalone
|
||||||
|
Zotero.Connector.callMethod("getTranslatorCode", {"translatorID":translatorID}, function(result) {
|
||||||
|
if(result) {
|
||||||
|
_haveCode(result, translatorID, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then try repo
|
||||||
|
Zotero.HTTP.doGet(ZOTERO_CONFIG.REPOSITORY_URL+"/code/"+translatorID, function(xmlhttp) {
|
||||||
|
_haveCode(xmlhttp.status === 200 ? xmlhttp.responseText : false, translatorID, callback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when code has been retrieved from standalone or repo
|
||||||
|
*/
|
||||||
|
function _haveCode(code, translatorID, callback) {
|
||||||
|
if(!code) {
|
||||||
|
callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
localStorage["translatorCode-"+translatorID] = Zotero.Translators.preprocessCode(code);
|
||||||
|
}
|
||||||
|
callback(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve translator metadata from Zotero Standalone
|
||||||
|
* @param {Boolean} [tryRepoOnFailure] If true, run _updateFromRepo() if standalone cannot be
|
||||||
|
* contacted
|
||||||
|
*/
|
||||||
|
function _updateFromStandalone(tryRepoOnFailure, reset, callback) {
|
||||||
|
Zotero.Connector.callMethod("getTranslators", {}, function(result) {
|
||||||
|
if(!result && tryRepoOnFailure) {
|
||||||
|
_updateFromRepo(reset, callback);
|
||||||
|
} else {
|
||||||
|
_handleResponse(result, reset);
|
||||||
|
if(callback) callback(!!result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve metadata from repository
|
||||||
|
*/
|
||||||
|
function _updateFromRepo(reset, callback) {
|
||||||
|
var url = ZOTERO_CONFIG.REPOSITORY_URL+"/metadata?last="+
|
||||||
|
Zotero.Prefs.get("connector.repo.lastCheck.repoTime");
|
||||||
|
|
||||||
|
Zotero.HTTP.doGet(url, function(xmlhttp) {
|
||||||
|
var success = xmlhttp.status === 200;
|
||||||
|
_handleResponse(success ? JSON.parse(xmlhttp.responseText) : false, reset);
|
||||||
|
|
||||||
|
if(success) {
|
||||||
|
var date = xmlhttp.getResponseHeader("Date");
|
||||||
|
Zotero.Prefs.set("connector.repo.lastCheck.repoTime",
|
||||||
|
Math.floor(Date.parse(date)/1000));
|
||||||
|
}
|
||||||
|
if(callback) callback(!!result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle response from Zotero Standalone or repository and set timer for next update
|
||||||
|
*/
|
||||||
|
function _handleResponse(result, reset) {
|
||||||
|
// set up timer
|
||||||
|
var now = Date.now();
|
||||||
|
|
||||||
|
if(result) {
|
||||||
|
Zotero.Translators.update(result, reset);
|
||||||
|
Zotero.Prefs.set("connector.repo.lastCheck.localTime", now);
|
||||||
|
Zotero.debug("Repo: Check succeeded");
|
||||||
|
} else {
|
||||||
|
Zotero.debug("Repo: Check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result || _nextCheck <= now) {
|
||||||
|
// if we failed a scheduled check, then use retry interval
|
||||||
|
_nextCheck = now+(result
|
||||||
|
? ZOTERO_CONFIG.REPOSITORY_CHECK_INTERVAL
|
||||||
|
: ZOTERO_CONFIG.REPOSITORY_RETRY_INTERVAL)*1000;
|
||||||
|
} else if(_timeoutID) {
|
||||||
|
// if we didn't fail a scheduled check and another is already scheduled, leave it
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove old timeout and create a new one
|
||||||
|
if(_timeoutID) clearTimeout(_timeoutID);
|
||||||
|
var nextCheckIn = (_nextCheck-now+2000);
|
||||||
|
_timeoutID = setTimeout(update, nextCheckIn);
|
||||||
|
Zotero.debug("Repo: Next check in "+nextCheckIn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,9 @@
|
||||||
|
|
||||||
Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
|
Zotero.Translate.ItemSaver = function(libraryID, attachmentMode, forceTagType) {
|
||||||
this.newItems = [];
|
this.newItems = [];
|
||||||
|
|
||||||
|
this._itemsToSaveToServer = [];
|
||||||
|
this._timeoutID = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0;
|
Zotero.Translate.ItemSaver.ATTACHMENT_MODE_IGNORE = 0;
|
||||||
|
@ -43,6 +46,84 @@ Zotero.Translate.ItemSaver.prototype = {
|
||||||
|
|
||||||
// save items
|
// save items
|
||||||
this.newItems.push(item);
|
this.newItems.push(item);
|
||||||
Zotero.Connector.callMethod("saveItems", {"items":[item]}, function(success) {});
|
var me = this;
|
||||||
|
Zotero.Connector.callMethod("saveItems", {"items":[item]}, function(success) {
|
||||||
|
if(success === false && !Zotero.isFx) {
|
||||||
|
// attempt to save to server on a timer
|
||||||
|
if(me._timeoutID) clearTimeout(me._timeoutID);
|
||||||
|
me._itemsToSaveToServer.push(item);
|
||||||
|
setTimeout(function() { me._saveToServer() }, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
"_saveToServer":function() {
|
||||||
|
const IGNORE_FIELDS = ["seeAlso", "attachments", "complete"];
|
||||||
|
|
||||||
|
// clear timeout, since saving has begin
|
||||||
|
this._timeoutID = null;
|
||||||
|
|
||||||
|
var newItems = new Array(this._itemsToSaveToServer.length);
|
||||||
|
for(var i in this._itemsToSaveToServer) {
|
||||||
|
var item = this._itemsToSaveToServer[i];
|
||||||
|
var newItem = newItems[i] = {};
|
||||||
|
|
||||||
|
var typeID = Zotero.ItemTypes.getID(item.itemType);
|
||||||
|
var fieldID;
|
||||||
|
for(var field in item) {
|
||||||
|
if(IGNORE_FIELDS.indexOf(field) !== -1) continue;
|
||||||
|
|
||||||
|
var val = item[field];
|
||||||
|
|
||||||
|
if(field === "itemType") {
|
||||||
|
newItem[field] = val;
|
||||||
|
} else if(field === "creators") {
|
||||||
|
// TODO normalize
|
||||||
|
newItem[field] = val;
|
||||||
|
} else if(field === "tags") {
|
||||||
|
// TODO normalize
|
||||||
|
newItem[field] = val;
|
||||||
|
} else if(field === "notes") {
|
||||||
|
// TODO normalize
|
||||||
|
newItem[field] = val;
|
||||||
|
} else if(fieldID = Zotero.ItemFields.getID(field)) {
|
||||||
|
// if content is not a string, either stringify it or delete it
|
||||||
|
if(typeof val !== "string") {
|
||||||
|
if(val || val === 0) {
|
||||||
|
val = val.toString();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// map from base field if possible
|
||||||
|
var itemFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(typeID, fieldID);
|
||||||
|
if(itemFieldID) {
|
||||||
|
newItem[Zotero.ItemFields.getName(itemFieldID)] = val;
|
||||||
|
continue; // already know this is valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// if field is valid for this type, set field
|
||||||
|
if(Zotero.ItemFields.isValidForType(fieldID, typeID)) {
|
||||||
|
newItem[field] = val;
|
||||||
|
} else {
|
||||||
|
Zotero.debug("Translate: Discarded field "+field+": field not valid for type "+item.itemType, 3);
|
||||||
|
}
|
||||||
|
} else if(field !== "complete") {
|
||||||
|
Zotero.debug("Translate: Discarded unknown field "+field, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = 'users/%%USERID%%/items?key=%%APIKEY%%';
|
||||||
|
var payload = JSON.stringify({"items":newItems});
|
||||||
|
this._itemsToSaveToServer = [];
|
||||||
|
|
||||||
|
Zotero.OAuth.doAuthenticatedPost(url, payload, function(status, message) {
|
||||||
|
if(!status) {
|
||||||
|
Zotero.Messaging.sendMessage("saveDialog_error", status);
|
||||||
|
throw new Error("Translate: Save to server failed: "+message);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -36,15 +36,28 @@ Zotero.Translators = new function() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes translator cache, loading all relevant translators into memory
|
* Initializes translator cache, loading all relevant translators into memory
|
||||||
|
* @param {Zotero.Translate[]} [translators] List of translators. If not specified, it will be
|
||||||
|
* retrieved from storage.
|
||||||
*/
|
*/
|
||||||
this.init = function() {
|
this.init = function(translators) {
|
||||||
|
if(!translators) {
|
||||||
|
translators = [];
|
||||||
|
if(!Zotero.isFx && localStorage["translatorMetadata"]) {
|
||||||
|
try {
|
||||||
|
translators = JSON.parse(localStorage["translatorMetadata"]);
|
||||||
|
if(typeof translators !== "object") {
|
||||||
|
translators = [];
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_cache = {"import":[], "export":[], "web":[], "search":[]};
|
_cache = {"import":[], "export":[], "web":[], "search":[]};
|
||||||
_translators = {};
|
_translators = {};
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
// Build caches
|
// Build caches
|
||||||
var translators = Zotero.Connector.data.translators;
|
for(var i in translators) {
|
||||||
for(var i=0; i<translators.length; i++) {
|
|
||||||
var translator = new Zotero.Translator(translators[i]);
|
var translator = new Zotero.Translator(translators[i]);
|
||||||
_translators[translator.translatorID] = translator;
|
_translators[translator.translatorID] = translator;
|
||||||
|
|
||||||
|
@ -185,24 +198,104 @@ Zotero.Translators = new function() {
|
||||||
/**
|
/**
|
||||||
* Converts translators to JSON-serializable objects
|
* Converts translators to JSON-serializable objects
|
||||||
*/
|
*/
|
||||||
this.serialize = function(translator) {
|
this.serialize = function(translator, properties) {
|
||||||
// handle translator arrays
|
// handle translator arrays
|
||||||
if(translator.length !== undefined) {
|
if(translator.length !== undefined) {
|
||||||
var newTranslators = new Array(translator.length);
|
var newTranslators = new Array(translator.length);
|
||||||
for(var i in translator) {
|
for(var i in translator) {
|
||||||
newTranslators[i] = Zotero.Translators.serialize(translator[i]);
|
newTranslators[i] = Zotero.Translators.serialize(translator[i], properties);
|
||||||
}
|
}
|
||||||
return newTranslators;
|
return newTranslators;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle individual translator
|
// handle individual translator
|
||||||
var newTranslator = {};
|
var newTranslator = {};
|
||||||
for(var i in PRESERVE_PROPERTIES) {
|
for(var i in properties) {
|
||||||
var property = PRESERVE_PROPERTIES[i];
|
var property = properties[i];
|
||||||
newTranslator[property] = translator[property];
|
newTranslator[property] = translator[property];
|
||||||
}
|
}
|
||||||
return newTranslator;
|
return newTranslator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves all translator data to localStorage
|
||||||
|
*/
|
||||||
|
this.update = function(newMetadata, reset) {
|
||||||
|
if(!_initialized) Zotero.Translators.init();
|
||||||
|
if(!newMetadata.length) return;
|
||||||
|
|
||||||
|
if(reset) {
|
||||||
|
var serializedTranslators = newMetadata;
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
// clear cached translatorCode
|
||||||
|
Zotero.debug("Translators: Resetting translators");
|
||||||
|
for(var i in localStorage) {
|
||||||
|
if(i.substr(0, TRANSLATOR_CODE_PREFIX.length) === TRANSLATOR_CODE_PREFIX) {
|
||||||
|
delete localStorage[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var serializedTranslators = [];
|
||||||
|
var hasChanged = false;
|
||||||
|
|
||||||
|
// Update translators with new metadata
|
||||||
|
for(var i in newMetadata) {
|
||||||
|
var newTranslator = new Zotero.Translator(newMetadata[i]);
|
||||||
|
|
||||||
|
if(_translators.hasOwnProperty(newTranslator.translatorID)) {
|
||||||
|
if(_translators[newTranslator.translatorID].lastUpdated !== newTranslator.lastUpdated) {
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
// if lastUpdated does not match between old and new translator
|
||||||
|
// invalidate translator code cache
|
||||||
|
delete localStorage["translatorCode-"+newTranslator.translatorID];
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Translators: Updating "+newTranslator.label);
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Zotero.debug("Translators: Adding "+newTranslator.label);
|
||||||
|
hasChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_translators[newTranslator.translatorID] = newTranslator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!hasChanged) return;
|
||||||
|
|
||||||
|
// Serialize translators
|
||||||
|
for(var i in _translators) {
|
||||||
|
var serializedTranslator = this.serialize(_translators[i], TRANSLATOR_SAVE_PROPERTIES);
|
||||||
|
|
||||||
|
// don't save run mode
|
||||||
|
delete serializedTranslator.runMode;
|
||||||
|
|
||||||
|
serializedTranslators.push(serializedTranslator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
localStorage["translatorMetadata"] = JSON.stringify(serializedTranslators);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinitialize
|
||||||
|
Zotero.Translators.init(serializedTranslators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preprocesses code for a translator
|
||||||
|
*/
|
||||||
|
this.preprocessCode = function(code) {
|
||||||
|
if(!Zotero.isFx) {
|
||||||
|
const foreach = /^(\s*)for each\s*\((var )?([^ ]+) in (.*?)\)(\s*){/gm;
|
||||||
|
code = code.replace(foreach, "$1var $3_zForEachSubject = $4; "+
|
||||||
|
"for(var $3_zForEachIndex in $3_zForEachSubject)$5{ "+
|
||||||
|
"$2$3 = $3_zForEachSubject[$3_zForEachIndex];", code);
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,10 +335,11 @@ Zotero.Translators.CodeGetter.prototype.getCodeFor = function(i) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TRANSLATOR_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
|
const TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
|
||||||
"priority", "browserSupport"];
|
"priority"];
|
||||||
var PRESERVE_PROPERTIES = TRANSLATOR_PROPERTIES.concat(["displayOptions", "configOptions",
|
var TRANSLATOR_PASSING_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["displayOptions", "configOptions",
|
||||||
"code", "runMode"]);
|
"browserSupport", "code", "runMode"]);
|
||||||
|
var TRANSLATOR_SAVE_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["browserSupport"]);
|
||||||
/**
|
/**
|
||||||
* @class Represents an individual translator
|
* @class Represents an individual translator
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -270,8 +364,8 @@ var PRESERVE_PROPERTIES = TRANSLATOR_PROPERTIES.concat(["displayOptions", "confi
|
||||||
*/
|
*/
|
||||||
Zotero.Translator = function(info) {
|
Zotero.Translator = function(info) {
|
||||||
// make sure we have all the properties
|
// make sure we have all the properties
|
||||||
for(var i in TRANSLATOR_PROPERTIES) {
|
for(var i in TRANSLATOR_REQUIRED_PROPERTIES) {
|
||||||
var property = TRANSLATOR_PROPERTIES[i];
|
var property = TRANSLATOR_REQUIRED_PROPERTIES[i];
|
||||||
if(info[property] === undefined) {
|
if(info[property] === undefined) {
|
||||||
this.logError('Missing property "'+property+'" in translator metadata JSON object in ' + info.label);
|
this.logError('Missing property "'+property+'" in translator metadata JSON object in ' + info.label);
|
||||||
haveMetadata = false;
|
haveMetadata = false;
|
||||||
|
@ -281,7 +375,9 @@ Zotero.Translator = function(info) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(info["browserSupport"].indexOf(Zotero.browser) !== -1) {
|
this.browserSupport = info["browserSupport"] ? info["browserSupport"] : "g";
|
||||||
|
|
||||||
|
if(this.browserSupport.indexOf(Zotero.browser) !== -1) {
|
||||||
this.runMode = Zotero.Translator.RUN_MODE_IN_BROWSER;
|
this.runMode = Zotero.Translator.RUN_MODE_IN_BROWSER;
|
||||||
} else {
|
} else {
|
||||||
this.runMode = Zotero.Translator.RUN_MODE_ZOTERO_STANDALONE;
|
this.runMode = Zotero.Translator.RUN_MODE_ZOTERO_STANDALONE;
|
||||||
|
@ -313,12 +409,13 @@ Zotero.Translator.prototype.getCode = function(callback) {
|
||||||
callback(true);
|
callback(true);
|
||||||
} else {
|
} else {
|
||||||
var me = this;
|
var me = this;
|
||||||
Zotero.Connector.callMethod("getTranslatorCode", {"translatorID":this.translatorID},
|
Zotero.Repo.getTranslatorCode(this.translatorID,
|
||||||
function(code) {
|
function(code) {
|
||||||
if(!code) {
|
if(!code) {
|
||||||
callback(false);
|
callback(false);
|
||||||
} else {
|
} else {
|
||||||
me.code = me.preprocessCode(code);
|
// cache code for session only (we have standalone anyway)
|
||||||
|
me.code = code;
|
||||||
callback(true);
|
callback(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,20 +423,6 @@ Zotero.Translator.prototype.getCode = function(callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Preprocesses code for this translator
|
|
||||||
*/
|
|
||||||
Zotero.Translator.prototype.preprocessCode = function(code) {
|
|
||||||
if(!Zotero.isFx) {
|
|
||||||
const foreach = /^(\s*)for each\s*\((var )?([^ ]+) in (.*?)\)(\s*){/gm;
|
|
||||||
code = code.replace(foreach, "$1var $3_zForEachSubject = $4; "+
|
|
||||||
"for(var $3_zForEachIndex in $3_zForEachSubject)$5{ "+
|
|
||||||
"$2$3 = $3_zForEachSubject[$3_zForEachIndex];", code);
|
|
||||||
Zotero.debug(code);
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Translator.prototype.__defineGetter__("displayOptions", function() {
|
Zotero.Translator.prototype.__defineGetter__("displayOptions", function() {
|
||||||
return Zotero.Utilities.deepCopy(this._displayOptions);
|
return Zotero.Utilities.deepCopy(this._displayOptions);
|
||||||
});
|
});
|
||||||
|
@ -356,7 +439,7 @@ Zotero.Translator.prototype.__defineGetter__("configOptions", function() {
|
||||||
* @param {Integer} colNumber
|
* @param {Integer} colNumber
|
||||||
*/
|
*/
|
||||||
Zotero.Translator.prototype.logError = function(message, type, line, lineNumber, colNumber) {
|
Zotero.Translator.prototype.logError = function(message, type, line, lineNumber, colNumber) {
|
||||||
Zotero.log(message, type ? type : "error", this.label);
|
Zotero.logError(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Translator.RUN_MODE_IN_BROWSER = 1;
|
Zotero.Translator.RUN_MODE_IN_BROWSER = 1;
|
||||||
|
|
1
chrome/content/zotero/xpcom/connector/typeSchemaData.js
Normal file
1
chrome/content/zotero/xpcom/connector/typeSchemaData.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CONNECTOR_SERVER_API_VERSION = 1;
|
const CONNECTOR_SERVER_API_VERSION = 2;
|
||||||
|
|
||||||
Zotero.Server.Connector = function() {};
|
Zotero.Server.Connector = function() {};
|
||||||
Zotero.Server.Connector._waitingForSelection = {};
|
Zotero.Server.Connector._waitingForSelection = {};
|
||||||
|
@ -196,30 +196,13 @@ Zotero.Server.Connector.CookieManager.prototype = {
|
||||||
* Lists all available translators, including code for translators that should be run on every page
|
* Lists all available translators, including code for translators that should be run on every page
|
||||||
*
|
*
|
||||||
* Accepts:
|
* Accepts:
|
||||||
* browser - one-letter code of the current browser
|
* Nothing
|
||||||
* g = Gecko (Firefox)
|
|
||||||
* c = Google Chrome (WebKit & V8)
|
|
||||||
* s = Safari (WebKit & Nitro/Squirrelfish Extreme)
|
|
||||||
* i = Internet Explorer
|
|
||||||
* Returns:
|
* Returns:
|
||||||
* translators - Zotero.Translator objects
|
* Array of Zotero.Translator objects
|
||||||
* schema - Some information about the database. Currently includes:
|
|
||||||
* itemTypes
|
|
||||||
* name
|
|
||||||
* localizedString
|
|
||||||
* creatorTypes
|
|
||||||
* fields
|
|
||||||
* baseFields
|
|
||||||
* creatorTypes
|
|
||||||
* name
|
|
||||||
* localizedString
|
|
||||||
* fields
|
|
||||||
* name
|
|
||||||
* localizedString
|
|
||||||
*/
|
*/
|
||||||
Zotero.Server.Connector.GetData = function() {};
|
Zotero.Server.Connector.GetTranslators = function() {};
|
||||||
Zotero.Server.Endpoints["/connector/getData"] = Zotero.Server.Connector.GetData;
|
Zotero.Server.Endpoints["/connector/getTranslators"] = Zotero.Server.Connector.GetTranslators;
|
||||||
Zotero.Server.Connector.GetData.prototype = {
|
Zotero.Server.Connector.GetTranslators.prototype = {
|
||||||
"supportedMethods":["POST"],
|
"supportedMethods":["POST"],
|
||||||
"supportedDataTypes":["application/json"],
|
"supportedDataTypes":["application/json"],
|
||||||
|
|
||||||
|
@ -229,12 +212,8 @@ Zotero.Server.Connector.GetData.prototype = {
|
||||||
* @param {Function} sendResponseCallback function to send HTTP response
|
* @param {Function} sendResponseCallback function to send HTTP response
|
||||||
*/
|
*/
|
||||||
"init":function(data, sendResponseCallback) {
|
"init":function(data, sendResponseCallback) {
|
||||||
if(data.apiVersion !== CONNECTOR_SERVER_API_VERSION) {
|
|
||||||
sendResponseCallback(412);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translator data
|
// Translator data
|
||||||
var responseData = {"preferences":{}, "translators":[]};
|
var responseData = [];
|
||||||
|
|
||||||
// TODO only send necessary translators
|
// TODO only send necessary translators
|
||||||
var translators = Zotero.Translators.getAll();
|
var translators = Zotero.Translators.getAll();
|
||||||
|
@ -247,68 +226,10 @@ Zotero.Server.Connector.GetData.prototype = {
|
||||||
|
|
||||||
// Do not pass targetless translators that do not support this browser (since that
|
// Do not pass targetless translators that do not support this browser (since that
|
||||||
// would mean passing each page back to Zotero)
|
// would mean passing each page back to Zotero)
|
||||||
responseData.translators.push(serializableTranslator);
|
responseData.push(serializableTranslator);
|
||||||
}
|
|
||||||
|
|
||||||
// Various DB data (only sending what is required at the moment)
|
|
||||||
var systemVersion = Zotero.Schema.getDBVersion("system");
|
|
||||||
if(systemVersion != data.systemVersion) {
|
|
||||||
responseData.schema = this._generateTypeSchema();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Preferences
|
|
||||||
var prefs = Zotero.Prefs.prefBranch.getChildList("", {}, {});
|
|
||||||
for each(var pref in prefs) {
|
|
||||||
responseData.preferences[pref] = Zotero.Prefs.get(pref);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResponseCallback(200, "application/json", JSON.stringify(responseData));
|
sendResponseCallback(200, "application/json", JSON.stringify(responseData));
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a type schema. This is used by connector/type.js to handle types without DB access.
|
|
||||||
*/
|
|
||||||
"_generateTypeSchema":function() {
|
|
||||||
var schema = {"itemTypes":{}, "creatorTypes":{}, "fields":{}};
|
|
||||||
var types = Zotero.ItemTypes.getTypes();
|
|
||||||
|
|
||||||
var fieldIDs = Zotero.DB.columnQuery("SELECT fieldID FROM fieldsCombined");
|
|
||||||
var baseMappedFields = Zotero.ItemFields.getBaseMappedFields();
|
|
||||||
for each(var fieldID in fieldIDs) {
|
|
||||||
var fieldObj = {"name":Zotero.ItemFields.getName(fieldID)};
|
|
||||||
try {
|
|
||||||
fieldObj.localizedString = Zotero.getString("itemFields." + fieldObj.name)
|
|
||||||
} catch(e) {}
|
|
||||||
schema.fields[fieldID] = fieldObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// names, localizedStrings, creatorTypes, and fields for each item type
|
|
||||||
for each(var type in types) {
|
|
||||||
var fieldIDs = Zotero.ItemFields.getItemTypeFields(type.id);
|
|
||||||
var baseFields = {};
|
|
||||||
for each(var fieldID in fieldIDs) {
|
|
||||||
if(baseMappedFields.indexOf(fieldID) !== -1) {
|
|
||||||
baseFields[fieldID] = Zotero.ItemFields.getFieldIDFromTypeAndBase(type.id, fieldID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var icon = Zotero.ItemTypes.getImageSrc(type.name);
|
|
||||||
icon = icon.substr(icon.lastIndexOf("/")+1);
|
|
||||||
|
|
||||||
schema.itemTypes[type.id] = {"name":type.name,
|
|
||||||
"localizedString":Zotero.ItemTypes.getLocalizedString(type.name),
|
|
||||||
"creatorTypes":[creatorType.id for each(creatorType in Zotero.CreatorTypes.getTypesForItemType(type.id))],
|
|
||||||
"fields":fieldIDs, "baseFields":baseFields, "icon":icon};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var types = Zotero.CreatorTypes.getTypes();
|
|
||||||
for each(var type in types) {
|
|
||||||
schema.creatorTypes[type.id] = {"name":type.name,
|
|
||||||
"localizedString":Zotero.CreatorTypes.getLocalizedString(type.name)};
|
|
||||||
}
|
|
||||||
|
|
||||||
return schema;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,3 +532,28 @@ Zotero.Server.Connector.GetTranslatorCode.prototype = {
|
||||||
sendResponseCallback(200, "application/javascript", translator.code);
|
sendResponseCallback(200, "application/javascript", translator.code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test connection
|
||||||
|
*
|
||||||
|
* Accepts:
|
||||||
|
* Nothing
|
||||||
|
* Returns:
|
||||||
|
* Nothing (200 OK response)
|
||||||
|
*/
|
||||||
|
Zotero.Server.Connector.Ping = function() {};
|
||||||
|
Zotero.Server.Endpoints["/connector/ping"] = Zotero.Server.Connector.Ping;
|
||||||
|
Zotero.Server.Connector.Ping.prototype = {
|
||||||
|
"supportedMethods":["POST"],
|
||||||
|
"supportedDataTypes":["application/json"],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes up translation when item selection is complete
|
||||||
|
* @param {String} data POST data or GET query string
|
||||||
|
* @param {Function} sendResponseCallback function to send HTTP response
|
||||||
|
*/
|
||||||
|
"init":function(postData, sendResponseCallback) {
|
||||||
|
sendResponseCallback(200);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1633,7 +1633,7 @@ Zotero.Translate.Search.prototype.setTranslator = function(translator) {
|
||||||
* translation fails
|
* translation fails
|
||||||
*/
|
*/
|
||||||
Zotero.Translate.Search.prototype.complete = function(returnValue, error) {
|
Zotero.Translate.Search.prototype.complete = function(returnValue, error) {
|
||||||
if(this._currentState == "translate" && !this.newItems.length) {
|
if(this._currentState == "translate" && (!this.newItems || !this.newItems.length)) {
|
||||||
Zotero.debug("Translate: Could not find a result using "+this.translator[0].label+": \n"
|
Zotero.debug("Translate: Could not find a result using "+this.translator[0].label+": \n"
|
||||||
+this._generateErrorString(error), 3);
|
+this._generateErrorString(error), 3);
|
||||||
if(this.translator.length > 1) {
|
if(this.translator.length > 1) {
|
||||||
|
|
|
@ -538,7 +538,7 @@ Zotero.Utilities = {
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
"deepCopy":function(obj) {
|
"deepCopy":function(obj) {
|
||||||
var obj2 = {};
|
var obj2 = (obj instanceof Array ? [] : {});
|
||||||
for(var i in obj) {
|
for(var i in obj) {
|
||||||
if(typeof obj[i] === "object") {
|
if(typeof obj[i] === "object") {
|
||||||
obj2[i] = Zotero.Utilities.deepCopy(obj[i]);
|
obj2[i] = Zotero.Utilities.deepCopy(obj[i]);
|
||||||
|
|
|
@ -424,7 +424,16 @@ if(appInfo.platformVersion[0] >= 2) {
|
||||||
// Load additional info for connector or not
|
// Load additional info for connector or not
|
||||||
if(Zotero.isConnector) {
|
if(Zotero.isConnector) {
|
||||||
Zotero.debug("Loading in connector mode");
|
Zotero.debug("Loading in connector mode");
|
||||||
Zotero.Connector.init();
|
Zotero.Connector_Types.init();
|
||||||
|
|
||||||
|
if(!Zotero.isFirstLoadThisSession) {
|
||||||
|
// wait for initComplete message if we switched to connector because standalone was
|
||||||
|
// started
|
||||||
|
_waitingForInitComplete = true;
|
||||||
|
while(_waitingForInitComplete) Zotero.mainThread.processNextEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.Repo.init();
|
||||||
} else {
|
} else {
|
||||||
Zotero.debug("Loading in full mode");
|
Zotero.debug("Loading in full mode");
|
||||||
_initFull();
|
_initFull();
|
||||||
|
@ -444,13 +453,6 @@ if(appInfo.platformVersion[0] >= 2) {
|
||||||
Zotero.debug("Initialized in "+((new Date()).getTime() - start)+" ms");
|
Zotero.debug("Initialized in "+((new Date()).getTime() - start)+" ms");
|
||||||
|
|
||||||
if(!Zotero.isFirstLoadThisSession) {
|
if(!Zotero.isFirstLoadThisSession) {
|
||||||
if(Zotero.isConnector) {
|
|
||||||
// wait for initComplete message if we switched to connector because standalone was
|
|
||||||
// started
|
|
||||||
_waitingForInitComplete = true;
|
|
||||||
while(_waitingForInitComplete) Zotero.mainThread.processNextEvent(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger zotero-reloaded event
|
// trigger zotero-reloaded event
|
||||||
Zotero.debug('Triggering "zotero-reloaded" event');
|
Zotero.debug('Triggering "zotero-reloaded" event');
|
||||||
observerService.notifyObservers(Zotero, "zotero-reloaded", null);
|
observerService.notifyObservers(Zotero, "zotero-reloaded", null);
|
||||||
|
|
|
@ -114,7 +114,9 @@ const xpcomFilesConnector = [
|
||||||
'connector/translate_item',
|
'connector/translate_item',
|
||||||
'connector/translator',
|
'connector/translator',
|
||||||
'connector/connector',
|
'connector/connector',
|
||||||
'connector/cachedTypes'
|
'connector/cachedTypes',
|
||||||
|
'connector/repo',
|
||||||
|
'connector/typeSchemaData'
|
||||||
];
|
];
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
|
@ -153,3 +153,7 @@ pref("extensions.zotero.pane.persist", '');
|
||||||
|
|
||||||
// Domains allowed to import, separated by a semicolon
|
// Domains allowed to import, separated by a semicolon
|
||||||
pref("extensions.zotero.ingester.allowedSites", "");
|
pref("extensions.zotero.ingester.allowedSites", "");
|
||||||
|
|
||||||
|
// Connector
|
||||||
|
pref("extensions.zotero.connector.repo.lastCheck.localTime", 0);
|
||||||
|
pref("extensions.zotero.connector.repo.lastCheck.repoTime", 0);
|
Loading…
Reference in a new issue