Merge branch 'fx60'

This commit is contained in:
Dan Stillman 2019-10-21 21:44:01 -04:00
commit d4f682aa88
152 changed files with 13336 additions and 5410 deletions

3
.gitmodules vendored
View file

@ -22,3 +22,6 @@
path = test/resource/chai-as-promised
url = https://github.com/domenic/chai-as-promised.git
branch = master
[submodule "resource/schema/global"]
path = resource/schema/global
url = git://github.com/zotero/zotero-schema.git

View file

@ -10,7 +10,7 @@ env:
global:
secure: "NxvkbZ7/Op7BTGQRR3C4q8lLoO29f8WtyNN27NSH7AO3H0vBr1Vp5xO8gn+H2qHEug5HvM+YrZ/xAkNXaZVbOInmBmKVMxqVvdpKp9JM1Amf+gzsXWQphfySvs6iqzyP6cwU/jspdvX/WSakgU5v7PWXxtUIaKxdANt6Rw7W+Pc="
matrix:
- FX_VERSION="52.0.3"
- FX_VERSION="60.8.0esr"
matrix:
fast_finish: true
#allow_failures:
@ -24,15 +24,13 @@ addons:
services:
- xvfb
install:
- if [ $FX_VERSION = "52.0.3" ]; then
wget -O tarball "https://archive.mozilla.org/pub/firefox/tinderbox-builds/mozilla-release-linux64-add-on-devel/1491732920/firefox-52.0.3.en-US.linux-x86_64-add-on-devel.tar.bz2";
fi
- wget -O tarball "https://zotero-download.s3.us-east-1.amazonaws.com/dev/firefox-60.8.0esr-x86_64-modified.tar.gz";
- tar xf tarball
before_script:
- npm i
- npm run build
- if [[ $TRAVIS_REPO_SLUG = "zotero/zotero" &&
($TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = *-hotfix) &&
($TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = *-hotfix || $TRAVIS_BRANCH = "fx60") &&
$TRAVIS_PULL_REQUEST = "false" ]]; then
mkdir build-zip;
cd build;
@ -51,4 +49,4 @@ before_script:
# Fix warnings in output
- dbus-launch
script:
- test/runtests.sh -x firefox/firefox -f
- test/runtests.sh -x firefox-x86_64/firefox -f

View file

@ -0,0 +1,9 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="chrome://scaffold/content/ace/ace.js"></script>
<script type="text/javascript" src="chrome://scaffold/content/ace/mode-javascript.js"></script>
<script type="text/javascript" src="chrome://scaffold/content/aceWrapper.js"></script>
</head>
<body id="body"></body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,6 @@ background-color: white;\
color: black;\
border: 1px solid #cbcbcb;\
border-right: 0 none;\
box-sizing: border-box!important;\
outline: 0;\
padding: 0;\
font-size: inherit;\
@ -54,6 +53,8 @@ line-height: inherit;\
padding: 0 6px;\
min-width: 17em;\
vertical-align: top;\
min-height: 1.8em;\
box-sizing: content-box;\
}\
.ace_searchbtn {\
border: 1px solid #cbcbcb;\
@ -66,7 +67,6 @@ border-left: 1px solid #dcdcdc;\
cursor: pointer;\
margin: 0;\
position: relative;\
box-sizing: content-box!important;\
color: #666;\
}\
.ace_searchbtn:last-child {\
@ -162,39 +162,38 @@ var MAX_COUNT = 999;
dom.importCssString(searchboxCss, "ace_searchbox");
var html = '<div class="ace_search right">\
<span action="hide" class="ace_searchbtn_close"></span>\
<div class="ace_search_form">\
<input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
<span action="findPrev" class="ace_searchbtn prev"></span>\
<span action="findNext" class="ace_searchbtn next"></span>\
<span action="findAll" class="ace_searchbtn" title="Alt-Enter">All</span>\
</div>\
<div class="ace_replace_form">\
<input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
<span action="replaceAndFindNext" class="ace_searchbtn">Replace</span>\
<span action="replaceAll" class="ace_searchbtn">All</span>\
</div>\
<div class="ace_search_options">\
<span action="toggleReplace" class="ace_button" title="Toggel Replace mode"\
style="float:left;margin-top:-2px;padding:0 5px;">+</span>\
<span class="ace_search_counter"></span>\
<span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
<span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
<span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
<span action="searchInSelection" class="ace_button" title="Search In Selection">S</span>\
</div>\
</div>'.replace(/> +/g, ">");
var SearchBox = function(editor, range, showReplaceForm) {
var div = dom.createElement("div");
div.innerHTML = html;
dom.buildDom(["div", {class:"ace_search right"},
["span", {action: "hide", class: "ace_searchbtn_close"}],
["div", {class: "ace_search_form"},
["input", {class: "ace_search_field", placeholder: "Search for", spellcheck: "false"}],
["span", {action: "findPrev", class: "ace_searchbtn prev"}, "\u200b"],
["span", {action: "findNext", class: "ace_searchbtn next"}, "\u200b"],
["span", {action: "findAll", class: "ace_searchbtn", title: "Alt-Enter"}, "All"]
],
["div", {class: "ace_replace_form"},
["input", {class: "ace_search_field", placeholder: "Replace with", spellcheck: "false"}],
["span", {action: "replaceAndFindNext", class: "ace_searchbtn"}, "Replace"],
["span", {action: "replaceAll", class: "ace_searchbtn"}, "All"]
],
["div", {class: "ace_search_options"},
["span", {action: "toggleReplace", class: "ace_button", title: "Toggle Replace mode",
style: "float:left;margin-top:-2px;padding:0 5px;"}, "+"],
["span", {class: "ace_search_counter"}],
["span", {action: "toggleRegexpMode", class: "ace_button", title: "RegExp Search"}, ".*"],
["span", {action: "toggleCaseSensitive", class: "ace_button", title: "CaseSensitive Search"}, "Aa"],
["span", {action: "toggleWholeWords", class: "ace_button", title: "Whole Word Search"}, "\\b"],
["span", {action: "searchInSelection", class: "ace_button", title: "Search In Selection"}, "S"]
]
], div);
this.element = div.firstChild;
this.setSession = this.setSession.bind(this);
this.$init();
this.setEditor(editor);
dom.importCssString(searchboxCss, "ace_searchbox", editor.container);
};
(function() {
@ -205,10 +204,9 @@ var SearchBox = function(editor, range, showReplaceForm) {
};
this.setSession = function(e) {
debugger
this.searchRange = null;
this.$syncOptions(true);
}
};
this.$initElements = function(sb) {
this.searchBox = sb.querySelector(".ace_search_form");
@ -287,6 +285,8 @@ var SearchBox = function(editor, range, showReplaceForm) {
sb.searchInput.focus();
},
"Ctrl-H|Command-Option-F": function(sb) {
if (sb.editor.getReadOnly())
return;
sb.replaceOption.checked = true;
sb.$syncOptions();
sb.replaceInput.focus();
@ -364,7 +364,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
this.editor.session.removeMarker(this.searchRangeMarker);
this.searchRangeMarker = null;
}
}
};
this.$syncOptions = function(preventScroll) {
dom.setCssClass(this.replaceOption, "checked", this.searchRange);
@ -373,13 +373,15 @@ var SearchBox = function(editor, range, showReplaceForm) {
dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
this.replaceBox.style.display = this.replaceOption.checked ? "" : "none";
var readOnly = this.editor.getReadOnly();
this.replaceOption.style.display = readOnly ? "none" : "";
this.replaceBox.style.display = this.replaceOption.checked && !readOnly ? "" : "none";
this.find(false, false, preventScroll);
};
this.highlight = function(re) {
this.editor.session.highlight(re || this.editor.$search.$options.re);
this.editor.renderer.updateBackMarkers()
this.editor.renderer.updateBackMarkers();
};
this.find = function(skipCurrent, backwards, preventScroll) {
var range = this.editor.find(this.searchInput.value, {
@ -402,7 +404,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
var editor = this.editor;
var regex = editor.$search.$options.re;
var all = 0;
var before = 0
var before = 0;
if (regex) {
var value = this.searchRange
? editor.session.getTextRange(this.searchRange)
@ -420,7 +422,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
if (last <= offset)
before++;
if (all > MAX_COUNT)
break
break;
if (!m[0]) {
regex.lastIndex = last += 1;
if (last >= value.length)
@ -455,7 +457,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
this.replaceAndFindNext = function() {
if (!this.editor.getReadOnly()) {
this.editor.replace(this.replaceInput.value);
this.findNext()
this.findNext();
}
};
this.replaceAll = function() {
@ -465,7 +467,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
this.hide = function() {
this.active = false;
this.setSearchRange(null)
this.setSearchRange(null);
this.editor.off("changeSession", this.setSession);
this.element.style.display = "none";
@ -492,7 +494,7 @@ var SearchBox = function(editor, range, showReplaceForm) {
this.isFocused = function() {
var el = document.activeElement;
return el == this.searchInput || el == this.replaceInput;
}
};
}).call(SearchBox.prototype);
exports.SearchBox = SearchBox;
@ -502,8 +504,11 @@ exports.Search = function(editor, isReplace) {
sb.show(editor.session.getTextRange(), isReplace);
};
});
(function() {
window.require(["ace/ext/searchbox"], function() {});
}); (function() {
window.require(["ace/ext/searchbox"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View file

@ -18,7 +18,7 @@ oop.inherits(Occur, Search);
var translatedPos = this.originalToOccurPosition(editor.session, pos);
editor.moveCursorToPosition(translatedPos);
return true;
}
};
this.exit = function(editor, options) {
var pos = options.translatePosition && editor.getCursorPosition();
var translatedPos = pos && this.occurToOriginalPosition(editor.session, pos);
@ -26,14 +26,14 @@ oop.inherits(Occur, Search);
if (translatedPos)
editor.moveCursorToPosition(translatedPos);
return true;
}
};
this.highlight = function(sess, regexp) {
var hl = sess.$occurHighlight = sess.$occurHighlight || sess.addDynamicMarker(
new SearchHighlight(null, "ace_occur-highlight", "text"));
hl.setRegexp(regexp);
sess._emit("changeBackMarker"); // force highlight layer redraw
}
};
this.displayOccurContent = function(editor, options) {
this.$originalSession = editor.session;
@ -47,12 +47,12 @@ oop.inherits(Occur, Search);
occurSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
this.highlight(occurSession, options.re);
occurSession._emit('changeBackMarker');
}
};
this.displayOriginalContent = function(editor) {
editor.setSession(this.$originalSession);
this.$originalSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
}
};
this.originalToOccurPosition = function(session, pos) {
var lines = session.$occurMatchingLines;
var nullPos = {row: 0, column: 0};
@ -62,13 +62,13 @@ oop.inherits(Occur, Search);
return {row: i, column: pos.column};
}
return nullPos;
}
};
this.occurToOriginalPosition = function(session, pos) {
var lines = session.$occurMatchingLines;
if (!lines || !lines[pos.row])
return pos;
return {row: lines[pos.row].row, column: pos.column};
}
};
this.matchingLines = function(session, options) {
options = oop.mixin({}, options);
@ -82,7 +82,7 @@ oop.inherits(Occur, Search);
lines :
lines.concat({row: row, content: session.getLine(row)});
}, []);
}
};
}).call(Occur.prototype);
@ -92,8 +92,6 @@ dom.importCssString(".ace_occur-highlight {\n\
background-color: rgba(87, 255, 8, 0.25);\n\
position: absolute;\n\
z-index: 4;\n\
-moz-box-sizing: border-box;\n\
-webkit-box-sizing: border-box;\n\
box-sizing: border-box;\n\
box-shadow: 0 0 4px rgb(91, 255, 50);\n\
}\n\
@ -158,13 +156,13 @@ oop.inherits(OccurKeyboardHandler, HashHandler);
this.attach = function(editor) {
HashHandler.call(this, occurCommands, editor.commands.platform);
this.$editor = editor;
}
};
var handleKeyboard$super = this.handleKeyboard;
this.handleKeyboard = function(data, hashId, key, keyCode) {
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
return (cmd && cmd.command) ? cmd : undefined;
}
};
}).call(OccurKeyboardHandler.prototype);
@ -172,14 +170,14 @@ OccurKeyboardHandler.installIn = function(editor) {
var handler = new this();
editor.keyBinding.addKeyboardHandler(handler);
editor.commands.addCommands(occurCommands);
}
};
OccurKeyboardHandler.uninstallFrom = function(editor) {
editor.commands.removeCommands(occurCommands);
var handler = editor.getKeyboardHandler();
if (handler.isOccurHandler)
editor.keyBinding.removeKeyboardHandler(handler);
}
};
exports.occurStartCommand = occurStartCommand;
@ -393,7 +391,7 @@ function regExpToObject(re) {
return {
expression: string.slice(start+1, flagStart),
flags: string.slice(flagStart+1)
}
};
}
function stringToRegExp(string, flags) {
@ -565,8 +563,6 @@ dom.importCssString && dom.importCssString("\
.ace_marker-layer .ace_isearch-result {\
position: absolute;\
z-index: 6;\
-moz-box-sizing: border-box;\
-webkit-box-sizing: border-box;\
box-sizing: border-box;\
}\
div.ace_isearch-result {\
@ -612,19 +608,6 @@ require("../incremental_search");
var iSearchCommandModule = require("../commands/incremental_search_commands");
var screenToTextBlockCoordinates = function(x, y) {
var canvasPos = this.scroller.getBoundingClientRect();
var col = Math.floor(
(x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth
);
var row = Math.floor(
(y + this.scrollTop - canvasPos.top) / this.lineHeight
);
return this.session.screenToDocumentPosition(row, col);
};
var HashHandler = require("./hash_handler").HashHandler;
exports.handler = new HashHandler();
@ -641,8 +624,6 @@ exports.handler.attach = function(editor) {
dom.importCssString('\
.emacs-mode .ace_cursor{\
border: 1px rgba(50,250,50,0.8) solid!important;\
-moz-box-sizing: border-box!important;\
-webkit-box-sizing: border-box!important;\
box-sizing: border-box!important;\
background-color: rgba(0,250,0,0.9);\
opacity: 0.5;\
@ -712,11 +693,11 @@ exports.handler.attach = function(editor) {
replacement : undefined);
}
return lastMark;
}
};
editor.on("click", $resetMarkMode);
editor.on("changeSession", $kbSessionChange);
editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates;
editor.renderer.$blockCursor = true;
editor.setStyle("emacs-mode");
editor.commands.addCommands(commands);
exports.handler.platform = editor.commands.platform;
@ -726,7 +707,7 @@ exports.handler.attach = function(editor) {
};
exports.handler.detach = function(editor) {
delete editor.renderer.screenToTextCoordinates;
editor.renderer.$blockCursor = false;
editor.session.$selectLongWords = $formerLongWords;
editor.session.$useEmacsStyleLineStart = $formerLineStart;
editor.removeEventListener("click", $resetMarkMode);
@ -809,7 +790,7 @@ exports.handler.getStatusText = function(editor, data) {
if (data.count)
str += data.count;
if (data.keyChain)
str += " " + data.keyChain
str += " " + data.keyChain;
return str;
};
@ -871,7 +852,7 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
data.lastCommand = null;
if (!command.readOnly && editor.emacsMark())
editor.setEmacsMark(null)
editor.setEmacsMark(null);
if (data.count) {
var count = data.count;
@ -1068,7 +1049,7 @@ exports.handler.addCommands({
var range = editor.getSelectionRange();
var line = editor.session.getLine(range.start.row);
range.end.column = line.length;
line = line.substr(range.start.column)
line = line.substr(range.start.column);
var foldLine = editor.session.getFoldLine(range.start.row);
if (foldLine && range.end.row != foldLine.end.row) {
@ -1128,6 +1109,7 @@ exports.handler.addCommands({
editor.$handlesEmacsOnCopy = false;
if (editor.inMultiSelectMode) editor.forEachSelection({exec: deselect});
else deselect();
editor.setEmacsMark(null);
editor.session.$emacsMarkRing = marks.concat(deselectedMarks.reverse());
}, 0);
},
@ -1178,4 +1160,11 @@ exports.killRing = {
}
};
});
}); (function() {
window.require(["ace/keyboard/emacs"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

File diff suppressed because it is too large Load diff

View file

@ -102,4 +102,11 @@ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgb
var dom = require("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass);
});
}); (function() {
window.require(["ace/theme/monokai"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View file

@ -134,7 +134,7 @@ window.define = function(id, deps, factory) {
exports: {},
factory: function() {
var module = this;
var returnExports = factory.apply(this, deps.map(function(dep) {
var returnExports = factory.apply(this, deps.slice(0, factory.length).map(function(dep) {
switch (dep) {
// Because "require", "exports" and "module" aren't actual
// dependencies, we must handle them seperately.
@ -217,7 +217,7 @@ window.onmessage = function(e) {
};
})(this);
define("ace/lib/oop",["require","exports","module"], function(require, exports, module) {
define("ace/lib/oop",[], function(require, exports, module) {
"use strict";
exports.inherits = function(ctor, superCtor) {
@ -245,7 +245,7 @@ exports.implement = function(proto, mixin) {
});
define("ace/range",["require","exports","module"], function(require, exports, module) {
define("ace/range",[], function(require, exports, module) {
"use strict";
var comparePoints = function(p1, p2) {
return p1.row - p2.row || p1.column - p2.column;
@ -484,7 +484,7 @@ Range.comparePoints = function(p1, p2) {
exports.Range = Range;
});
define("ace/apply_delta",["require","exports","module"], function(require, exports, module) {
define("ace/apply_delta",[], function(require, exports, module) {
"use strict";
function throwDeltaError(delta, errorText){
@ -549,7 +549,7 @@ exports.applyDelta = function(docLines, delta, doNotValidate) {
};
});
define("ace/lib/event_emitter",["require","exports","module"], function(require, exports, module) {
define("ace/lib/event_emitter",[], function(require, exports, module) {
"use strict";
var EventEmitter = {};
@ -599,10 +599,15 @@ EventEmitter._signal = function(eventName, e) {
EventEmitter.once = function(eventName, callback) {
var _self = this;
callback && this.addEventListener(eventName, function newCallback() {
this.addEventListener(eventName, function newCallback() {
_self.removeEventListener(eventName, newCallback);
callback.apply(null, arguments);
});
if (!callback) {
return new Promise(function(resolve) {
callback = resolve;
});
}
};
@ -630,7 +635,6 @@ EventEmitter.removeDefaultHandler = function(eventName, callback) {
var disabled = handlers._disabled_[eventName];
if (handlers[eventName] == callback) {
var old = handlers[eventName];
if (disabled)
this.setDefaultHandler(eventName, disabled.pop());
} else if (disabled) {
@ -675,7 +679,7 @@ exports.EventEmitter = EventEmitter;
});
define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"], function(require, exports, module) {
define("ace/anchor",[], function(require, exports, module) {
"use strict";
var oop = require("./lib/oop");
@ -800,7 +804,7 @@ var Anchor = exports.Anchor = function(doc, row, column) {
});
define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"], function(require, exports, module) {
define("ace/document",[], function(require, exports, module) {
"use strict";
var oop = require("./lib/oop");
@ -1091,28 +1095,23 @@ var Document = function(textOrLines) {
return;
}
if (isInsert && delta.lines.length > 20000)
if (isInsert && delta.lines.length > 20000) {
this.$splitAndapplyLargeDelta(delta, 20000);
applyDelta(this.$lines, delta, doNotValidate);
this._signal("change", delta);
}
else {
applyDelta(this.$lines, delta, doNotValidate);
this._signal("change", delta);
}
};
this.$splitAndapplyLargeDelta = function(delta, MAX) {
var lines = delta.lines;
var l = lines.length;
var l = lines.length - MAX + 1;
var row = delta.start.row;
var column = delta.start.column;
var from = 0, to = 0;
do {
from = to;
for (var from = 0, to = 0; from < l; from = to) {
to += MAX - 1;
var chunk = lines.slice(from, to);
if (to > l) {
delta.lines = chunk;
delta.start.row = row + from;
delta.start.column = column;
break;
}
chunk.push("");
this.applyDelta({
start: this.pos(row + from, column),
@ -1120,7 +1119,11 @@ var Document = function(textOrLines) {
action: delta.action,
lines: chunk
}, true);
} while(true);
}
delta.lines = lines.slice(from);
delta.start.row = row + from;
delta.start.column = column;
this.applyDelta(delta, true);
};
this.revertDelta = function(delta) {
this.applyDelta({
@ -1138,7 +1141,7 @@ var Document = function(textOrLines) {
if (index < 0)
return {row: i, column: index + lines[i].length + newlineLength};
}
return {row: l-1, column: lines[l-1].length};
return {row: l-1, column: index + lines[l-1].length + newlineLength};
};
this.positionToIndex = function(pos, startRow) {
var lines = this.$lines || this.getAllLines();
@ -1156,7 +1159,7 @@ var Document = function(textOrLines) {
exports.Document = Document;
});
define("ace/lib/lang",["require","exports","module"], function(require, exports, module) {
define("ace/lib/lang",[], function(require, exports, module) {
"use strict";
exports.last = function(a) {
@ -1258,7 +1261,7 @@ exports.escapeRegExp = function(str) {
};
exports.escapeHTML = function(str) {
return str.replace(/&/g, "&#38;").replace(/"/g, "&#34;").replace(/'/g, "&#39;").replace(/</g, "&#60;");
return ("" + str).replace(/&/g, "&#38;").replace(/"/g, "&#34;").replace(/'/g, "&#39;").replace(/</g, "&#60;");
};
exports.getMatchOffsets = function(string, regExp) {
@ -1344,7 +1347,7 @@ exports.delayedCall = function(fcn, defaultTimeout) {
};
});
define("ace/worker/mirror",["require","exports","module","ace/range","ace/document","ace/lib/lang"], function(require, exports, module) {
define("ace/worker/mirror",[], function(require, exports, module) {
"use strict";
var Range = require("../range").Range;
@ -1406,7 +1409,7 @@ var Mirror = exports.Mirror = function(sender) {
});
define("ace/mode/javascript/jshint",["require","exports","module"], function(require, exports, module) {
define("ace/mode/javascript/jshint",[], function(require, exports, module) {
module.exports = (function outer (modules, cache, entry) {
var previousRequire = typeof require == "function" && require;
function newRequire(name, jumped){
@ -11683,7 +11686,7 @@ exports.jasmine = {
});
define("ace/mode/javascript_worker",["require","exports","module","ace/lib/oop","ace/worker/mirror","ace/mode/javascript/jshint"], function(require, exports, module) {
define("ace/mode/javascript_worker",[], function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
@ -11830,7 +11833,7 @@ oop.inherits(JavaScriptWorker, Mirror);
});
define("ace/lib/es5-shim",["require","exports","module"], function(require, exports, module) {
define("ace/lib/es5-shim",[], function(require, exports, module) {
function Empty() {}

View file

@ -24,6 +24,7 @@
*/
Components.utils.import("resource://gre/modules/Services.jsm");
import FilePicker from 'zotero/filePicker';
var Zotero = Components.classes["@zotero.org/Zotero;1"]
// Currently uses only nsISupports
@ -70,7 +71,7 @@ var Scaffold = new function() {
'textbox-hidden-prefs':'hiddenPrefs'
};
this.onLoad = function (e) {
this.onLoad = async function (e) {
if(e.target !== document) return;
_document = document;
@ -145,7 +146,7 @@ var Scaffold = new function() {
}
if (!Scaffold_Translators.getDirectory()) {
if (!this.promptForTranslatorsDirectory()) {
if (!await this.promptForTranslatorsDirectory()) {
window.close();
return;
}
@ -155,7 +156,7 @@ var Scaffold = new function() {
_translatorProvider = Scaffold_Translators.getProvider();
};
this.promptForTranslatorsDirectory = function () {
this.promptForTranslatorsDirectory = async function () {
var ps = Services.prompt;
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING
@ -171,7 +172,7 @@ var Scaffold = new function() {
);
// Revert to home directory
if (index == 0) {
let dir = this.setTranslatorsDirectory();
let dir = await this.setTranslatorsDirectory();
if (dir) {
return true;
}
@ -182,23 +183,22 @@ var Scaffold = new function() {
return false;
};
this.setTranslatorsDirectory = function () {
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
this.setTranslatorsDirectory = async function () {
var fp = new FilePicker();
var oldPath = Zotero.Prefs.get('scaffold.translatorsDir');
if (oldPath) {
fp.displayDirectory = Zotero.File.pathToFile(oldPath);
fp.displayDirectory = oldPath;
}
fp.init(
window,
"Select Translators Directory",
nsIFilePicker.modeGetFolder
fp.modeGetFolder
);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() != nsIFilePicker.returnOK) {
fp.appendFilters(fp.filterAll);
if (await fp.show() != fp.returnOK) {
return false;
}
var path = OS.Path.normalize(fp.file.path);
var path = OS.Path.normalize(fp.file);
if (oldPath == path) {
return false;
}

View file

@ -190,7 +190,7 @@
</menupopup>
</toolbarbutton>
</hbox>
<iframe src="data:text/html,%3C!DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.1//EN%22%20%22http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd%22%3E%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/ace.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/mode-javascript.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/aceWrapper.js%22%3E%3C/script%3E%3C/head%3E%3Cbody%20id=%22body%22%3E%3C/body%3E%3C/html%3E" id="editor-code" flex="1"/>
<iframe src="chrome://scaffold/content/ace/ace.html" id="editor-code" flex="1"/>
<hbox id="editor-external-box" align="center">
<checkbox id="checkbox-editor-external"/>
<label class="label-metadata" value="&scaffold.editor.external.label;" control="checkbox-editor-external"/>
@ -198,10 +198,10 @@
</vbox>
</tabpanel>
<tabpanel flex="1" id="tabpanel-import">
<iframe src="data:text/html,%3C!DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.1//EN%22%20%22http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd%22%3E%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/ace.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/mode-javascript.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/aceWrapper.js%22%3E%3C/script%3E%3C/head%3E%3Cbody%20id=%22body%22%3E%3C/body%3E%3C/html%3E" id="editor-import" flex="1"/>
<iframe src="chrome://scaffold/content/ace/ace.html" id="editor-import" flex="1"/>
</tabpanel>
<tabpanel flex="1" id="tabpanel-tests">
<iframe src="data:text/html,%3C!DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.1//EN%22%20%22http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd%22%3E%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/ace.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/ace/mode-javascript.js%22%3E%3C/script%3E%3Cscript%20type=%22text/javascript%22%20src=%22chrome://scaffold/content/aceWrapper.js%22%3E%3C/script%3E%3C/head%3E%3Cbody%20id=%22body%22%3E%3C/body%3E%3C/html%3E" id="editor-tests" flex="1"/>
<iframe src="chrome://scaffold/content/ace/ace.html" id="editor-tests" flex="1"/>
</tabpanel>
<tabpanel flex="1" id="tabpanel-testing">
<vbox flex="1"><hbox><description>&scaffold.testing.description;</description></hbox>

View file

@ -22,52 +22,46 @@ var Scaffold_Translators = {
var dir = this.getDirectory();
var numLoaded = 0;
var deletedTranslators = new Set(this._translatorFiles.keys());
await Zotero.File.iterateDirectory(dir, function* (iterator) {
while (true) {
let entries = yield iterator.nextBatch(50);
if (!entries.length) break;
for (let entry of entries) {
if (entry.isDir || entry.name.startsWith('.') || !entry.name.endsWith('.js')) {
continue;
}
deletedTranslators.delete(entry.name);
try {
let fmtime;
if ('winLastWriteDate' in entry) {
fmtime = entry.winLastWriteDate.getTime();
}
else {
fmtime = (yield OS.File.stat(entry.path)).lastModificationDate.getTime();
}
let translatorID = this._translatorFiles.get(entry.name);
let loadFile = true;
// If translator is already loaded, see if mtime has changed
if (translatorID) {
let mtime = this._translators.get(translatorID).mtime;
if (mtime == fmtime) {
loadFile = false;
}
}
if (loadFile) {
let translator = yield Zotero.Translators.loadFromFile(entry.path);
this._translators.set(
translator.translatorID,
{
translator,
filename: entry.name,
mtime: fmtime,
}
);
this._translatorFiles.set(entry.name, translator.translatorID);
numLoaded++;
}
}
catch (e) {
Zotero.logError(e);
await Zotero.File.iterateDirectory(dir, async function (entry) {
if (entry.isDir || entry.name.startsWith('.') || !entry.name.endsWith('.js')) {
return;
}
deletedTranslators.delete(entry.name);
try {
let fmtime;
if ('winLastWriteDate' in entry) {
fmtime = entry.winLastWriteDate.getTime();
}
else {
fmtime = (await OS.File.stat(entry.path)).lastModificationDate.getTime();
}
let translatorID = this._translatorFiles.get(entry.name);
let loadFile = true;
// If translator is already loaded, see if mtime has changed
if (translatorID) {
let mtime = this._translators.get(translatorID).mtime;
if (mtime == fmtime) {
loadFile = false;
}
}
if (loadFile) {
let translator = await Zotero.Translators.loadFromFile(entry.path);
this._translators.set(
translator.translatorID,
{
translator,
filename: entry.name,
mtime: fmtime,
}
);
this._translatorFiles.set(entry.name, translator.translatorID);
numLoaded++;
}
}
catch (e) {
Zotero.logError(e);
}
}.bind(this));

View file

@ -1,12 +1,3 @@
#zotero-splitter
{
border-top: none;
border-bottom: 1px solid #A3A3A3;
min-height: 1px !important;
max-height: 1px !important;
background-image: none;
}
#zotero-items-toolbar[state=collapsed]
{
margin-left: -8px !important;
@ -107,11 +98,6 @@
padding: 2px 0px 2px 8px !important;
}
#zotero-close-button {
margin: 0px !important;
padding: 0px 0px 0px 10px !important;
}
#zotero-tb-sync {
margin: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

View file

@ -0,0 +1,138 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
prefwindow,
prefwindow:root /* override :root from above */ {
-moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#prefwindow");
-moz-box-orient: vertical;
}
prefpane {
-moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#prefpane");
-moz-box-orient: vertical;
}
prefwindow > .paneDeckContainer {
overflow: hidden;
}
prefpane > .content-box {
overflow: hidden;
}
prefwindow[type="child"] > .paneDeckContainer {
overflow: -moz-hidden-unscrollable;
}
prefwindow[type="child"] > prefpane > .content-box {
-moz-box-flex: 1;
overflow: -moz-hidden-unscrollable;
}
preferences {
-moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#preferences");
visibility: collapse;
}
preference {
-moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#preference");
visibility: collapse;
}
radio[pane] {
-moz-binding: url("chrome://zotero/content/bindings/preferences-mac.xml#panebutton") !important;
-moz-box-orient: vertical;
-moz-box-align: center;
}
prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar {
display: none;
}
prefwindow {
padding: 0;
font: -moz-dialog !important;
-moz-appearance: dialog;
color: -moz-DialogText;
}
prefpane {
padding: 12px 12px 0 12px;
}
prefwindow[type="child"] > prefpane {
padding: 0;
}
.prefWindow-dlgbuttons {
margin: 0 12px 12px;
padding-top: 0 !important;
}
.paneSelector {
font: message-box;
padding: 1px 4px;
-moz-appearance: toolbar;
margin: 0;
}
radio[pane] {
border: solid transparent;
border-width: 0 2px;
padding: 5px 4px 3px;
margin: 0;
-moz-appearance: none;
text-shadow: rgba(255, 255, 255, 0.4) 0 1px;
}
radio[pane]:active:hover {
text-shadow: none;
}
radio[pane]:active:hover > .paneButtonIcon {
filter: brightness(0.55);
}
radio[pane][selected="true"] {
-moz-border-image: url("chrome://zotero-platform/content/panebutton-active.png") 0 2 fill repeat stretch;
}
radio[pane][selected="true"]:-moz-window-inactive {
-moz-border-image: url("chrome://zotero-platform/content/panebutton-inactive.png") 0 2 fill repeat stretch;
}
.paneButtonLabel {
margin: 0 !important;
}
groupbox {
padding: 5px 1px 1px;
padding-inline-start: 0;
margin: 6px;
}
.groupbox-title {
border-bottom: #b5b5b5 1px solid;
}
.groupbox-body {
-moz-appearance: groupbox;
padding: 8px 8px 3px;
margin: 0;
}
caption {
padding-inline-start: 4px;
padding-bottom: 1px;
font: caption;
}
/* !important is needed to override label in global.css */
.caption-text {
margin-top: 0 !important;
margin-bottom: 0 !important;
margin-inline-start: 1px !important;
}

View file

@ -124,7 +124,7 @@
</menupopup>
</rule>
</template>
<menupopup id="menu_windowPopup">
<menupopup id="windowPopup">
<menuitem command="minimizeWindow" label="&minimizeWindow.label;" key="key_minimizeWindow"/>
<menuitem command="zoomWindow" label="&zoomWindow.label;"/>
<!-- decomment when "BringAllToFront" is implemented

View file

@ -61,9 +61,6 @@ tab {
border-top: 1px solid hsla(0, 0%, 0%, 0.2);
border-right: 1px solid hsla(0, 0%, 0%, 0.2);
border-left: 1px solid hsla(0, 0%, 0%, 0.2);
-moz-border-top-colors: none;
-moz-border-right-colors: none;
-moz-border-left-colors: none;
}
/* Fixes misc Ubuntu 16.10 rendering issue fixes. */
#zotero-prefs menulist dropmarker{

View file

@ -0,0 +1,107 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
prefwindow,
prefwindow:root /* override :root from above */ {
-moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#prefwindow");
-moz-box-orient: vertical;
}
prefpane {
-moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#prefpane");
-moz-box-orient: vertical;
}
prefwindow > .paneDeckContainer {
overflow: hidden;
}
prefpane > .content-box {
overflow: hidden;
}
prefwindow[type="child"] > .paneDeckContainer {
overflow: -moz-hidden-unscrollable;
}
prefwindow[type="child"] > prefpane > .content-box {
-moz-box-flex: 1;
overflow: -moz-hidden-unscrollable;
}
preferences {
-moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#preferences");
visibility: collapse;
}
preference {
-moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#preference");
visibility: collapse;
}
radio[pane] {
-moz-binding: url("chrome://zotero/content/bindings/preferences-unix.xml#panebutton") !important;
-moz-box-orient: vertical;
-moz-box-align: center;
}
prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar {
display: none;
}
prefwindow {
padding: 0px;
-moz-appearance: window;
background-color: -moz-Dialog;
color: -moz-DialogText;
font: message-box;
}
prefpane {
padding: 8px;
}
prefwindow[type="child"] {
padding: 8px;
}
prefwindow[type="child"] > prefpane {
padding: 0px;
}
.prefWindow-dlgbuttons {
padding-bottom: 8px;
padding-inline-start: 8px;
padding-inline-end: 8px;
}
prefwindow[type="child"] .prefWindow-dlgbuttons {
padding: 0px;
}
radio[pane] {
-moz-appearance: none;
min-width: 4.5em;
margin: 0;
padding: 3px;
color: -moz-FieldText;
}
.paneSelector {
-moz-appearance: listbox;
margin: 8px 8px 0 8px;
padding: 0;
}
.paneButtonIcon {
width: 32px;
height: 32px;
}
radio[pane][selected="true"] {
background-color: Highlight;
color: HighlightText;
}

View file

@ -105,10 +105,6 @@
border-color: #818790;
margin: 0;
padding: 0;
-moz-border-top-colors: none;
-moz-border-bottom-colors: none;
-moz-border-right-colors: none;
-moz-border-left-colors: none;
}
treechildren::-moz-tree-twisty {

View file

@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
prefwindow,
prefwindow:root /* override :root from above */ {
-moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#prefwindow");
-moz-box-orient: vertical;
}
prefpane {
-moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#prefpane");
-moz-box-orient: vertical;
}
prefwindow > .paneDeckContainer {
overflow: hidden;
}
prefpane > .content-box {
overflow: hidden;
}
prefwindow[type="child"] > .paneDeckContainer {
overflow: -moz-hidden-unscrollable;
}
prefwindow[type="child"] > prefpane > .content-box {
-moz-box-flex: 1;
overflow: -moz-hidden-unscrollable;
}
preferences {
-moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#preferences");
visibility: collapse;
}
preference {
-moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#preference");
visibility: collapse;
}
radio[pane] {
-moz-binding: url("chrome://zotero/content/bindings/preferences-win.xml#panebutton") !important;
-moz-box-orient: vertical;
-moz-box-align: center;
}
prefwindow[chromehidden~="toolbar"] .chromeclass-toolbar {
display: none;
}
prefwindow {
padding: 0px;
-moz-appearance: window;
background-color: -moz-Dialog;
color: -moz-DialogText;
font: message-box;
}
prefpane {
padding-top: 8px;
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
prefwindow[type="child"] {
padding-top: 8px;
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
prefwindow[type="child"] > prefpane {
padding: 0px;
}
.prefWindow-dlgbuttons {
padding-bottom: 10px;
padding-inline-start: 8px;
padding-inline-end: 10px;
}
prefwindow[type="child"] .prefWindow-dlgbuttons {
padding: 0px;
}
radio[pane] {
-moz-appearance: none;
margin: 0px 1px 0px 1px;
padding: 1px 3px 1px 3px;
min-width: 4.5em;
}
.paneSelector {
border-bottom: 2px groove ThreeDFace;
margin: 0px;
padding-inline-start: 10px;
background-color: -moz-Field;
color: -moz-FieldText;
}
.paneButtonIcon {
width: 32px;
height: 32px;
}
radio[pane]:hover {
background-color: #E0E8F6;
color: black;
-moz-appearance: none;
}
radio[pane][selected="true"] {
background-color: #C1D2EE;
color: black;
-moz-appearance: none;
}

View file

@ -172,10 +172,6 @@ var ZoteroAdvancedSearch = new function() {
return;
}
if (lastWin.ZoteroOverlay) {
lastWin.ZoteroOverlay.toggleDisplay(true);
}
lastWin.ZoteroPane.selectItem(item.getID(), false, true);
lastWin.focus();
}

View file

@ -387,9 +387,7 @@
if (fieldIsClickable
&& !Zotero.Items.isPrimaryField(fieldName)
&& (Zotero.ItemFields.isFieldOfBase(Zotero.ItemFields.getID(fieldName), 'date')
// TEMP - filingDate
|| fieldName == 'filingDate')
&& Zotero.ItemFields.isDate(fieldName)
// TEMP - NSF
&& fieldName != 'dateSent') {
this.addDateRow(fieldNames[i], this.item.getField(fieldName, true), tabindex);
@ -414,7 +412,7 @@
if (fieldName) {
label.setAttribute("value", prefix +
Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, fieldName));
Zotero.ItemFields.getLocalizedString(fieldName));
}
// TEMP - NSF (homepage)
@ -897,7 +895,7 @@
<body>
<![CDATA[
var label = document.createElement("label");
label.setAttribute("value", Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, field));
label.setAttribute("value", Zotero.ItemFields.getLocalizedString(field));
label.setAttribute("fieldname", field);
label.setAttribute("onclick", "this.nextSibling.firstChild.blur()");
@ -1158,7 +1156,7 @@
var fieldNames = "";
for (var i=0; i<fieldsToDelete.length; i++) {
fieldNames += "\n - " +
Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, fieldsToDelete[i]);
Zotero.ItemFields.getLocalizedString(fieldsToDelete[i]);
}
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
@ -1215,7 +1213,7 @@
);
valueElement.parentNode.replaceChild(newValueElement, valueElement);
var text = Zotero.ItemFields.getLocalizedString(this.item.itemTypeID, 'abstractNote');
var text = Zotero.ItemFields.getLocalizedString('abstractNote');
// Add '(...)' before "Abstract" for collapsed abstracts
if (valueText && cur) {
text = '(\u2026) ' + text;

View file

@ -511,10 +511,6 @@
var lastWin = window.open();
}
if (lastWin.ZoteroOverlay && !lastWin.ZoteroPane.isShowing()) {
lastWin.ZoteroOverlay.toggleDisplay(true);
}
var zp = lastWin.ZoteroPane;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -644,14 +644,12 @@
// for read-only mode
var htmlFile = this.mode + (this.getAttribute('readonly') != 'true' ? "" : "view");
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI("resource://zotero/tinymce/" + htmlFile + ".html", null, null);
// Pass directionality (LTR/RTL) and locale in URL
uri.spec += "?locale=" + encodeURIComponent(Zotero.locale)
var url = `resource://zotero/tinymce/${htmlFile}.html`
// Pass directionality (LTR/RTL) and locale in URL
+ "?locale=" + encodeURIComponent(Zotero.locale)
+ "&dir=" + Zotero.dir;
Components.utils.import("resource://gre/modules/Services.jsm");
var uri = Services.io.newURI(url, null, null);
Zotero.debug("Loading " + uri.spec);

View file

@ -139,7 +139,7 @@
<body><![CDATA[
return Zotero.spawn(function* () {
if (type == 'setting') {
if (ids.some(function (val) val.split("/")[1] == 'tagColors') && this.item) {
if (ids.some(val => val.split("/")[1] == 'tagColors') && this.item) {
this.reload();
return;
}
@ -227,7 +227,7 @@
// Sort tags alphabetically
var collation = Zotero.getLocaleCollation();
tags.sort(function (a, b) collation.compareString(1, a.tag, b.tag));
tags.sort((a, b) => collation.compareString(1, a.tag, b.tag));
for (let i=0; i<tags.length; i++) {
this.addDynamicRow(tags[i], i+1);
@ -682,7 +682,7 @@
return;
}
var tags = value.split(/\r\n?|\n/).map(function (val) val.trim());
var tags = value.split(/\r\n?|\n/).map(val => val.trim());
// Modifying existing tag with a single new one
if (!isNew && tags.length < 2) {

View file

@ -333,7 +333,7 @@
if (!this.id(condition.name + '-tooltip')) {
var fieldName = null;
try {
fieldName = Zotero.ItemFields.getLocalizedString(null, condition.name);
fieldName = Zotero.ItemFields.getLocalizedString(condition.name);
}
catch (e) {}

View file

@ -9,6 +9,11 @@
<style>
body {
margin: 0;
background: white;
}
a {
text-decoration: underline;
}
header {

View file

@ -24,6 +24,7 @@
*/
Components.utils.import("resource://gre/modules/osfile.jsm")
import FilePicker from 'zotero/filePicker';
/****Zotero_File_Exporter****
**
@ -44,9 +45,9 @@ var Zotero_File_Exporter = function() {
*
* @return {Promise}
**/
Zotero_File_Exporter.prototype.save = Zotero.Promise.coroutine(function* () {
Zotero_File_Exporter.prototype.save = async function () {
var translation = new Zotero.Translate.Export();
var translators = yield translation.getTranslators();
var translators = await translation.getTranslators();
// present options dialog
var io = {translators:translators}
@ -56,17 +57,15 @@ Zotero_File_Exporter.prototype.save = Zotero.Promise.coroutine(function* () {
return false;
}
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString("fileInterface.export"), nsIFilePicker.modeSave);
var fp = new FilePicker();
fp.init(window, Zotero.getString("fileInterface.export"), fp.modeSave);
// set file name and extension
if(io.displayOptions.exportFileData) {
// if the result will be a folder, don't append any extension or use
// filters
fp.defaultString = this.name;
fp.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
fp.appendFilters(fp.filterAll);
} else {
// if the result will be a file, append an extension and use filters
fp.defaultString = this.name+(io.selectedTranslator.target ? "."+io.selectedTranslator.target : "");
@ -74,8 +73,8 @@ Zotero_File_Exporter.prototype.save = Zotero.Promise.coroutine(function* () {
fp.appendFilter(io.selectedTranslator.label, "*."+(io.selectedTranslator.target ? io.selectedTranslator.target : "*"));
}
var rv = fp.show();
if (rv != nsIFilePicker.returnOK && rv != nsIFilePicker.returnReplace) {
var rv = await fp.show();
if (rv != fp.returnOK && rv != fp.returnReplace) {
return;
}
@ -89,7 +88,7 @@ Zotero_File_Exporter.prototype.save = Zotero.Promise.coroutine(function* () {
translation.setLibraryID(this.libraryID);
}
translation.setLocation(fp.file);
translation.setLocation(Zotero.File.pathToFile(fp.file));
translation.setTranslator(io.selectedTranslator);
translation.setDisplayOptions(io.displayOptions);
translation.setHandler("itemDone", function () {
@ -100,7 +99,7 @@ Zotero_File_Exporter.prototype.save = Zotero.Promise.coroutine(function* () {
Zotero.getString("fileInterface.itemsExported")
);
translation.translate()
});
};
/*
* Closes the items exported indicator
@ -238,19 +237,16 @@ var Zotero_File_Interface = new function() {
Zotero.debug(`${dir} does not exist`);
return dbs;
}
await Zotero.File.iterateDirectory(dir, function* (iterator) {
while (true) {
let entry = yield iterator.next();
if (entry.isDir) continue;
// online.sqlite, counterintuitively, is the default database before you sign in
if (entry.name == 'online.sqlite' || entry.name.endsWith('@www.mendeley.com.sqlite')) {
dbs.push({
name: entry.name,
path: entry.path,
lastModified: null,
size: null
});
}
await Zotero.File.iterateDirectory(dir, function (entry) {
if (entry.isDir) return;
// online.sqlite, counterintuitively, is the default database before you sign in
if (entry.name == 'online.sqlite' || entry.name.endsWith('@www.mendeley.com.sqlite')) {
dbs.push({
name: entry.name,
path: entry.path,
lastModified: null,
size: null
});
}
});
for (let i = 0; i < dbs.length; i++) {
@ -596,7 +592,7 @@ var Zotero_File_Interface = new function() {
/**
* Creates a bibliography from a collection or saved search
*/
this.bibliographyFromCollection = function () {
this.bibliographyFromCollection = async function () {
var items = ZoteroPane.getSortedItems();
// Find collection name
@ -612,17 +608,17 @@ var Zotero_File_Interface = new function() {
}
}
_doBibliographyOptions(name, items);
await _doBibliographyOptions(name, items);
}
/*
* Creates a bibliography from a items
*/
function bibliographyFromItems() {
async function bibliographyFromItems() {
var items = ZoteroPane_Local.getSelectedItems();
if(!items || !items.length) throw("no items currently selected");
_doBibliographyOptions(Zotero.getString("fileInterface.untitledBibliography"), items);
await _doBibliographyOptions(Zotero.getString("fileInterface.untitledBibliography"), items);
}
@ -691,7 +687,7 @@ var Zotero_File_Interface = new function() {
/*
* Shows bibliography options and creates a bibliography
*/
function _doBibliographyOptions(name, items) {
async function _doBibliographyOptions(name, items) {
// make sure at least one item is not a standalone note or attachment
var haveRegularItem = false;
for (let item of items) {
@ -782,7 +778,7 @@ var Zotero_File_Interface = new function() {
browser.loadURIWithFlags("data:text/html;charset=utf-8,"+encodeURI(bibliography),
Components.interfaces.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, "utf-8", null);
} else if(io.method == "save-as-html") {
var fStream = _saveBibliography(name, "HTML");
let fStream = await _saveBibliography(name, "HTML");
if(fStream !== false) {
var html = "";
@ -808,7 +804,7 @@ var Zotero_File_Interface = new function() {
fStream.close();
}
} else if(io.method == "save-as-rtf") {
var fStream = _saveBibliography(name, "RTF");
let fStream = await _saveBibliography(name, "RTF");
if(fStream !== false) {
fStream.write(bibliography, bibliography.length);
fStream.close();
@ -817,29 +813,31 @@ var Zotero_File_Interface = new function() {
}
function _saveBibliography(name, format) {
async function _saveBibliography(name, format) {
// saveable bibliography, using a file stream
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, "Save Bibliography", nsIFilePicker.modeSave);
var fp = new FilePicker();
fp.init(window, "Save Bibliography", fp.modeSave);
if(format == "RTF") {
var extension = "rtf";
fp.appendFilter("RTF", "*.rtf");
} else {
var extension = "html";
fp.appendFilters(nsIFilePicker.filterHTML);
fp.appendFilters(fp.filterHTML);
}
fp.defaultString = name+"."+extension;
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var rv = await fp.show();
if (rv == fp.returnOK || rv == fp.returnReplace) {
// open file
var fStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
fStream.init(fp.file, 0x02 | 0x08 | 0x20, 0o664, 0); // write, create, truncate
fStream.init(
Zotero.File.pathToFile(fp.file),
0x02 | 0x08 | 0x20, 0o664, // write, create, truncate
0
);
return fStream;
} else {
return false;

View file

@ -1,3 +1,5 @@
import FilePicker from 'zotero/filePicker';
var Zotero_Import_Wizard = {
_wizard: null,
_dbs: null,
@ -95,12 +97,10 @@ var Zotero_Import_Wizard = {
chooseFile: async function (translation) {
var translation = new Zotero.Translate.Import();
var translators = await translation.getTranslators();
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString("fileInterface.import"), nsIFilePicker.modeOpen);
var fp = new FilePicker();
fp.init(window, Zotero.getString("fileInterface.import"), fp.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll);
fp.appendFilters(fp.filterAll);
var collation = Zotero.getLocaleCollation();
@ -117,14 +117,14 @@ var Zotero_Import_Wizard = {
fp.appendFilter(filter.label, "*." + filter.target);
}
var rv = fp.show();
if (rv !== nsIFilePicker.returnOK && rv !== nsIFilePicker.returnReplace) {
var rv = await fp.show();
if (rv !== fp.returnOK && rv !== fp.returnReplace) {
return false;
}
Zotero.debug(`File is ${fp.file.path}`);
Zotero.debug(`File is ${fp.file}`);
this._file = fp.file.path;
this._file = fp.file;
this._wizard.canAdvance = true;
this._wizard.goTo('page-options');
},
@ -151,16 +151,14 @@ var Zotero_Import_Wizard = {
*/
chooseMendeleyDB: async function () {
document.getElementById('file-list').selectedIndex = -1;
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString('fileInterface.import'), nsIFilePicker.modeOpen);
var fp = new FilePicker();
fp.init(window, Zotero.getString('fileInterface.import'), fp.modeOpen);
fp.appendFilter("Mendeley Database", "*.sqlite"); // TODO: Localize
var rv = fp.show();
if (rv != nsIFilePicker.returnOK) {
var rv = await fp.show();
if (rv != fp.returnOK) {
return false;
}
this._file = fp.file.path;
this._file = fp.file;
this._wizard.canAdvance = true;
this._wizard.advance();
},

View file

@ -1208,19 +1208,16 @@ Zotero_Import_Mendeley.prototype.deleteNonPrimaryFiles = async function () {
let filename = row.path.substr(8);
Zotero.debug(`Checking for extra files in ${dir}`);
await Zotero.File.iterateDirectory(dir, function* (iterator) {
while (true) {
let entry = yield iterator.next();
if (entry.name.startsWith('.zotero') || entry.name == filename) {
continue;
}
Zotero.debug(`Deleting ${entry.path}`);
try {
yield OS.File.remove(entry.path);
}
catch (e) {
Zotero.logError(e);
}
await Zotero.File.iterateDirectory(dir, async function (entry) {
if (entry.name.startsWith('.zotero') || entry.name == filename) {
return;
}
Zotero.debug(`Deleting ${entry.path}`);
try {
await OS.File.remove(entry.path);
}
catch (e) {
Zotero.logError(e);
}
});
}

View file

@ -80,6 +80,12 @@ var Zotero_QuickFormat = new function () {
}
}
// With fx60 and drawintitlebar=true Firefox calculates the minHeight
// as titlebar+maincontent, so we have hack around that here.
if (Zotero.isMac && Zotero.platformMajorVersion >= 60) {
qfb.style.marginBottom = "-22px";
}
// add labels to popup
var locators = Zotero.Cite.labels;
var menu = document.getElementById("locator-label");
@ -807,11 +813,21 @@ var Zotero_QuickFormat = new function () {
qfs.setAttribute("multiline", true);
qfs.style.height = ((Zotero.isMac ? 6 : 4)+qfe.scrollHeight)+"px";
window.sizeToContent();
// the above line causes drawing artifacts to appear due to a bug with drawintitle property
// in fx60. this fixes the artifacting
if (Zotero.isMac && Zotero.platformMajorVersion >= 60) {
document.children[0].setAttribute('drawintitlebar', 'false');
document.children[0].setAttribute('drawintitlebar', 'true');
}
} else {
delete qfs.style.height;
qfe.removeAttribute("multiline");
qfs.removeAttribute("multiline");
window.sizeToContent();
if (Zotero.isMac && Zotero.platformMajorVersion >= 60) {
document.children[0].setAttribute('drawintitlebar', 'false');
document.children[0].setAttribute('drawintitlebar', 'true');
}
}
var panelShowing = referencePanel.state === "open" || referencePanel.state === "showing";

View file

@ -41,8 +41,8 @@
onunload="Zotero_QuickFormat.onUnload()">
<script src="../include.js"/>
<script src="windowDraggingUtils.js" type="text/javascript;version=1.8"/>
<script src="quickFormat.js" type="text/javascript;version=1.8"/>
<script src="windowDraggingUtils.js" type="text/javascript"/>
<script src="quickFormat.js" type="text/javascript"/>
<box orient="horizontal" id="quick-format-entry">
<deck id="quick-format-deck" selectedIndex="0" flex="1">

View file

@ -30,7 +30,7 @@
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="include.js"/>
<script src="itemPane.js" type="application/javascript;version=1.8"/>
<script src="itemPane.js" type="application/javascript"/>
<vbox id="zotero-item-pane" zotero-persist="width height">
<!-- My Publications -->

View file

@ -26,6 +26,7 @@
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/preferences.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>

View file

@ -0,0 +1,201 @@
import { Cc, Ci, Cu } from 'chrome';
Cu.import("resource://gre/modules/osfile.jsm");
/**
* Interface to the system filepicker.
*
* Based on Mozilla's nsIFilePicker, with minor modifications (e.g., strings paths instead nsIFile,
* promise-returning show()).
*
* @class
*/
class FilePicker {
constructor() {
this._fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
}
/**
* @param {Window} parentWindow
* @param {String} title
* @param {Integer} mode - One of the mode constants, indicating the type of picker to create
*/
init(parentWindow, title, mode) {
this._fp.init(parentWindow, title, mode);
};
/**
* Appends a custom file extension filter to the dialog. The filter appended first will be used when
* the dialog is initially opened. The user may then select another from the list.
*
* @param {String} title - The title of the filter
* @param {String} filter - The filter string. Multiple extensions may be included, separated by a
* semicolon and a space.
*/
appendFilter(title, filter) {
this._fp.appendFilter(title, filter);
};
/**
* Appends a list of file extension filters, from the predefined list, to the dialog
*
* @param {Integer} filterMask - A combination of the filters you wish to use. You may OR multiple
* filters together; for example <code>filterAll | filterHTML</code>.
*/
appendFilters(filterMask) {
this._fp.appendFilters(filterMask);
};
/**
* Show the dialog
*
* @return {Promise<Integer>} One of the return constants
*/
async show() {
return new Zotero.Promise(function (resolve) {
this._fp.open(returnConstant => resolve(returnConstant));
}.bind(this));
};
};
/** @const {Integer} FilePicker#modeOpen - Load a file */
/** @const {Integer} FilePicker#modeSave - Save a file */
/** @const {Integer} FilePicker#modeGetFolder - Select a folder/directory */
/** @const {Integer} FilePicker#modeOpenMultiple - Load multiple files */
FilePicker.prototype.modeOpen = 0;
FilePicker.prototype.modeSave = 1;
FilePicker.prototype.modeGetFolder = 2;
FilePicker.prototype.modeOpenMultiple = 3;
/** @const {Integer} FilePicker#returnOK - The file picker dialog was closed by the user hitting 'OK' */
/** @const {Integer} FilePicker#returnCancel - The file picker dialog was closed by the user hitting 'Cancel' */
/** @const {Integer} FilePicker#returnReplace - The user chose an existing file and acknowledged that they want to overwrite the file */
FilePicker.prototype.returnOK = 0;
FilePicker.prototype.returnCancel = 1;
FilePicker.prototype.returnReplace = 2;
/** @const {Integer} FilePicker#filterAll - All files */
/** @const {Integer} FilePicker#filterHTML - HTML files */
/** @const {Integer} FilePicker#filterText - Text files */
/** @const {Integer} FilePicker#filterImages - Image files */
/** @const {Integer} FilePicker#filterXML - XML files */
/** @const {Integer} FilePicker#filterApps - Platform-specific application filter */
/** @const {Integer} FilePicker#filterAllowURLs - Allow URLs */
/** @const {Integer} FilePicker#filterAudio - Audio files */
/** @const {Integer} FilePicker#filterVideo - Video files */
FilePicker.prototype.filterAll = 0x001;
FilePicker.prototype.filterHTML = 0x002;
FilePicker.prototype.filterText = 0x004;
FilePicker.prototype.filterImages = 0x008;
FilePicker.prototype.filterXML = 0x010;
FilePicker.prototype.filterApps = 0x040;
FilePicker.prototype.filterAllowURLs = 0x80;
FilePicker.prototype.filterAudio = 0x100;
FilePicker.prototype.filterVideo = 0x200;
['addToRecentDocs', 'defaultExtension', 'defaultString', 'displayDirectory', 'filterIndex'].forEach((prop) => {
/**
* @name FilePicker#addToRecentDocs
* @type Boolean
* @default false
* @desc If true, the file is added to the operating system's "recent documents" list (if the
* operating system has one; nothing happens if there is no such concept on the user's platform).
*/
/**
* @name FilePicker#defaultExtension
* @type String
* @desc The extension for the type of files you want to work with. On some platforms, this is
* automatically appended to filenames the user enters, if required. Specify it without a
* leading dot, for example "jpg".
*/
/**
* @name FilePicker#defaultString
* @type String
* @desc The filename, including extension, that should be suggested to the user as a default.
* This should be set before calling show().
*/
/**
* @name FilePicker#displayDirectory
* @type String
* @desc The filename, including extension, that should be suggested to the user as a default.
* This should be set before calling show().
*/
/**
* @name FilePicker#filterIndex
* @type Integer
* @desc The (0-based) index of the filter which is currently selected in the file picker dialog.
* Set this to choose a particular filter to be selected by default.
*/
Object.defineProperty(FilePicker.prototype, prop, {
// TODO: Others
get: function () {
var val = this._fp[prop];
if (prop == 'displayDirectory') {
// Convert from nsIFile
val = val.path;
}
return val;
},
set: function (val) {
if (prop == 'displayDirectory') {
// Convert to nsIFile
val = Zotero.File.pathToFile(val);
}
this._fp[prop] = val;
},
enumerable: true
});
});
// Read-only properties
['file', 'files', 'fileURL'].forEach((prop) => {
/**
* @name FilePicker#file
* @type String
* @readonly
* @desc The selected file or directory.
*/
/**
* @name FilePicker#files
* @type String[]
* @readonly
* @desc An array of the selected files. Only works with `modeOpenMultiple` mode.
*/
/**
* @name FilePicker#fileURL
* @type String
* @readonly
* @desc The URI of the selected file or directory.
*/
Object.defineProperty(FilePicker.prototype, prop, {
get: function () {
var val = this._fp[prop];
switch (prop) {
case 'file':
// Convert from nsIFile
val = OS.Path.normalize(val.path);
break;
case 'files':
var files = [];
while (val.hasMoreElements()) {
let file = val.getNext();
file.QueryInterface(Ci.nsIFile);
files.push(file.path);
}
val = files;
break;
case 'fileURL':
val = val.spec;
break;
}
return val;
},
enumerable: true
});
});
Object.freeze(FilePicker.prototype);
export default FilePicker;

View file

@ -23,166 +23,42 @@
***** END LICENSE BLOCK *****
*/
/*
* This object contains the various functions for the interface
*/
var ZoteroOverlay = new function()
{
const DEFAULT_ZPANE_HEIGHT = 300;
var toolbarCollapseState;
var zoteroPane, zoteroSplitter;
this.isTab = false;
this.onLoad = function () {
Zotero.spawn(function* () {
try {
//
// Code that runs in both full and connector mode
//
zoteroPane = document.getElementById('zotero-pane-stack');
zoteroSplitter = document.getElementById('zotero-splitter');
if (!Zotero) {
throw new Error("No Zotero object");
}
if (Zotero.skipLoading) {
throw new Error("Skipping loading");
}
ZoteroPane_Overlay = ZoteroPane;
// TODO: Add only when progress window is open
document.getElementById('appcontent').addEventListener('mousemove', Zotero.ProgressWindowSet.updateTimers, false);
// Perform additional initialization for full mode
if (!Zotero.isConnector) {
yield _onLoadFull();
}
var ZoteroOverlay = new function () {
this.onLoad = async function () {
try {
if (!Zotero) {
throw new Error("No Zotero object");
}
catch (e) {
Zotero.debug(e, 1);
throw e;
if (Zotero.skipLoading) {
throw new Error("Skipping loading");
}
});
}
/**
* Initialize overlay in new windows in full mode
*
* This is never run in Zotero for Firefox if Standalone is open first and Z4Fx is opened
* second, but we don't care.
*/
var _onLoadFull = function () {
return Zotero.spawn(function* () {
yield Zotero.Promise.all([Zotero.initializationPromise, Zotero.unlockPromise]);
await Zotero.Promise.all([Zotero.initializationPromise, Zotero.unlockPromise]);
Zotero.debug("Initializing overlay");
if (Zotero.skipLoading) {
throw new Error("Skipping loading");
}
ZoteroPane.init();
// Clear old Zotero icon pref
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch('extensions.zotero.');
prefBranch.clearUserPref('statusBarIcon');
// Used for loading pages from upgrade wizard
if (Zotero.initialURL) {
setTimeout(function () {
Zotero.launchURL(ZOTERO_CONFIG.START_URL);
Zotero.initialURL = null;
}, 1);
}
}, this);
}
}
catch (e) {
Zotero.debug(e, 1);
throw e;
}
};
this.onUnload = function() {
ZoteroPane.destroy();
}
/**
* Hides/displays the Zotero interface
* @param {Boolean} makeVisible Whether or not Zotero interface should be visible
* @param {Boolean} dontRefocus If true, don't focus content when closing Zotero pane. Used
* when closing pane because Zotero Standalone is being opened, to avoid pulling Firefox to
* the foreground.
*/
this.toggleDisplay = function(makeVisible, dontRefocus)
{
if (!Zotero || Zotero.startupError || Zotero.skipLoading) {
ZoteroPane.displayStartupError();
return;
}
// Don't do anything if pane is already showing
if (makeVisible && ZoteroPane.isShowing()) {
return;
}
if(makeVisible === undefined) makeVisible = zoteroPane.hidden || zoteroPane.collapsed;
/*
Zotero.debug("zoteroPane.boxObject.height: " + zoteroPane.boxObject.height);
Zotero.debug("zoteroPane.getAttribute('height'): " + zoteroPane.getAttribute('height'));
Zotero.debug("zoteroPane.getAttribute('minheight'): " + zoteroPane.getAttribute('minheight'));
Zotero.debug("savedHeight: " + savedHeight);
*/
if(makeVisible) {
zoteroSplitter.setAttribute('hidden', false);
zoteroPane.setAttribute('hidden', false);
zoteroPane.setAttribute('collapsed', false);
// Get saved height (makeVisible() may change it)
if (zoteroPane.hasAttribute('savedHeight')) {
var savedHeight = zoteroPane.getAttribute('savedHeight');
}
else {
var savedHeight = DEFAULT_ZPANE_HEIGHT;
}
// Restore height
var max = document.getElementById('appcontent').boxObject.height
- zoteroSplitter.boxObject.height;
zoteroPane.setAttribute('height', Math.min(savedHeight, max));
// Make visible
ZoteroPane.makeVisible();
// Warn about unsafe data directory on first display
Zotero.DataDirectory.checkForUnsafeLocation(Zotero.DataDirectory.dir); // async
// Make sure tags splitter isn't missing for people upgrading from <2.0b7
document.getElementById('zotero-tags-splitter').collapsed = false;
} else {
ZoteroPane.makeHidden();
// Collapse pane
zoteroSplitter.setAttribute('hidden', true);
zoteroPane.setAttribute('collapsed', true);
zoteroPane.height = 0;
document.getElementById('content').setAttribute('collapsed', false);
if(!dontRefocus) {
// Return focus to the browser content pane
window.content.window.focus();
}
}
}
}
window.addEventListener("load", function(e) {
window.addEventListener("load", async function(e) {
try {
ZoteroOverlay.onLoad(e);
await ZoteroOverlay.onLoad(e);
await ZoteroPane.makeVisible();
}
catch (e) {
Components.utils.reportError(e);

View file

@ -41,10 +41,6 @@
<toolbar id="zotero-toolbar" nowindowdrag="true"/>
<vbox id="appcontent">
<!-- onmouseup shouldn't be necessary but seems to help prevent tag selector from sometimes going off the screen -->
<splitter id="zotero-splitter" resizebefore="closest" resizeafter="closest" hidden="true"
onmouseup="ZoteroPane.updateTagSelectorSize()"/>
<stack id="zotero-pane-stack" persist="savedHeight" savedHeight="300" hidden="true"/>
<stack id="zotero-pane-stack"/>
</vbox>
</overlay>

View file

@ -26,6 +26,7 @@
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/preferences.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
<?xml-stylesheet href="chrome://zotero/skin/preferences.css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css"?>
@ -83,5 +84,5 @@
observerService.notifyObservers(null, "charsetmenu-selected", "other");
]]>
</script>
<script src="preferences.js" type="application/javascript;version=1.8"/>
<script src="preferences.js" type="application/javascript"/>
</prefwindow>

View file

@ -24,6 +24,7 @@
*/
Components.utils.import("resource://gre/modules/Services.jsm");
import FilePicker from 'zotero/filePicker';
Zotero_Preferences.Advanced = {
_openURLResolvers: null,
@ -554,25 +555,20 @@ Zotero_Preferences.Attachment_Base_Directory = {
},
choosePath: Zotero.Promise.coroutine(function* () {
choosePath: async function () {
var oldPath = this.getPath();
//Prompt user to choose new base path
var fp = new FilePicker();
if (oldPath) {
var oldPathFile = Zotero.File.pathToFile(oldPath);
fp.displayDirectory = oldPath;
}
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
if (oldPathFile) {
fp.displayDirectory = oldPathFile;
}
fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), nsIFilePicker.modeGetFolder);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() != nsIFilePicker.returnOK) {
fp.init(window, Zotero.getString('attachmentBasePath.selectDir'), fp.modeGetFolder);
fp.appendFilters(fp.filterAll);
if (await fp.show() != fp.returnOK) {
return false;
}
var newPath = OS.Path.normalize(fp.file.path);
var newPath = fp.file;
if (oldPath && oldPath == newPath) {
Zotero.debug("Base directory hasn't changed");
@ -580,7 +576,7 @@ Zotero_Preferences.Attachment_Base_Directory = {
}
return this.changePath(newPath);
}),
},
changePath: Zotero.Promise.coroutine(function* (basePath) {

View file

@ -47,7 +47,6 @@
<preference id="pref-openURL-resolver" name="extensions.zotero.openURL.resolver" type="string"/>
<preference id="pref-openURL-version" name="extensions.zotero.openURL.version" type="string"/>
<preference id="pref-keys-openZotero" name="extensions.zotero.keys.openZotero" type="string"/>
<preference id="pref-keys-saveToZotero" name="extensions.zotero.keys.saveToZotero" type="string"/>
<preference id="pref-keys-library" name="extensions.zotero.keys.library" type="string"/>
<preference id="pref-keys-quicksearch" name="extensions.zotero.keys.quicksearch" type="string"/>
@ -341,5 +340,5 @@
<separator/>
</prefpane>
<script src="preferences_advanced.js" type="application/javascript;version=1.8"/>
<script src="preferences_advanced.js" type="application/javascript"/>
</overlay>

View file

@ -25,6 +25,8 @@
"use strict";
import FilePicker from 'zotero/filePicker';
Zotero_Preferences.Cite = {
wordPluginIDs: new Set([
'zoteroOpenOfficeIntegration@zotero.org',
@ -123,8 +125,11 @@ Zotero_Preferences.Cite = {
// (The first two aren't sent to the client normally, but hide anyway in case they are.)
var style = doc.createElement('style');
style.type = 'text/css';
style.innerHTML = 'h1, #intro, .style-individual-link, .style-view-source { display: none !important; }';
Zotero.debug(doc.documentElement.innerHTML);
style.innerHTML = 'h1, #intro, .style-individual-link, .style-view-source { display: none !important; }'
// TEMP: Default UA styles that aren't being included in Firefox 60 for some reason
+ 'html { background: #fff; }'
+ 'a { color: rgb(0, 0, 238) !important; text-decoration: underline; }'
+ 'a:active { color: rgb(238, 0, 0) !important; }';
doc.getElementsByTagName('head')[0].appendChild(style);
});
},
@ -133,21 +138,27 @@ Zotero_Preferences.Cite = {
/**
* Adds a new style to the style pane
**/
addStyle: function () {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString("zotero.preferences.styles.addStyle"), nsIFilePicker.modeOpen);
addStyle: async function () {
var fp = new FilePicker();
fp.init(window, Zotero.getString("zotero.preferences.styles.addStyle"), fp.modeOpen);
fp.appendFilter("CSL Style", "*.csl");
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
Zotero.Styles.install({ file: fp.file }, fp.file.path, true)
.catch(function (e) {
var rv = await fp.show();
if (rv == fp.returnOK || rv == fp.returnReplace) {
try {
await Zotero.Styles.install(
{
file: Zotero.File.pathToFile(fp.file)
},
fp.file,
true
);
}
catch (e) {
(new Zotero.Exception.Alert("styles.install.unexpectedError",
fp.file.path, "styles.install.title", e)).present()
});
fp.file, "styles.install.title", e)).present()
}
}
},

View file

@ -110,5 +110,5 @@
</tabbox>
</prefpane>
<script src="preferences_cite.js" type="application/javascript;version=1.8"/>
<script src="preferences_cite.js" type="application/javascript"/>
</overlay>

View file

@ -111,6 +111,6 @@
<separator/>
<separator/>
<script src="preferences_export.js" type="application/javascript;version=1.8"/>
<script src="preferences_export.js" type="application/javascript"/>
</prefpane>
</overlay>

View file

@ -90,6 +90,6 @@
<separator/>
</prefpane>
<script src="preferences_proxies.js" type="application/javascript;version=1.8"/>
<script src="preferences_proxies.js" type="application/javascript"/>
</prefwindow>
</overlay>

View file

@ -27,6 +27,7 @@
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
import FilePicker from 'zotero/filePicker';
Zotero_Preferences.General = {
init: function () {
@ -51,28 +52,25 @@ Zotero_Preferences.General = {
//
// File handlers
//
chooseFileHandler: function (type) {
chooseFileHandler: async function (type) {
var pref = this._getFileHandlerPref(type);
var currentPath = Zotero.Prefs.get(pref);
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
var fp = new FilePicker();
if (currentPath) {
fp.displayDirectory = Zotero.File.pathToFile(OS.Path.dirname(currentPath));
fp.displayDirectory = OS.Path.dirname(currentPath);
}
fp.init(
window,
Zotero.getString('zotero.preferences.chooseApplication'),
nsIFilePicker.modeOpen
fp.modeOpen
);
fp.appendFilters(nsIFilePicker.filterApps);
if (fp.show() != nsIFilePicker.returnOK) {
fp.appendFilters(fp.filterApps);
if (await fp.show() != fp.returnOK) {
this._updateFileHandlerUI();
return false;
}
var newPath = OS.Path.normalize(fp.file.path);
this.setFileHandler(type, newPath);
this.setFileHandler(type, fp.file);
},
setFileHandler: function (type, handler) {

View file

@ -104,5 +104,5 @@
<separator/>
</prefpane>
<script src="preferences_general.js" type="application/javascript;version=1.8"/>
<script src="preferences_general.js" type="application/javascript"/>
</overlay>

View file

@ -93,5 +93,5 @@
<separator/>
</prefpane>
<script src="preferences_search.js" type="application/javascript;version=1.8"/>
<script src="preferences_search.js" type="application/javascript"/>
</overlay>

View file

@ -326,5 +326,5 @@
<separator/>
</prefpane>
<script src="preferences_sync.js" type="application/javascript;version=1.8"/>
<script src="preferences_sync.js" type="application/javascript"/>
</overlay>

View file

@ -26,8 +26,9 @@
/**
* @fileOverview Tools for automatically retrieving a citation for the given PDF
*/
import FilePicker from 'zotero/filePicker';
/**
* Front end for recognizing PDFs
* @namespace
@ -50,13 +51,11 @@ var Zotero_RTFScan = new function() {
this.introPageShowing = function() {
var path = Zotero.Prefs.get("rtfScan.lastInputFile");
if(path) {
inputFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
inputFile.initWithPath(path);
inputFile = Zotero.File.pathToFile(path);
}
var path = Zotero.Prefs.get("rtfScan.lastOutputFile");
if(path) {
outputFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
outputFile.initWithPath(path);
outputFile = Zotero.File.pathToFile(path);
}
_updatePath();
document.getElementById("choose-input-file").focus();
@ -73,19 +72,17 @@ var Zotero_RTFScan = new function() {
/**
* Called to select the file to be processed
*/
this.chooseInputFile = function() {
this.chooseInputFile = async function () {
// display file picker
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString("rtfScan.openTitle"), nsIFilePicker.modeOpen);
var fp = new FilePicker();
fp.init(window, Zotero.getString("rtfScan.openTitle"), fp.modeOpen);
fp.appendFilters(nsIFilePicker.filterAll);
fp.appendFilters(fp.filterAll);
fp.appendFilter(Zotero.getString("rtfScan.rtf"), "*.rtf");
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
inputFile = fp.file;
var rv = await fp.show();
if (rv == fp.returnOK || rv == fp.returnReplace) {
inputFile = Zotero.File.pathToFile(fp.file);
_updatePath();
}
}
@ -93,11 +90,9 @@ var Zotero_RTFScan = new function() {
/**
* Called to select the output file
*/
this.chooseOutputFile = function() {
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString("rtfScan.saveTitle"), nsIFilePicker.modeSave);
this.chooseOutputFile = async function () {
var fp = new FilePicker();
fp.init(window, Zotero.getString("rtfScan.saveTitle"), fp.modeSave);
fp.appendFilter(Zotero.getString("rtfScan.rtf"), "*.rtf");
if(inputFile) {
var leafName = inputFile.leafName;
@ -110,9 +105,9 @@ var Zotero_RTFScan = new function() {
fp.defaultString = "Untitled.rtf";
}
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
outputFile = fp.file;
var rv = await fp.show();
if (rv == fp.returnOK || rv == fp.returnReplace) {
outputFile = Zotero.File.pathToFile(fp.file);
_updatePath();
}
}

View file

@ -37,6 +37,9 @@
]>
<window id="main-window" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
Components.utils.import("resource://gre/modules/Services.jsm");
</script>
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
<script>
// Equivalent to Zotero.openMainWindow()

View file

@ -446,14 +446,11 @@ const ZoteroStandalone = new function() {
this.updateAddonsPane = function (doc) {
// Hide unsigned add-on verification warnings
//
// This only works for the initial load of the window. If the user switches to Appearance
// or Plugins and then back to Extensions, the warnings will appear again. A better way to
// disable this might be discoverable by studying
// https://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/content/extensions.js
var addonList = doc.getElementById('addon-list');
setTimeout(function () {
// Unsigned add-on warnings are hidden by default in extensions.css (via style rules added
// by fetch_xulrunner.sh), but allow other warnings
function updateExtensions () {
var addonList = doc.getElementById('addon-list');
for (let i = 0; i < addonList.itemCount; i++) {
let richListItem = addonList.getItemAtIndex(i);
let container = doc.getAnonymousElementByAttribute(
@ -463,13 +460,16 @@ const ZoteroStandalone = new function() {
let link = doc.getAnonymousElementByAttribute(
richListItem, 'anonid', 'warning-link'
);
if (link && link.href.indexOf('unsigned-addons') != -1) {
richListItem.removeAttribute('notification');
container.hidden = true;
if (link) {
if (!link.href.includes('unsigned-addons')) {
container.classList.add('allowed-warning');
}
}
}
}
});
}
doc.getElementById('category-extension').onclick = updateExtensions;
setTimeout(updateExtensions);
}
/**
@ -713,30 +713,43 @@ ZoteroStandalone.DebugOutput = {
function toJavaScriptConsole() {
toOpenWindowByType("global:console", "chrome://global/content/console.xul");
openWindowByType('chrome://global/content/console.xul', 'global:console');
}
function openRunJSWindow() {
window.open('chrome://zotero/content/runJS.html', 'run-js', 'width=900,height=700,resizable');
openWindowByType(
'chrome://zotero/content/runJS.html',
'zotero:run-js',
'chrome,width=900,height=700,resizable,centerscreen'
);
}
function openStyleEditor() {
window.open('chrome://zotero/content/tools/csledit.xul', 'style-editor', 'width=950,height=700,resizable');
openWindowByType(
'chrome://zotero/content/tools/csledit.xul',
'zotero:style-editor',
'chrome,width=950,height=700,resizable'
);
}
function openScaffold() {
window.open('chrome://scaffold/content/scaffold.xul', 'scaffold', 'chrome,resizable');
openWindowByType(
'chrome://scaffold/content/scaffold.xul',
'zotero:scaffold',
'chrome,resizable'
);
}
function toOpenWindowByType(inType, uri, features)
{
var topWindow = Services.wm.getMostRecentWindow(inType);
function openWindowByType(uri, type, features) {
var win = Services.wm.getMostRecentWindow(type);
if (topWindow) {
topWindow.focus();
} else if(features) {
if (win) {
win.focus();
}
else if (features) {
window.open(uri, "_blank", features);
} else {
}
else {
window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
}
}

View file

@ -300,7 +300,12 @@
</menupopup>
</menu>
<menu id="windowMenu" />
<menu id="windowMenu"
label="&windowMenu.label;"
onpopupshowing="macWindowMenuDidShow();"
onpopuphidden="macWindowMenuDidHide();"
>
</menu>
<menu id="helpMenu"
label="&helpMenu.label;"

View file

@ -11,7 +11,7 @@
style="padding:2em">
<script src="../include.js"/>
<script type="application/javascript;version=1.8" src="test.js"/>
<script type="application/javascript" src="test.js"/>
<hbox><label>This is a test page.</label></hbox>
</window>

View file

@ -7,12 +7,14 @@
<p id="result"></p>
<script src="../include.js"></script>
<script type="text/javascript">
Zotero.Promise.coroutine(function* (){
import FilePicker from 'zotero/filePicker';
(async function () {
// Create schema
var schema = {"itemTypes":{}, "creatorTypes":{}, "fields":{}};
var types = Zotero.ItemTypes.getTypes();
var fieldIDs = yield Zotero.DB.columnQueryAsync("SELECT fieldID FROM fieldsCombined");
var fieldIDs = await Zotero.DB.columnQueryAsync("SELECT fieldID FROM fieldsCombined");
var baseMappedFields = Zotero.ItemFields.getBaseMappedFields();
for (let fieldID of fieldIDs) {
@ -70,19 +72,20 @@
}
// Write to file
var nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString('dataDir.selectDir'), nsIFilePicker.modeGetFolder);
var fp = new FilePicker();
fp.init(window, Zotero.getString('dataDir.selectDir'), fp.modeGetFolder);
let resultElem = document.getElementById('result');
if (fp.show() != nsIFilePicker.returnOK) {
if (await fp.show() != fp.returnOK) {
result.innerHTML = '<p>Failed.</p>';
} else {
let schemaFile = fp.file;
schemaFile.append("connectorTypeSchemaData.js");
Zotero.File.putContents(schemaFile, `Zotero.Connector_Types.schema = ${JSON.stringify(schema)}`);
result.innerHTML = `<p>Wrote ${schemaFile.path} successfully.</p>`;
await Zotero.File.putContentsAsync(
schemaFile,
`Zotero.Connector_Types.schema = ${JSON.stringify(schema)}`
);
result.innerHTML = `<p>Wrote ${schemaFile} successfully.</p>`;
}
})();
</script>

View file

@ -23,6 +23,8 @@
***** END LICENSE BLOCK *****
*/
import FilePicker from 'zotero/filePicker';
var Zotero_CSL_Editor = new function() {
this.init = init;
this.handleKeyPress = handleKeyPress;
@ -83,13 +85,11 @@ var Zotero_CSL_Editor = new function() {
this.generateBibliography(this.loadStyleFromEditor());
}
this.save = function() {
this.save = async function () {
var editor = document.getElementById('zotero-csl-editor');
var style = editor.value;
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(window, Zotero.getString('styles.editor.save'), nsIFilePicker.modeSave);
var fp = new FilePicker();
fp.init(window, Zotero.getString('styles.editor.save'), fp.modeSave);
fp.appendFilter("Citation Style Language", "*.csl");
//get the filename from the id; we could consider doing even more here like creating the id from filename.
var parser = new DOMParser();
@ -102,10 +102,10 @@ var Zotero_CSL_Editor = new function() {
else {
fp.defaultString = "untitled.csl";
}
var rv = fp.show();
if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
var outputFile = fp.file;
Zotero.File.putContents(outputFile, style);
var rv = await fp.show();
if (rv == fp.returnOK || rv == fp.returnReplace) {
let outputFile = fp.file;
Zotero.File.putContentsAsync(outputFile, style);
}
};

View file

@ -858,15 +858,11 @@ Zotero.Attachments = new function(){
}
wbp.progressListener = new Zotero.WebProgressFinishListener(() => resolve());
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
.createInstance(Components.interfaces.nsIURL);
nsIURL.spec = url;
var headers = {};
if (options.referrer) {
headers.Referer = options.referrer;
}
Zotero.Utilities.Internal.saveURI(wbp, nsIURL, path, headers);
Zotero.Utilities.Internal.saveURI(wbp, url, path, headers);
});
if (options.isPDF) {
@ -2114,21 +2110,15 @@ Zotero.Attachments = new function(){
var parent = OS.Path.dirname(path);
var iterator = new OS.File.DirectoryIterator(parent);
try {
while (true) {
let entry = yield iterator.next();
yield iterator.forEach((entry) => {
if (entry.name.startsWith('.')) {
continue;
return;
}
numFiles++;
if (numFiles > 1) {
break;
iterator.close();
}
}
}
catch (e) {
if (e != StopIteration) {
throw e;
}
});
}
finally {
iterator.close();
@ -2427,53 +2417,43 @@ Zotero.Attachments = new function(){
this._getFileNameFromURL = function(url, contentType) {
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
.createInstance(Components.interfaces.nsIURL);
nsIURL.spec = url;
url = Zotero.Utilities.parseURL(url);
var ext = Zotero.MIME.getPrimaryExtension(contentType, nsIURL.fileExtension);
var fileBaseName = url.fileBaseName;
var fileExt = Zotero.MIME.getPrimaryExtension(contentType, url.fileExtension);
if (!nsIURL.fileName) {
var matches = nsIURL.directory.match(/\/([^\/]+)\/$/);
if (!fileBaseName) {
let matches = url.pathname.match(/\/([^\/]+)\/$/);
// If no filename, use the last part of the path if there is one
if (matches) {
nsIURL.fileName = matches[1];
fileBaseName = matches[1];
}
// Or just use the host
else {
nsIURL.fileName = nsIURL.host;
var tld = nsIURL.fileExtension;
fileBaseName = url.hostname;
}
}
// If we found a better extension, use that
if (ext && (!nsIURL.fileExtension || nsIURL.fileExtension != ext)) {
nsIURL.fileExtension = ext;
}
// If we replaced the TLD (which would've been interpreted as the extension), add it back
if (tld && tld != nsIURL.fileExtension) {
nsIURL.fileBaseName = nsIURL.fileBaseName + '.' + tld;
}
// Test unencoding fileBaseName
try {
decodeURIComponent(nsIURL.fileBaseName);
decodeURIComponent(fileBaseName);
}
catch (e) {
if (e.name == 'URIError') {
// If we got a 'malformed URI sequence' while decoding,
// use MD5 of fileBaseName
nsIURL.fileBaseName = Zotero.Utilities.Internal.md5(nsIURL.fileBaseName, false);
fileBaseName = Zotero.Utilities.Internal.md5(fileBaseName, false);
}
else {
throw e;
}
}
var fileName = fileBaseName + (fileExt ? '.' + fileExt : '');
// Pass unencoded name to getValidFileName() so that percent-encoded
// characters aren't stripped to just numbers
return Zotero.File.getValidFileName(decodeURIComponent(nsIURL.fileName));
return Zotero.File.getValidFileName(decodeURIComponent(fileName));
}

View file

@ -673,23 +673,8 @@ Zotero.Cite.Locale = {
if (str) {
return str;
}
var uri = `chrome://zotero/content/locale/csl/locales-${locale}.xml`;
try {
let protHandler = Components.classes["@mozilla.org/network/protocol;1?name=chrome"]
.createInstance(Components.interfaces.nsIProtocolHandler);
let channel = protHandler.newChannel(protHandler.newURI(uri));
let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Components.interfaces.nsIConverterInputStream);
cstream.init(channel.open(), "UTF-8", 0, 0);
let obj = {};
let read = 0;
let str = "";
do {
// Read as much as we can and put it in obj.value
read = cstream.readString(0xffffffff, obj);
str += obj.value;
} while (read != 0);
cstream.close();
str = Zotero.File.getResource(`chrome://zotero/content/locale/csl/locales-${locale}.xml`);
this._cache.set(locale, str);
return str;
}

View file

@ -221,7 +221,7 @@ Zotero.CookieSandbox.prototype = {
*/
"getCookiesForURI": function(uri) {
var hostParts = Zotero.CookieSandbox.normalizeHost(uri.host).split('.'),
pathParts = Zotero.CookieSandbox.normalizePath(uri.path).split('/'),
pathParts = Zotero.CookieSandbox.normalizePath(uri.filePath || uri.pathQueryRef).split('/'),
cookies = {}, found = false, secure = uri.scheme.toUpperCase() == 'HTTPS';
// Fetch cookies starting from the highest level domain

View file

@ -239,6 +239,7 @@ Zotero.CreatorTypes = new function() {
var sql = "SELECT itemTypeID, creatorTypeID AS id, creatorType AS name, primaryField "
+ "FROM itemTypeCreatorTypes NATURAL JOIN creatorTypes";
var rows = yield Zotero.DB.queryAsync(sql);
_creatorTypesByItemType = {};
for (let i=0; i<rows.length; i++) {
let row = rows[i];
let itemTypeID = row.itemTypeID;
@ -307,7 +308,8 @@ Zotero.CreatorTypes = new function() {
this.getLocalizedString = function(idOrName) {
return Zotero.getString("creatorTypes."+this.getName(idOrName));
var name = this.getName(idOrName);
return Zotero.Schema.globalSchemaLocale.creatorTypes[name];
}
@ -437,7 +439,7 @@ Zotero.ItemTypes = new function() {
return _customLabels[id];
}
return Zotero.getString("itemTypes." + typeName);
return Zotero.Schema.globalSchemaLocale.itemTypes[typeName];
}
this.getImageSrc = function (itemType) {

View file

@ -699,7 +699,25 @@ Zotero.Collection.prototype.serialize = function(nested) {
*
* If this object is identified (has an id or library/key), loadAllData() must have been called.
*/
Zotero.Collection.prototype.fromJSON = function (json) {
Zotero.Collection.prototype.fromJSON = function (json, options = {}) {
if (options.strict) {
for (let prop in json) {
switch (prop) {
case 'key':
case 'version':
case 'name':
case 'parentCollection':
case 'relations':
break;
default:
let e = new Error(`Unknown collection property '${prop}'`);
e.name = "ZoteroInvalidDataError";
throw e;
}
}
}
if (!json.name) {
throw new Error("'name' property not provided for collection");
}

View file

@ -171,7 +171,7 @@ Zotero.Creators = new function() {
},
this.cleanData = function (data) {
this.cleanData = function (data, options = {}) {
// Validate data
if (data.name === undefined && data.lastName === undefined) {
throw new Error("Creator data must contain either 'name' or 'firstName'/'lastName' properties");
@ -227,9 +227,12 @@ Zotero.Creators = new function() {
if (creatorType) {
cleanedData.creatorTypeID = Zotero.CreatorTypes.getID(creatorType);
if (!cleanedData.creatorTypeID) {
let msg = "'" + creatorType + "' isn't a valid creator type";
Zotero.debug(msg, 2);
Components.utils.reportError(msg);
if (options.strict) {
let e = new Error(`Unknown creator type '${creatorType}'`);
e.name = "ZoteroInvalidDataError";
throw e;
}
Zotero.warn(`'${creatorType}' isn't a valid creator type`);
}
}

View file

@ -129,7 +129,7 @@ Zotero.DataObjectUtilities = {
delete target[i];
}
}
// If field from base doesn't exist in new version, clear it
// Field from base doesn't exist in new version
else {
switch (i) {
// When changing an item from top-level to child, the collections property is
@ -137,14 +137,17 @@ Zotero.DataObjectUtilities = {
case 'collections':
break;
// Set known boolean fields to false
case 'deleted':
case 'parentItem':
case 'inPublications':
target[i] = false;
break;
// Skip other fields. This prevents us from clearing fields in the pristine JSON
// that aren't handled here.
default:
target[i] = '';
delete target[i];
}
}
}

View file

@ -272,8 +272,7 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped)
if (!unformatted) {
// Multipart date fields
// TEMP - filingDate
if (Zotero.ItemFields.isFieldOfBase(fieldID, 'date') || field == 'filingDate') {
if (Zotero.ItemFields.isDate(fieldID)) {
value = Zotero.Date.multipartToStr(value);
}
}
@ -283,9 +282,8 @@ Zotero.Item.prototype.getField = function(field, unformatted, includeBaseMapped)
Zotero.Item.prototype.getExtraField = function (fieldName) {
var fields = Zotero.Utilities.Internal.extractExtraFields(this.getField('extra'));
var doi = fields.get(fieldName);
return (doi && doi.value) ? doi.value : '';
var { fields } = Zotero.Utilities.Internal.extractExtraFields(this.getField('extra'));
return fields.get(fieldName) || '';
};
@ -577,7 +575,6 @@ Zotero.Item.prototype.setType = function(itemTypeID, loadIn) {
*/
Zotero.Item.prototype.getFieldsNotInType = function (itemTypeID, allowBaseConversion) {
var fieldIDs = [];
for (var field in this._itemData) {
if (this._itemData[field]) {
var fieldID = Zotero.ItemFields.getID(field);
@ -598,32 +595,9 @@ Zotero.Item.prototype.getFieldsNotInType = function (itemTypeID, allowBaseConver
fieldIDs.push(fieldID);
}
}
/*
var sql = "SELECT fieldID FROM itemTypeFields WHERE itemTypeID=?1 AND "
+ "fieldID IN (SELECT fieldID FROM itemData WHERE itemID=?2) AND "
+ "fieldID NOT IN (SELECT fieldID FROM itemTypeFields WHERE itemTypeID=?3)";
if (allowBaseConversion) {
// Not the type-specific field for a base field in the new type
sql += " AND fieldID NOT IN (SELECT fieldID FROM baseFieldMappings "
+ "WHERE itemTypeID=?1 AND baseFieldID IN "
+ "(SELECT fieldID FROM itemTypeFields WHERE itemTypeID=?3)) AND ";
// And not a base field with a type-specific field in the new type
sql += "fieldID NOT IN (SELECT baseFieldID FROM baseFieldMappings "
+ "WHERE itemTypeID=?3) AND ";
// And not the type-specific field for a base field that has
// a type-specific field in the new type
sql += "fieldID NOT IN (SELECT fieldID FROM baseFieldMappings "
+ "WHERE itemTypeID=?1 AND baseFieldID IN "
+ "(SELECT baseFieldID FROM baseFieldMappings WHERE itemTypeID=?3))";
}
return Zotero.DB.columnQuery(sql, [this.itemTypeID, this.id, { int: itemTypeID }]);
*/
if (!fieldIDs.length) {
return false;
}
return fieldIDs;
}
@ -753,7 +727,7 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
throw new Error('"' + field + '" is not a valid itemData field');
}
if (loadIn && this.isNote() && field == 110) { // title
if (loadIn && this.isNote() && field == Zotero.ItemFields.getID('title')) {
this._noteTitle = value ? value : "";
return true;
}
@ -798,9 +772,8 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
if (!loadIn) {
// Save date field as multipart date
// TEMP - filingDate
if (value !== false
&& (Zotero.ItemFields.isFieldOfBase(fieldID, 'date') || field == 'filingDate')
&& (Zotero.ItemFields.isDate(fieldID))
&& !Zotero.Date.isMultipart(value)) {
value = Zotero.Date.strToMultipart(value);
}
@ -867,19 +840,29 @@ Zotero.Item.prototype.updateDisplayTitle = function () {
var itemTypeID = this.itemTypeID;
var itemTypeName = Zotero.ItemTypes.getName(itemTypeID);
if (title === "" && (itemTypeID == 8 || itemTypeID == 10)) { // 'letter' and 'interview' itemTypeIDs
var itemTypeLetter = Zotero.ItemTypes.getID('letter');
var itemTypeInterview = Zotero.ItemTypes.getID('interview');
var itemTypeCase = Zotero.ItemTypes.getID('case');
var creatorTypeAuthor = Zotero.CreatorTypes.getID('author');
var creatorTypeRecipient = Zotero.CreatorTypes.getID('recipient');
var creatorTypeInterviewer = Zotero.CreatorTypes.getID('interviewer');
var creatorTypeInterviewee = Zotero.CreatorTypes.getID('interviewee');
// 'letter' and 'interview'
if (title === "" && (itemTypeID == itemTypeLetter || itemTypeID == itemTypeInterview)) {
var creatorsData = this.getCreators();
var authors = [];
var participants = [];
for (let i=0; i<creatorsData.length; i++) {
let creatorData = creatorsData[i];
let creatorTypeID = creatorsData[i].creatorTypeID;
if ((itemTypeID == 8 && creatorTypeID == 16) || // 'letter'
(itemTypeID == 10 && creatorTypeID == 7)) { // 'interview'
if ((itemTypeID == itemTypeLetter && creatorTypeID == creatorTypeRecipient) ||
(itemTypeID == itemTypeInterview && creatorTypeID == creatorTypeInterviewer)) {
participants.push(creatorData);
}
else if ((itemTypeID == 8 && creatorTypeID == 1) || // 'letter'/'author'
(itemTypeID == 10 && creatorTypeID == 6)) { // 'interview'/'interviewee'
else if ((itemTypeID == itemTypeLetter && creatorTypeID == creatorTypeAuthor) ||
(itemTypeID == itemTypeInterview && creatorTypeID == creatorTypeInterviewee)) {
authors.push(creatorData);
}
}
@ -919,7 +902,8 @@ Zotero.Item.prototype.updateDisplayTitle = function () {
title = '[' + strParts.join('; ') + ']';
}
else if (itemTypeID == 17) { // 'case' itemTypeID
// 'case'
else if (itemTypeID == itemTypeCase) {
if (title) { // common law cases always have case names
var reporter = this.getField('reporter');
if (reporter) {
@ -946,7 +930,7 @@ Zotero.Item.prototype.updateDisplayTitle = function () {
}
var creatorData = this.getCreator(0);
if (creatorData && creatorData.creatorTypeID === 1) { // author
if (creatorData && creatorData.creatorTypeID === creatorTypeAuthor) {
strParts.push(creatorData.lastName);
}
@ -1038,8 +1022,10 @@ Zotero.Item.prototype.getCreatorsJSON = function () {
* <li>'name' or 'firstName'/'lastName', or 'firstName'/'lastName'/'fieldMode'</li>
* <li>'creatorType' (can be name or id) or 'creatorTypeID'</li>
* </ul>
* @param {Object} [options]
* @param {Boolean} [options.strict] - Throw on invalid creator type
*/
Zotero.Item.prototype.setCreator = function (orderIndex, data) {
Zotero.Item.prototype.setCreator = function (orderIndex, data, options = {}) {
var itemTypeID = this._itemTypeID;
if (!itemTypeID) {
throw new Error('Item type must be set before setting creators');
@ -1047,7 +1033,8 @@ Zotero.Item.prototype.setCreator = function (orderIndex, data) {
this._requireData('creators');
data = Zotero.Creators.cleanData(data);
var origCreatorType = data.creatorType;
data = Zotero.Creators.cleanData(data, options);
if (data.creatorTypeID === undefined) {
throw new Error("Creator data must include a valid 'creatorType' or 'creatorTypeID' property");
@ -1055,9 +1042,14 @@ Zotero.Item.prototype.setCreator = function (orderIndex, data) {
// If creatorTypeID isn't valid for this type, use the primary type
if (!data.creatorTypeID || !Zotero.CreatorTypes.isValidForItemType(data.creatorTypeID, itemTypeID)) {
var msg = "Creator type '" + Zotero.CreatorTypes.getName(data.creatorTypeID) + "' "
+ "isn't valid for " + Zotero.ItemTypes.getName(itemTypeID)
+ " -- changing to primary creator";
let itemType = Zotero.ItemTypes.getName(itemTypeID);
if (options.strict) {
let e = new Error(`Invalid creator type '${origCreatorType}' for type ${itemType}`);
e.name = "ZoteroInvalidDataError";
throw e;
}
let msg = `Creator type '${origCreatorType}' isn't valid for ${itemType} -- `
+ "changing to primary creator";
Zotero.warn(msg);
data.creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID);
}
@ -1087,7 +1079,7 @@ Zotero.Item.prototype.setCreator = function (orderIndex, data) {
/**
* @param {Object[]} data - An array of creator data in internal or API JSON format
*/
Zotero.Item.prototype.setCreators = function (data) {
Zotero.Item.prototype.setCreators = function (data, options = {}) {
// If empty array, clear all existing creators
if (!data.length) {
while (this.hasCreatorAt(0)) {
@ -1097,7 +1089,7 @@ Zotero.Item.prototype.setCreators = function (data) {
}
for (let i = 0; i < data.length; i++) {
this.setCreator(i, data[i]);
this.setCreator(i, data[i], options);
}
}
@ -2239,7 +2231,7 @@ Zotero.Item.prototype.getFilePath = function () {
}
let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
.createInstance(Components.interfaces.nsIFile);
try {
file.persistentDescriptor = path;
}
@ -2345,7 +2337,7 @@ Zotero.Item.prototype.getFilePathAsync = Zotero.Promise.coroutine(function* () {
// the file couldn't be found
if (Zotero.isMac && path.startsWith('AAAA')) {
let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
.createInstance(Components.interfaces.nsIFile);
try {
file.persistentDescriptor = path;
}
@ -3292,14 +3284,15 @@ Zotero.Item.prototype.getBestAttachments = Zotero.Promise.coroutine(function* ()
}
var url = this.getField('url');
var urlFieldID = Zotero.ItemFields.getID('url');
var sql = "SELECT IA.itemID FROM itemAttachments IA NATURAL JOIN items I "
+ "LEFT JOIN itemData ID ON (IA.itemID=ID.itemID AND fieldID=1) "
+ `LEFT JOIN itemData ID ON (IA.itemID=ID.itemID AND fieldID=${urlFieldID}) `
+ "LEFT JOIN itemDataValues IDV ON (ID.valueID=IDV.valueID) "
+ "WHERE parentItemID=? AND linkMode NOT IN (?) "
+ `WHERE parentItemID=? AND linkMode NOT IN (${Zotero.Attachments.LINK_MODE_LINKED_URL}) `
+ "AND IA.itemID NOT IN (SELECT itemID FROM deletedItems) "
+ "ORDER BY contentType='application/pdf' DESC, value=? DESC, dateAdded ASC";
var itemIDs = yield Zotero.DB.columnQueryAsync(sql, [this.id, Zotero.Attachments.LINK_MODE_LINKED_URL, url]);
var itemIDs = yield Zotero.DB.columnQueryAsync(sql, [this.id, url]);
return this.ObjectsClass.get(itemIDs);
});
@ -4193,31 +4186,48 @@ Zotero.Item.prototype.isCollection = function() {
/**
* Populate the object's data from an API JSON data object
*
* @param {Object} json
* @param {Object} [options]
* @param {Boolean} [options.strict = false] - Throw on unknown field or invalid field for type
*/
Zotero.Item.prototype.fromJSON = function (json) {
Zotero.Item.prototype.fromJSON = function (json, options = {}) {
var strict = !!options.strict;
if (!json.itemType && !this._itemTypeID) {
throw new Error("itemType property not provided");
}
let itemTypeID = Zotero.ItemTypes.getID(json.itemType);
if (!itemTypeID) {
let e = new Error(`Invalid item type '${json.itemType}'`);
e.name = "ZoteroUnknownTypeError";
let e = new Error(`Unknown item type '${json.itemType}'`);
e.name = "ZoteroInvalidDataError";
throw e;
}
this.setType(itemTypeID);
var isValidForType = {};
var setFields = {};
var setFields = new Set();
/*var { fields: extraFields, creators: extraCreators, extra } = Zotero.Utilities.Internal.extractExtraFields(
json.extra !== undefined ? json.extra : '',
this,
Object.keys(json)
);*/
// Transfer valid fields from Extra to regular fields
// Currently disabled
/*for (let [field, value] of extraFields) {
this.setField(field, value);
setFields.add(field);
extraFields.delete(field);
}*/
// Primary data
for (let field in json) {
let val = json[field];
switch (field) {
case 'key':
case 'version':
case 'synced':
case 'itemType':
case 'note':
// Use?
@ -4228,6 +4238,7 @@ Zotero.Item.prototype.fromJSON = function (json) {
case 'parentItem':
case 'deleted':
case 'inPublications':
case 'extra':
break;
case 'accessDate':
@ -4240,7 +4251,7 @@ Zotero.Item.prototype.fromJSON = function (json) {
val = Zotero.Date.dateToSQL(d, true);
}
this.setField(field, val);
setFields[field] = true;
setFields.add(field);
break;
case 'dateAdded':
@ -4257,7 +4268,8 @@ Zotero.Item.prototype.fromJSON = function (json) {
break;
case 'creators':
this.setCreators(json.creators);
//this.setCreators(json.creators.concat(extraCreators), options);
this.setCreators(json.creators, options);
break;
case 'tags':
@ -4300,8 +4312,20 @@ Zotero.Item.prototype.fromJSON = function (json) {
default:
let fieldID = Zotero.ItemFields.getID(field);
if (!fieldID) {
Zotero.logError("Discarding unknown JSON field '" + field + "' for item "
+ this.libraryKey);
// In strict mode, fail on unknown field
if (strict) {
let e = new Error(`Unknown field '${field}'`);
e.name = "ZoteroInvalidDataError";
throw e;
}
// Otherwise store in Extra
// TEMP: Disabled for now, along with tests in itemTest.js
/*if (typeof val == 'string') {
Zotero.warn(`Storing unknown field '${field}' in Extra for item ${this.libraryKey}`);
extraFields.set(field, val);
break;
}*/
Zotero.warn(`Discarding unknown JSON ${typeof val} '${field}' for item ${this.libraryKey}`);
continue;
}
// Convert to base-mapped field if necessary, so that setFields has the base-mapped field
@ -4314,15 +4338,30 @@ Zotero.Item.prototype.fromJSON = function (json) {
}
isValidForType[field] = Zotero.ItemFields.isValidForType(fieldID, this.itemTypeID);
if (!isValidForType[field]) {
Zotero.logError("Discarding invalid field '" + origField + "' for type " + itemTypeID
+ " for item " + this.libraryKey);
let type = Zotero.ItemTypes.getName(itemTypeID);
// In strict mode, fail on invalid field for type
if (strict) {
let e = new Error(`Invalid field '${origField}' for type ${type}`);
e.name = "ZoteroInvalidDataError";
throw e;
}
// Otherwise store in Extra
// TEMP: Disabled for now, since imports can assign values to multiple versions of
// fields
// https://groups.google.com/d/msg/zotero-dev/a1IPUJ2m_3s/hfmdK2P3BwAJ
/*Zotero.warn(`Storing invalid field '${origField}' for type ${type} in Extra for `
+ `item ${this.libraryKey}`);
extraFields.set(field, val);*/
continue;
}
this.setField(field, json[origField]);
setFields[field] = true;
setFields.add(field);
}
}
//this.setField('extra', Zotero.Utilities.Internal.combineExtraFields(extra, extraFields));
this.setField('extra', json.extra !== undefined ? json.extra : '');
if (json.collections || this._collections.length) {
this.setCollections(json.collections);
}
@ -4330,7 +4369,7 @@ Zotero.Item.prototype.fromJSON = function (json) {
// Clear existing fields not specified
var previousFields = this.getUsedFields(true);
for (let field of previousFields) {
if (!setFields[field] && isValidForType[field] !== false) {
if (!setFields.has(field) && isValidForType[field] !== false && field != 'extra') {
this.setField(field, false);
}
}
@ -4511,6 +4550,45 @@ Zotero.Item.prototype.toResponseJSON = function (options = {}) {
};
/**
* Migrate valid fields in Extra to real fields
*
* A separate save is required
*/
Zotero.Item.prototype.migrateExtraFields = function () {
var { itemType, fields, creators, extra } = Zotero.Utilities.Internal.extractExtraFields(
this.getField('extra'), this
);
if (itemType) {
this.setType(Zotero.ItemTypes.getID(itemType));
}
for (let [field, value] of fields) {
this.setField(field, value);
}
if (creators.length) {
this.setCreators([...item.getCreators(), ...creators]);
}
this.setField('extra', extra);
if (!this.hasChanged()) {
return false;
}
Zotero.debug("Migrating Extra fields for item " + this.libraryKey);
if (itemType) {
Zotero.debug("Item Type: " + itemType);
}
if (fields.size) {
Zotero.debug(Array.from(fields.entries()));
}
if (creators.length) {
Zotero.debug(creators);
}
Zotero.debug(extra);
return true;
}
//////////////////////////////////////////////////////////////////////////////
//
// Asynchronous load methods

View file

@ -42,7 +42,6 @@ Zotero.ItemFields = new function() {
// Privileged methods
this.getName = getName;
this.getID = getID;
this.getLocalizedString = getLocalizedString;
this.isValidForType = isValidForType;
this.isInteger = isInteger;
this.getItemTypeFields = getItemTypeFields;
@ -77,17 +76,29 @@ Zotero.ItemFields = new function() {
var baseFields = yield Zotero.DB.columnQueryAsync(sql);
for (let field of fields) {
_fields[field['fieldID']] = {
id: field['fieldID'],
let isBaseField = baseFields.includes(field.fieldID);
let label;
try {
label = field.label || Zotero.Schema.globalSchemaLocale.fields[field.fieldName];
}
// Some base fields aren't localized
catch (e) {
if (!isBaseField) {
throw e;
}
}
_fields[field.fieldID] = {
id: field.fieldID,
name: field.fieldName,
label: field.label,
label,
custom: !!field.custom,
isBaseField: (baseFields.indexOf(field['fieldID']) != -1),
formatID: field['fieldFormatID'],
itemTypes: fieldItemTypes[field['fieldID']]
isBaseField: baseFields.includes(field.fieldID),
formatID: field.fieldFormatID,
itemTypes: fieldItemTypes[field.fieldID]
};
// Store by name as well as id
_fields[field['fieldName']] = _fields[field['fieldID']];
_fields[field.fieldName] = _fields[field.fieldID];
_allFields.push({
id: field.fieldID,
name: field.fieldName
@ -133,40 +144,29 @@ Zotero.ItemFields = new function() {
};
function getLocalizedString(itemType, field) {
// unused currently
//var typeName = Zotero.ItemTypes.getName(itemType);
var fieldName = Zotero.ItemFields.getName(field);
this.getLocalizedString = function (field) {
if (arguments.length == 2) {
Zotero.warn("Zotero.ItemFields.getLocalizedString() no longer takes two arguments "
+ "-- update your code");
field = arguments[1];
}
var fieldName = this.getName(field);
// Fields in the items table are special cases
switch (field) {
case 'dateAdded':
case 'dateModified':
case 'itemType':
return Zotero.getString("itemFields." + field);
return Zotero.Schema.globalSchemaLocale.fields[field];
}
// TODO: different labels for different item types
_fieldCheck(field, 'getLocalizedString');
_fieldCheck(field);
if (_fields[field].label) {
return _fields[field].label;
}
else {
try {
var loc = Zotero.getString("itemFields." + fieldName);
}
// If localized string not found, try base field
catch (e) {
Zotero.debug("Localized string not found for field '" + fieldName + "' -- trying base field");
var baseFieldID = this.getBaseIDFromTypeAndField(itemType, field);
fieldName = this.getName(baseFieldID);
var loc = Zotero.getString("itemFields." + fieldName);
}
return loc;
}
}
return _fields[field].label;
};
function isValidForType(fieldID, itemTypeID) {
@ -182,15 +182,28 @@ Zotero.ItemFields = new function() {
function isInteger(fieldID) {
_fieldCheck(fieldID, 'isInteger');
_fieldCheck(fieldID);
var ffid = _fields[fieldID]['formatID'];
return _fieldFormats[ffid] ? _fieldFormats[ffid]['isInteger'] : false;
}
this.isDate = function (field) {
var fieldID = this.getID(field);
var fieldName = this.getName(field);
if (Zotero.ItemFields.isFieldOfBase(fieldID, 'date')) {
return true;
}
if (Zotero.Schema.globalSchemaMeta.fields[fieldName]) {
return Zotero.Schema.globalSchemaMeta.fields[fieldName].type == 'date'
}
return false;
};
this.isCustom = function (fieldID) {
_fieldCheck(fieldID, 'isCustom');
_fieldCheck(fieldID);
return _fields[fieldID].custom;
}
@ -202,7 +215,7 @@ Zotero.ItemFields = new function() {
function getItemTypeFields(itemTypeID) {
if (!itemTypeID) {
let e = new Error("Invalid item type id '" + itemTypeID + "'");
e.name = "ZoteroUnknownTypeError";
e.name = "ZoteroInvalidDataError";
throw e;
}
@ -219,14 +232,14 @@ Zotero.ItemFields = new function() {
function isBaseField(field) {
_fieldCheck(field, arguments.callee.name);
_fieldCheck(field);
return _fields[field]['isBaseField'];
}
function isFieldOfBase(field, baseField) {
var fieldID = _fieldCheck(field, 'isFieldOfBase');
var fieldID = _fieldCheck(field);
var baseFieldID = this.getID(baseField);
if (!baseFieldID) {
@ -299,7 +312,7 @@ Zotero.ItemFields = new function() {
throw new Error("Invalid item type '" + itemType + "'");
}
_fieldCheck(typeField, 'getBaseIDFromTypeAndField');
_fieldCheck(typeField);
if (!this.isValidForType(typeFieldID, itemTypeID)) {
throw new Error("'" + typeField + "' is not a valid field for '" + itemType + "'");
@ -406,11 +419,11 @@ Zotero.ItemFields = new function() {
* Check whether a field is valid, throwing an exception if not
* (since it should never actually happen)
**/
function _fieldCheck(field, func) {
function _fieldCheck(field) {
var fieldID = Zotero.ItemFields.getID(field);
if (!fieldID) {
Zotero.debug((new Error).stack, 1);
throw new Error("Invalid field '" + field + (func ? "' in ItemFields." + func + "()" : "'"));
throw new Error(`Invalid field '${field}`);
}
return fieldID;
}
@ -508,8 +521,9 @@ Zotero.ItemFields = new function() {
var sql = 'SELECT itemTypeID, fieldID FROM itemTypeFieldsCombined ORDER BY orderIndex';
var rows = yield Zotero.DB.queryAsync(sql);
_itemTypeFields = {};
_itemTypeFields[1] = []; // notes have no fields
_itemTypeFields = {
[Zotero.ItemTypes.getID('note')]: [] // Notes have no fields
};
for (let i=0; i<rows.length; i++) {
let row = rows[i];

View file

@ -36,6 +36,9 @@ Zotero.Items = function() {
// but otherwise it can be just a simple property
Zotero.defineProperty(this, "_primaryDataSQLParts", {
get: function () {
var itemTypeAttachment = Zotero.ItemTypes.getID('attachment');
var itemTypeNote = Zotero.ItemTypes.getID('note');
return {
itemID: "O.itemID",
itemTypeID: "O.itemTypeID",
@ -52,8 +55,10 @@ Zotero.Items = function() {
deleted: "DI.itemID IS NOT NULL AS deleted",
inPublications: "PI.itemID IS NOT NULL AS inPublications",
parentID: "(CASE O.itemTypeID WHEN 14 THEN IAP.itemID WHEN 1 THEN INoP.itemID END) AS parentID",
parentKey: "(CASE O.itemTypeID WHEN 14 THEN IAP.key WHEN 1 THEN INoP.key END) AS parentKey",
parentID: `(CASE O.itemTypeID WHEN ${itemTypeAttachment} THEN IAP.itemID `
+ `WHEN ${itemTypeNote} THEN INoP.itemID END) AS parentID`,
parentKey: `(CASE O.itemTypeID WHEN ${itemTypeAttachment} THEN IAP.key `
+ `WHEN ${itemTypeNote} THEN INoP.key END) AS parentKey`,
attachmentCharset: "CS.charset AS attachmentCharset",
attachmentLinkMode: "IA.linkMode AS attachmentLinkMode",

View file

@ -282,7 +282,7 @@ Zotero.Search.prototype.addCondition = function (condition, operator, value, req
if (!Zotero.SearchConditions.hasOperator(condition, operator)){
let e = new Error("Invalid operator '" + operator + "' for condition " + condition);
e.name = "ZoteroUnknownFieldError";
e.name = "ZoteroInvalidDataError";
throw e;
}
@ -425,7 +425,7 @@ Zotero.Search.prototype.updateCondition = function (searchConditionID, condition
if (!Zotero.SearchConditions.hasOperator(condition, operator)){
let e = new Error("Invalid operator '" + operator + "' for condition " + condition);
e.name = "ZoteroUnknownFieldError";
e.name = "ZoteroInvalidDataError";
throw e;
}
@ -808,8 +808,29 @@ Zotero.Search.prototype.search = Zotero.Promise.coroutine(function* (asTempTable
* Populate the object's data from an API JSON data object
*
* If this object is identified (has an id or library/key), loadAll() must have been called.
*
* @param {Object} json
* @param {Object} [options]
* @param {Boolean} [options.strict = false] - Throw on unknown property
*/
Zotero.Search.prototype.fromJSON = function (json) {
Zotero.Search.prototype.fromJSON = function (json, options = {}) {
if (options.strict) {
for (let prop in json) {
switch (prop) {
case 'key':
case 'version':
case 'name':
case 'conditions':
break;
default:
let e = new Error(`Unknown search property '${prop}'`);
e.name = "ZoteroInvalidDataError";
throw e;
}
}
}
if (json.name) {
this.name = json.name;
}

View file

@ -629,7 +629,7 @@ Zotero.SearchConditions = new function(){
if (!_conditions[condition]){
let e = new Error("Invalid condition '" + condition + "' in hasOperator()");
e.name = "ZoteroUnknownFieldError";
e.name = "ZoteroInvalidDataError";
throw e;
}
@ -651,7 +651,7 @@ Zotero.SearchConditions = new function(){
return Zotero.getString('searchConditions.' + str)
}
catch (e) {
return Zotero.ItemFields.getLocalizedString(null, str);
return Zotero.ItemFields.getLocalizedString(str);
}
}

View file

@ -25,6 +25,8 @@
"use strict";
import FilePicker from 'zotero/filePicker';
Zotero.DataDirectory = {
MIGRATION_MARKER: 'migrate-dir',
@ -98,7 +100,7 @@ Zotero.DataDirectory = {
let nsIFile;
try {
nsIFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
.createInstance(Components.interfaces.nsIFile);
nsIFile.persistentDescriptor = prefVal;
}
catch (e) {
@ -263,7 +265,7 @@ Zotero.DataDirectory = {
let nsIFile;
try {
nsIFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
.createInstance(Components.interfaces.nsIFile);
nsIFile.persistentDescriptor = prefs['extensions.zotero.dataDir'];
}
catch (e) {
@ -439,7 +441,7 @@ Zotero.DataDirectory = {
},
choose: Zotero.Promise.coroutine(function* (forceQuitNow, useHomeDir, moreInfoCallback) {
choose: async function (forceQuitNow, useHomeDir, moreInfoCallback) {
var win = Services.wm.getMostRecentWindow('navigator:browser');
var ps = Services.prompt;
@ -450,17 +452,13 @@ Zotero.DataDirectory = {
}
}
else {
var nsIFilePicker = Components.interfaces.nsIFilePicker;
while (true) {
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(win, Zotero.getString('dataDir.selectDir'), nsIFilePicker.modeGetFolder);
fp.displayDirectory = Zotero.File.pathToFile(
this._dir ? this._dir : OS.Path.dirname(this.defaultDir)
);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() == nsIFilePicker.returnOK) {
var file = fp.file;
let fp = new FilePicker();
fp.init(win, Zotero.getString('dataDir.selectDir'), fp.modeGetFolder);
fp.displayDirectory = this._dir ? this._dir : OS.Path.dirname(this.defaultDir);
fp.appendFilters(fp.filterAll);
if (await fp.show() == fp.returnOK) {
let file = Zotero.File.pathToFile(fp.file);
let dialogText = '';
let dialogTitle = '';
@ -547,24 +545,22 @@ Zotero.DataDirectory = {
}
return useHomeDir ? true : file;
}),
},
forceChange: function (win) {
forceChange: async function (win) {
if (!win) {
win = Services.wm.getMostRecentWindow('navigator:browser');
}
var ps = Services.prompt;
var nsIFilePicker = Components.interfaces.nsIFilePicker;
while (true) {
var fp = Components.classes["@mozilla.org/filepicker;1"]
.createInstance(nsIFilePicker);
fp.init(win, Zotero.getString('dataDir.selectNewDir', Zotero.clientName), nsIFilePicker.modeGetFolder);
fp.displayDirectory = Zotero.File.pathToFile(this.dir);
fp.appendFilters(nsIFilePicker.filterAll);
if (fp.show() == nsIFilePicker.returnOK) {
var file = fp.file;
let fp = new FilePicker();
fp.init(win, Zotero.getString('dataDir.selectNewDir', Zotero.clientName), fp.modeGetFolder);
fp.displayDirectory = this.dir;
fp.appendFilters(fp.filterAll);
if (await fp.show() == fp.returnOK) {
let file = Zotero.File.pathToFile(fp.file);
if (file.directoryEntries.hasMoreElements()) {
ps.alert(null,
@ -1012,21 +1008,13 @@ Zotero.DataDirectory = {
// Focus the first file/folder in the old directory
else if (index == 2) {
try {
let it = new OS.File.DirectoryIterator(oldDir);
let entry;
try {
entry = yield it.next();
}
catch (e) {
if (e != StopIteration) {
throw e;
}
}
finally {
it.close();
}
if (entry) {
yield Zotero.File.reveal(entry.path);
let firstEntry;
yield Zotero.File.iterateDirectory(oldDir, function (entry, index, iterator) {
firstEntry = entry;
iterator.close();
});
if (firstEntry) {
yield Zotero.File.reveal(firstEntry.path);
}
// Focus the database file in the new directory
yield Zotero.File.reveal(OS.Path.join(newDir, this.getDatabaseFilename()));

View file

@ -586,22 +586,14 @@ Zotero.DBConnection.prototype.queryAsync = Zotero.Promise.coroutine(function* (s
}
var failed = false;
if (options && options.onRow) {
// Errors in onRow don't stop the query unless StopIteration is thrown
onRow = function (row) {
// Errors in onRow don't stop the query unless the 'cancel' function is called
onRow = function (row, cancel) {
try {
options.onRow(row);
options.onRow(row, cancel);
}
catch (e) {
// If the onRow throws a StopIteration, stop gracefully
if (e instanceof StopIteration) {
Zotero.debug("Query cancelled", 3);
}
// Otherwise, mark the promise as rejected, which Sqlite.jsm doesn't do
// on a StopIteration by default
else {
failed = e;
}
throw StopIteration;
failed = e;
cancel();
}
}
}
@ -659,7 +651,7 @@ Zotero.DBConnection.prototype.queryAsync = Zotero.Promise.coroutine(function* (s
}
else {
// lastInsertRowID is unreliable for async queries, so we don't bother
// returning it for SELECT and REPLACE queries
// returning it for INSERT and REPLACE queries
return;
}
}
@ -767,7 +759,7 @@ Zotero.DBConnection.prototype.columnQueryAsync = Zotero.Promise.coroutine(functi
Zotero.DBConnection.prototype.logQuery = function (sql, params = [], options) {
if (options && options.debug === false) return;
var msg = sql;
if (params.length) {
if (params.length && (!options || options.debugParams !== false)) {
msg += " [";
for (let i = 0; i < params.length; i++) {
let param = params[i];

View file

@ -270,6 +270,9 @@ Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function*
yearCache[row.itemID] = row.year;
}
var itemTypeAttachment = Zotero.ItemTypes.getID('attachment');
var itemTypeNote = Zotero.ItemTypes.getID('note');
// Match on normalized title
var titleIDs = Zotero.ItemFields.getTypeFieldsFromBase('title');
titleIDs.push(Zotero.ItemFields.getID('title'));
@ -277,7 +280,7 @@ Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function*
+ "JOIN itemDataValues USING (valueID) "
+ "WHERE libraryID=? AND fieldID IN "
+ "(" + titleIDs.join(', ') + ") "
+ "AND itemTypeID NOT IN (1, 14) "
+ `AND itemTypeID NOT IN (${itemTypeAttachment}, ${itemTypeNote}) `
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems)";
var rows = yield Zotero.DB.queryAsync(sql, [this._libraryID]);
if (rows.length) {
@ -299,7 +302,7 @@ Zotero.Duplicates.prototype._findDuplicates = Zotero.Promise.coroutine(function*
let sql = "SELECT itemID, lastName, firstName, fieldMode FROM items "
+ "JOIN itemCreators USING (itemID) "
+ "JOIN creators USING (creatorID) "
+ "WHERE libraryID=? AND itemTypeID NOT IN (1, 14) AND "
+ `WHERE libraryID=? AND itemTypeID NOT IN (${itemTypeAttachment}, ${itemTypeNote}) AND `
+ "itemID NOT IN (SELECT itemID FROM deletedItems)"
+ "ORDER BY itemID, orderIndex";
let creatorRows = yield Zotero.DB.queryAsync(sql, this._libraryID);

View file

@ -333,8 +333,7 @@ Zotero.File = new function(){
* Runs synchronously, so should only be run on local (e.g. chrome) URLs
*/
function getContentsFromURL(url) {
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', url, false);
xmlhttp.overrideMimeType("text/plain");
xmlhttp.send(null);
@ -450,7 +449,7 @@ Zotero.File = new function(){
this.download = Zotero.Promise.coroutine(function* (uri, path) {
Zotero.debug("Saving " + (uri.spec ? uri.spec : uri)
+ " to " + (path.path ? path.path : path));
+ " to " + (path.pathQueryRef ? path.pathQueryRef : path));
var deferred = Zotero.Promise.defer();
NetUtil.asyncFetch(uri, function (is, status, request) {
@ -563,51 +562,39 @@ Zotero.File = new function(){
/**
* @return {Promise<Boolean>}
*/
this.directoryIsEmpty = Zotero.Promise.coroutine(function* (path) {
var it = new OS.File.DirectoryIterator(path);
this.directoryIsEmpty = async function (path) {
var iterator = new OS.File.DirectoryIterator(path);
var empty = true;
try {
let entry = yield it.next();
return false;
}
catch (e) {
if (e != StopIteration) {
throw e;
}
await iterator.forEach(() => {
iterator.close();
empty = false;
});
}
finally {
it.close();
iterator.close();
}
return true;
});
return empty;
};
/**
* Run a generator with an OS.File.DirectoryIterator, closing the
* iterator when done. Promises yielded by the generator are awaited.
* Run a function on each entry in a directory
*
* The DirectoryIterator is passed as the first parameter to the generator.
* 'entry' is an instance of OS.File.DirectoryIterator.Entry:
*
* Zotero.File.iterateDirectory(path, function* (iterator) {
* while (true) {
* let entry = yield iterator.next();
* let contents = yield Zotero.File.getContentsAsync(entry.path);
* [...]
* }
* })
* https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/OSFile.jsm/OS.File.DirectoryIterator.Entry
*
* @return {Promise}
*/
this.iterateDirectory = function (path, generator) {
this.iterateDirectory = async function (path, onEntry) {
var iterator = new OS.File.DirectoryIterator(path);
return Zotero.Promise.coroutine(generator)(iterator)
.catch(function (e) {
if (e != StopIteration) {
throw e;
}
})
.finally(function () {
try {
await iterator.forEach(onEntry);
}
finally {
iterator.close();
});
}
}
@ -682,85 +669,82 @@ Zotero.File = new function(){
Zotero.debug("Moving files in " + oldDir);
yield Zotero.File.iterateDirectory(oldDir, function* (iterator) {
while (true) {
let entry = yield iterator.next();
let dest = newDir + entry.path.substr(rootDir.length);
// entry.isDir can be false for some reason on Travis, causing spurious test failures
if (Zotero.automatedTest && !entry.isDir && (yield OS.File.stat(entry.path)).isDir) {
Zotero.debug("Overriding isDir for " + entry.path);
entry.isDir = true;
yield Zotero.File.iterateDirectory(oldDir, async function (entry) {
var dest = newDir + entry.path.substr(rootDir.length);
// entry.isDir can be false for some reason on Travis, causing spurious test failures
if (Zotero.automatedTest && !entry.isDir && (await OS.File.stat(entry.path)).isDir) {
Zotero.debug("Overriding isDir for " + entry.path);
entry.isDir = true;
}
// Move files in directory
if (!entry.isDir) {
try {
await OS.File.move(
entry.path,
dest,
{
noOverwrite: options
&& options.noOverwrite
&& options.noOverwrite(entry.path)
}
);
}
catch (e) {
checkError(e);
Zotero.debug("Error moving " + entry.path);
addError(e);
}
}
else {
// Move directory with external command if possible and the directory doesn't
// already exist in target
let moved = false;
// Move files in directory
if (!entry.isDir) {
if (useCmd && !(await OS.File.exists(dest))) {
Zotero.debug(`Moving ${entry.path} with ${cmd}`);
let args = [entry.path, dest];
try {
yield OS.File.move(
entry.path,
dest,
{
noOverwrite: options
&& options.noOverwrite
&& options.noOverwrite(entry.path)
}
);
await Zotero.Utilities.Internal.exec(cmd, args);
moved = true;
}
catch (e) {
checkError(e);
Zotero.debug("Error moving " + entry.path);
addError(e);
Zotero.debug(e, 1);
}
}
else {
// Move directory with external command if possible and the directory doesn't
// already exist in target
let moved = false;
if (useCmd && !(yield OS.File.exists(dest))) {
Zotero.debug(`Moving ${entry.path} with ${cmd}`);
let args = [entry.path, dest];
try {
yield Zotero.Utilities.Internal.exec(cmd, args);
moved = true;
}
catch (e) {
checkError(e);
Zotero.debug(e, 1);
}
// If can't use command, try moving with OS.File.move(). Technically this is
// unsupported for directories, but it works on all platforms as long as noCopy
// is set (and on some platforms regardless)
if (!moved && useFunction) {
Zotero.debug(`Moving ${entry.path} with OS.File`);
try {
await OS.File.move(
entry.path,
dest,
{
noCopy: true
}
);
moved = true;
}
// If can't use command, try moving with OS.File.move(). Technically this is
// unsupported for directories, but it works on all platforms as long as noCopy
// is set (and on some platforms regardless)
if (!moved && useFunction) {
Zotero.debug(`Moving ${entry.path} with OS.File`);
try {
yield OS.File.move(
entry.path,
dest,
{
noCopy: true
}
);
moved = true;
}
catch (e) {
checkError(e);
Zotero.debug(e, 1);
}
catch (e) {
checkError(e);
Zotero.debug(e, 1);
}
// Otherwise, recurse into subdirectories to copy files individually
if (!moved) {
try {
yield moveSubdirs(entry.path, depth - 1);
}
catch (e) {
checkError(e);
addError(e);
}
}
// Otherwise, recurse into subdirectories to copy files individually
if (!moved) {
try {
await moveSubdirs(entry.path, depth - 1);
}
catch (e) {
checkError(e);
addError(e);
}
}
}
@ -984,11 +968,8 @@ Zotero.File = new function(){
unixMode: 0o755
});
return this.iterateDirectory(source, function* (iterator) {
while (true) {
let entry = yield iterator.next();
yield OS.File.copy(entry.path, OS.Path.join(target, entry.name));
}
return this.iterateDirectory(source, function (entry) {
return OS.File.copy(entry.path, OS.Path.join(target, entry.name));
})
});
@ -1338,7 +1319,6 @@ Zotero.File = new function(){
dialogButtonText: Zotero.getString('file.accessError.showParentDir'),
dialogButtonCallback: function () {
try {
file.parent.QueryInterface(Components.interfaces.nsILocalFile);
file.parent.reveal();
}
// Unsupported on some platforms
@ -1372,13 +1352,12 @@ Zotero.File = new function(){
Zotero.debug("Revealing " + file);
var nsIFile = this.pathToFile(file);
nsIFile.QueryInterface(Components.interfaces.nsILocalFile);
try {
nsIFile.reveal();
}
catch (e) {
Zotero.logError(e);
// On platforms that don't support nsILocalFile.reveal() (e.g. Linux),
// On platforms that don't support nsIFile.reveal() (e.g. Linux),
// launch the directory
let zp = Zotero.getActiveZoteroPane();
if (zp) {

View file

@ -226,8 +226,7 @@ Zotero.HTTP = new function() {
if (options.username) {
options.username = options.username.replace(/%2E/, '.');
options.password = url.password || null;
url = url.clone();
url.userPass = '';
url = url.mutate().setUserPass('').finalize();
}
url = url.spec;
@ -566,8 +565,7 @@ Zotero.HTTP = new function() {
return false;
}
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
var xmlhttp = new XMLHttpRequest();
// Prevent certificate/authentication dialogs from popping up
xmlhttp.mozBackgroundRequest = true;
@ -640,8 +638,7 @@ Zotero.HTTP = new function() {
return false;
}
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
var xmlhttp = new XMLHttpRequest();
// Prevent certificate/authentication dialogs from popping up
xmlhttp.mozBackgroundRequest = true;
xmlhttp.open('POST', url, true);
@ -718,8 +715,7 @@ Zotero.HTTP = new function() {
// Workaround for "Accept third-party cookies" being off in Firefox 3.0.1
// https://www.zotero.org/trac/ticket/1070
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
var xmlhttp = new XMLHttpRequest();
// Prevent certificate/authentication dialogs from popping up
xmlhttp.mozBackgroundRequest = true;
xmlhttp.open('HEAD', url, true);
@ -766,8 +762,7 @@ Zotero.HTTP = new function() {
return false;
}
var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance();
var xmlhttp = new XMLHttpRequest();
// Prevent certificate/authentication dialogs from popping up
xmlhttp.mozBackgroundRequest = true;
xmlhttp.open('OPTIONS', uri.spec, true);

View file

@ -27,9 +27,12 @@ Zotero.ID_Tracker = function () {
var _tables = [
'collections',
'creators',
'creatorTypes',
'customFields',
'customItemTypes',
'fields',
'itemDataValues',
'itemTypes',
'items',
'libraries',
'proxies',
@ -54,7 +57,7 @@ Zotero.ID_Tracker = function () {
throw new Error("IDs not loaded for table '" + table + "'");
}
return ++_nextIDs[table];
return _nextIDs[table]++;
};

View file

@ -83,9 +83,7 @@ Zotero.Integration = new function() {
// on OS X, first try /Users/Shared for those who can't put pipes in their home
// directories
var pipe = null;
var sharedDir = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
sharedDir.initWithPath("/Users/Shared");
var sharedDir = Zotero.File.pathToFile('/Users/Shared');
if(sharedDir.exists() && sharedDir.isDirectory()) {
var logname = Components.classes["@mozilla.org/process/environment;1"].

View file

@ -229,7 +229,7 @@ Zotero.IPC = new function() {
if(pipeDir.exists()) {
var dirEntries = pipeDir.directoryEntries;
while (dirEntries.hasMoreElements()) {
var pipe = dirEntries.getNext().QueryInterface(Ci.nsILocalFile);
var pipe = dirEntries.getNext().QueryInterface(Ci.nsIFile);
if(pipe.leafName[0] !== "." && (!_instancePipe || !pipe.equals(_instancePipe))) {
pipes.push(pipe);
}
@ -461,7 +461,12 @@ Zotero.IPC.Pipe.DeferredOpen.prototype = {
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(fifoStream, -1, -1, 4096, 1, true);
try {
pump.init(fifoStream, 0, 0, true);
}
catch (e) {
pump.init(fifoStream, -1, -1, 4096, 1, true);
}
pump.asyncRead(this, null);
this._openTime = Date.now();

View file

@ -890,11 +890,14 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
else if (ids.length <= 5) {
var items = Zotero.Items.get(ids);
if (items) {
let itemTypeAttachment = Zotero.ItemTypes.getID('attachment');
let itemTypeNote = Zotero.ItemTypes.getID('note');
var found = false;
for (let item of items) {
// Check for note and attachment type, since it's quicker
// Check for attachment and note types, since it's quicker
// than checking for parent item
if (item.itemTypeID == 1 || item.itemTypeID == 14) {
if (item.itemTypeID == itemTypeAttachment || item.itemTypeID == itemTypeNote) {
continue;
}
@ -2847,7 +2850,7 @@ Zotero.ItemTreeView.fileDragDataProvider.prototype = {
var dirPrimitive = {};
var dataSize = {};
transferable.getTransferData("application/x-moz-file-promise-dir", dirPrimitive, dataSize);
var destDir = dirPrimitive.value.QueryInterface(Components.interfaces.nsILocalFile);
var destDir = dirPrimitive.value.QueryInterface(Components.interfaces.nsIFile);
var draggedItems = Zotero.Items.get(this._itemIDs);
var items = [];
@ -2901,7 +2904,7 @@ Zotero.ItemTreeView.fileDragDataProvider.prototype = {
var numFiles = 0;
while (files.hasMoreElements()) {
var f = files.getNext();
f.QueryInterface(Components.interfaces.nsILocalFile);
f.QueryInterface(Components.interfaces.nsIFile);
if (f.leafName.indexOf('.') != 0) {
numFiles++;
}

View file

@ -277,17 +277,14 @@ Zotero.LocateManager = new function() {
* @param {Object} [obj] The locate engine, in parsed form, as it was serialized to JSON
*/
var LocateEngine = function(obj) {
this.alias = this.name = "Untitled";
this.description = this._urlTemplate = this.icon = null;
this.hidden = false;
this._alias = this._name = "Untitled";
this._description = null;
this._icon = null;
this._hidden = false;
this._urlTemplate = null;
this._urlParams = [];
if(obj) for(var prop in obj) this[prop] = obj[prop];
// Queue deferred serialization whenever a property is modified
for (let prop of ["alias", "name", "description", "icon", "hidden"]) {
this.watch(prop, _watchLocateEngineProperties);
}
}
LocateEngine.prototype = {
@ -488,4 +485,19 @@ Zotero.LocateManager = new function() {
this.icon = OS.Path.toFileURI(iconFile);
}
}
// Queue deferred serialization whenever a property is modified
for (let prop of ["alias", "name", "description", "icon", "hidden"]) {
let propName = '_' + prop;
Object.defineProperty(LocateEngine.prototype, prop, {
get: function () {
return this[propName];
},
set: function (val) {
var oldVal = this[propName];
this[propName] = val;
_watchLocateEngineProperties(prop, oldVal, val);
}
});
}
}

View file

@ -47,7 +47,7 @@ Zotero.OpenURL = new function() {
* Queries OCLC's OpenURL resolver registry and returns an address and version
*/
function discoverResolvers() {
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
var req = new XMLHttpRequest();
req.open("GET", "http://worldcatlibraries.org/registry/lookup?IP=requestor", false);
req.send(null);

View file

@ -24,7 +24,6 @@
*/
Zotero.Prefs = new function(){
// Privileged methods
this.init = init;
this.get = get;
this.set = set;
@ -32,11 +31,10 @@ Zotero.Prefs = new function(){
this.unregister = unregister;
this.observe = observe;
// Public properties
this.prefBranch;
this.rootBranch = Services.prefs.getBranch("");
function init(){
this.prefBranch = Services.prefs.getBranch(ZOTERO_CONFIG.PREF_BRANCH);
this.init = async function init() {
await loadExtensionDefaults();
// Register observer to handle pref changes
this.register();
@ -87,24 +85,39 @@ Zotero.Prefs = new function(){
**/
function get(pref, global){
try {
if (global) {
var branch = Services.prefs.getBranch("");
}
else {
var branch = this.prefBranch;
}
pref = global ? pref : ZOTERO_CONFIG.PREF_BRANCH + pref;
let branch = this.rootBranch;
let value;
switch (branch.getPrefType(pref)){
case branch.PREF_BOOL:
return branch.getBoolPref(pref);
value = branch.getBoolPref(pref);
break;
case branch.PREF_STRING:
return '' + branch.getComplexValue(pref, Components.interfaces.nsISupportsString);
// Pre-Fx59
if (!branch.getStringPref) {
value = '' + branch.getComplexValue(pref, Components.interfaces.nsISupportsString);
}
else {
value = branch.getStringPref(pref);
}
break;
case branch.PREF_INT:
return branch.getIntPref(pref);
value = branch.getIntPref(pref);
break;
}
return value;
}
catch (e){
throw new Error("Invalid preference '" + pref + "'");
catch (e) {
// If debug system isn't yet initialized, log proper error
if (Zotero.Debug.enabled === undefined) {
dump(e + "\n\n");
}
Zotero.logError(e);
throw new Error(`Error getting preference '${pref}'`);
}
}
@ -114,21 +127,21 @@ Zotero.Prefs = new function(){
**/
function set(pref, value, global) {
try {
if (global) {
var branch = Services.prefs.getBranch("");
}
else {
var branch = this.prefBranch;
}
pref = global ? pref : ZOTERO_CONFIG.PREF_BRANCH + pref;
let branch = this.rootBranch;
switch (branch.getPrefType(pref)) {
case branch.PREF_BOOL:
return branch.setBoolPref(pref, value);
case branch.PREF_STRING:
let str = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
str.data = value;
return branch.setComplexValue(pref, Ci.nsISupportsString, str);
// Pre-Fx59
if (!branch.setStringPref) {
let str = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
str.data = value;
return branch.setComplexValue(pref, Ci.nsISupportsString, str);
}
return branch.setStringPref(pref, value);
case branch.PREF_INT:
return branch.setIntPref(pref, value);
@ -140,7 +153,14 @@ Zotero.Prefs = new function(){
}
if (typeof value == 'string') {
Zotero.debug("Creating string pref '" + pref + "'");
return branch.setCharPref(pref, value);
// Pre-Fx59
if (!branch.setStringPref) {
let str = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
str.data = value;
return branch.setComplexValue(pref, Ci.nsISupportsString, str);
}
return branch.setStringPref(pref, value);
}
if (parseInt(value) == value) {
Zotero.debug("Creating integer pref '" + pref + "'");
@ -150,32 +170,36 @@ Zotero.Prefs = new function(){
}
}
catch (e) {
// If debug system isn't yet initialized, log proper error
if (Zotero.Debug.enabled === undefined) {
dump(e + "\n\n");
}
Zotero.logError(e);
throw new Error("Invalid preference '" + pref + "'");
throw new Error(`Error setting preference '${pref}'`);
}
}
this.clear = function (pref, global) {
if (global) {
var branch = Services.prefs.getBranch("");
}
else {
var branch = this.prefBranch;
}
branch.clearUserPref(pref);
pref = global ? pref : ZOTERO_CONFIG.PREF_BRANCH + pref;
this.rootBranch.clearUserPref(pref);
}
this.resetBranch = function (exclude = []) {
var keys = this.prefBranch.getChildList("", {});
/**
* @param {String[]} [exclude]
* @param {String} [branch] - Name of pref branch, ending with a period
*/
this.resetBranch = function (exclude = [], branch) {
var branch = Services.prefs.getBranch(branch || ZOTERO_CONFIG.PREF_BRANCH);
var keys = branch.getChildList("", {});
for (let key of keys) {
if (this.prefBranch.prefHasUserValue(key)) {
if (branch.prefHasUserValue(key)) {
if (exclude.includes(key)) {
continue;
}
Zotero.debug("Clearing " + key);
this.prefBranch.clearUserPref(key);
branch.clearUserPref(key);
}
}
};
@ -236,7 +260,7 @@ Zotero.Prefs = new function(){
// Methods to register a preferences observer
//
function register(){
this.prefBranch.addObserver("", this, false);
this.rootBranch.addObserver("", this, false);
// Register pre-set handlers
for (var i=0; i<_handlers.length; i++) {
@ -245,10 +269,10 @@ Zotero.Prefs = new function(){
}
function unregister(){
if (!this.prefBranch){
if (!this.rootBranch){
return;
}
this.prefBranch.removeObserver("", this);
this.rootBranch.removeObserver("", this);
}
/**
@ -264,7 +288,7 @@ Zotero.Prefs = new function(){
var obs = _observers[data];
for (var i=0; i<obs.length; i++) {
try {
obs[i](this.get(data));
obs[i](this.get(data, true));
}
catch (e) {
Zotero.debug("Error while executing preference observer handler for " + data);
@ -277,11 +301,14 @@ Zotero.Prefs = new function(){
var _observersBySymbol = {};
/**
* @param {String} name - Preference name on extensions.zotero branch
* @param {String} name - Preference name; if not global, this is on the extensions.zotero branch
* @param {Function} handler
* @param {Boolean} [global]
* @return {Symbol} - Symbol to pass to unregisterObserver()
*/
this.registerObserver = function (name, handler) {
this.registerObserver = function (name, handler, global) {
name = global ? name : ZOTERO_CONFIG.PREF_BRANCH + name;
var symbol = Symbol();
_observers[name] = _observers[name] || [];
_observers[name].push(handler);
@ -309,4 +336,108 @@ Zotero.Prefs = new function(){
}
obs.splice(i, 1);
}
/**
* Firefox 60 no longer loads default preferences for extensions, so do it manually
*/
async function loadExtensionDefaults() {
var defaultBranch = Services.prefs.getDefaultBranch("");
return new Zotero.Promise(function (resolve) {
Cu.import("resource://gre/modules/AddonManager.jsm");
// Lines are in format `pref("[key]", [val]);`, so define a function to be called that
// sets the defaults
function pref(key, value) {
switch (typeof value) {
case "boolean":
defaultBranch.setBoolPref(key, value);
break;
case "number":
defaultBranch.setIntPref(key, value);
break;
case "string":
defaultBranch.setStringPref(key, value);
break;
}
}
function readDefaults(contents) {
let re = /^\s*pref\s*\(\s*['"]([a-zA-Z0-9_\-.]+)['"]\s*,\s*["']?.*["']?\s*\)\s*;\s*$/;
let lines = contents.split(/\n/g).filter(line => re.test(line));
for (let line of lines) {
try {
eval(line);
}
catch (e) {
dump(e + "\n\n");
Components.utils.reportError(e);
}
}
}
AddonManager.getAllAddons(async function(addons) {
var reusableStreamInstance = Cc['@mozilla.org/scriptableinputstream;1']
.createInstance(Ci.nsIScriptableInputStream);
for (let addon of addons) {
if (!addon.isActive) {
continue;
}
try {
let path = OS.Path.fromFileURI(addon.getResourceURI().spec);
// Directory
if ((await OS.File.stat(path)).isDir) {
let dir = OS.Path.join(path, 'defaults', 'preferences');
if (await OS.File.exists(dir)) {
await Zotero.File.iterateDirectory(dir, async function (entry) {
if (!entry.name.endsWith('.js')) return;
readDefaults(Zotero.File.getContents(entry.path));
});
}
}
// XPI
else {
let file = Zotero.File.pathToFile(path);
let zipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"].
createInstance(Components.interfaces.nsIZipReader);
try {
try {
zipReader.open(file);
zipReader.test(null);
}
catch (e) {
Zotero.logError(path + " is not a valid ZIP file");
continue;
}
let entries = zipReader.findEntries('defaults/preferences/*.js');
while (entries.hasMore()) {
let entryName = entries.getNext();
let entry = zipReader.getEntry(entryName);
if (!entry.isDirectory) {
let inputStream = zipReader.getInputStream(entryName);
reusableStreamInstance.init(inputStream);
readDefaults(reusableStreamInstance.read(entry.realSize));
}
}
}
finally {
zipReader.close();
}
}
}
catch (e) {
Zotero.logError(e);
}
}
resolve();
});
});
}
}

View file

@ -283,17 +283,14 @@ Zotero.Profile = {
*/
_getProfilesInDir: Zotero.Promise.coroutine(function* (profilesDir) {
var dirs = [];
yield Zotero.File.iterateDirectory(profilesDir, function* (iterator) {
while (true) {
let entry = yield iterator.next();
// entry.isDir can be false for some reason on Travis, causing spurious test failures
if (Zotero.automatedTest && !entry.isDir && (yield OS.File.stat(entry.path)).isDir) {
Zotero.debug("Overriding isDir for " + entry.path);
entry.isDir = true;
}
if (entry.isDir && (yield OS.File.exists(OS.Path.join(entry.path, "prefs.js")))) {
dirs.push(entry.path);
}
yield Zotero.File.iterateDirectory(profilesDir, async function (entry) {
// entry.isDir can be false for some reason on Travis, causing spurious test failures
if (Zotero.automatedTest && !entry.isDir && (await OS.File.stat(entry.path)).isDir) {
Zotero.debug("Overriding isDir for " + entry.path);
entry.isDir = true;
}
if (entry.isDir && (await OS.File.exists(OS.Path.join(entry.path, "prefs.js")))) {
dirs.push(entry.path);
}
});
return dirs;

View file

@ -248,10 +248,6 @@ Zotero.ProgressQueueDialog = function (progressQueue) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
if (win) {
if (win.ZoteroOverlay) {
win.ZoteroOverlay.toggleDisplay(true);
}
win.ZoteroPane.selectItem(itemID, false, true);
win.focus();
}

View file

@ -28,7 +28,6 @@ Zotero.ProgressWindowSet = new function() {
this.add = add;
this.tile = tile;
this.remove = remove;
this.updateTimers = updateTimers;
var _progressWindows = [];
@ -87,19 +86,6 @@ Zotero.ProgressWindowSet = new function() {
}
function updateTimers() {
if (!_progressWindows.length) {
return;
}
for (var i=0; i<_progressWindows.length; i++) {
// Pass |requireMouseOver| so that the window only closes
// if the mouse was over it at some point
_progressWindows[i].instance.startCloseTimer(null, true);
}
}
this.closeAll = function () {
_progressWindows.forEach(pw => pw.instance.close());
}

View file

@ -509,11 +509,11 @@ Zotero.Proxy.prototype.toProxy = function(uri) {
if(param == "%h") {
value = this.dotsToHyphens ? uri.hostPort.replace(/-/g, '.') : uri.hostPort;
} else if(param == "%p") {
value = uri.path.substr(1);
value = uri.pathQueryRef.substr(1);
} else if(param == "%d") {
value = uri.path.substr(0, uri.path.lastIndexOf("/"));
value = uri.pathQueryRef.substr(0, uri.pathQueryRef.lastIndexOf("/"));
} else if(param == "%f") {
value = uri.path.substr(uri.path.lastIndexOf("/")+1)
value = uri.pathQueryRef.substr(uri.pathQueryRef.lastIndexOf("/")+1)
}
proxyURL = proxyURL.substr(0, this.indices[param])+value+proxyURL.substr(this.indices[param]+2);

View file

@ -172,7 +172,7 @@ Zotero.QuickCopy = new function() {
// Accessing some properties may throw for URIs that do not support those
// parts. E.g. hostPort throws NS_ERROR_FAILURE for about:blank
var urlHostPort = nsIURI.hostPort;
var urlPath = nsIURI.path;
var urlPath = nsIURI.pathQueryRef;
}
catch (e) {}

View file

@ -36,9 +36,9 @@ Zotero.Report.HTML = new function () {
+ ' <head>\n'
+ ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n'
+ ' <title>' + Zotero.getString('report.title.default') + '</title>\n'
+ ' <link rel="stylesheet" type="text/css" href="zotero://report/detail.css"/>\n'
+ ' <link rel="stylesheet" type="text/css" media="screen,projection" href="zotero://report/detail_screen.css"/>\n'
+ ' <link rel="stylesheet" type="text/css" media="print" href="zotero://report/detail_print.css"/>\n'
+ ' <link rel="stylesheet" type="text/css" href="' + _getCSSDataURI('detail') + '"/>\n'
+ ' <link rel="stylesheet" type="text/css" media="screen,projection" href="' + _getCSSDataURI('detail_screen') + '"/>\n'
+ ' <link rel="stylesheet" type="text/css" media="print" href="' + _getCSSDataURI('detail_print') + '"/>\n'
+ ' </head>\n'
+ ' <body>\n'
+ ' <ul class="report' + (combineChildItems ? ' combineChildItems' : '') + '">';
@ -130,6 +130,14 @@ Zotero.Report.HTML = new function () {
};
function _getCSSDataURI(file) {
return 'data:text/css;base64,'
+ Zotero.Utilities.Internal.Base64.encode(
Zotero.File.getResource(`chrome://zotero/skin/report/${file}.css`)
);
}
function _generateMetadataTable(obj) {
var table = false;
var content = '\t\t\t\t<table>\n';
@ -200,7 +208,7 @@ Zotero.Report.HTML = new function () {
}
try {
var localizedFieldName = Zotero.ItemFields.getLocalizedString(obj.itemType, i);
var localizedFieldName = Zotero.ItemFields.getLocalizedString(i);
}
// Skip fields we don't have a localized string for
catch (e) {

View file

@ -181,7 +181,7 @@ Zotero.Retractions = {
doi = json.DOI;
}
else if (json.extra) {
let fields = Zotero.Utilities.Internal.extractExtraFields(json.extra);
let { fields } = Zotero.Utilities.Internal.extractExtraFields(json.extra);
let extraField = fields.get('DOI');
if (extraField && extraField.value) {
doi = extraField.value;
@ -765,7 +765,7 @@ Zotero.Retractions = {
+ "JOIN itemDataValues USING (valueID) WHERE fieldID=?";
rows = await Zotero.DB.queryAsync(sql, Zotero.ItemFields.getID('extra'));
for (let row of rows) {
let fields = Zotero.Utilities.Internal.extractExtraFields(row.value);
let { fields } = Zotero.Utilities.Internal.extractExtraFields(row.value);
let doi = fields.get('doi');
if (!doi || !doi.value) continue;
let value = Zotero.Utilities.cleanDOI(doi.value);
@ -783,7 +783,7 @@ Zotero.Retractions = {
var rows = await Zotero.DB.queryAsync(sql, Zotero.ItemFields.getID('extra'));
for (let row of rows) {
/*
let fields = Zotero.Utilities.Internal.extractExtraFields(row.value);
let { fields } = Zotero.Utilities.Internal.extractExtraFields(row.value);
let pmid = fields.get('pmid') || fields.get('pubmedID');
if (!pmid || !pmid.value) continue;
this._addItemKeyMapping(this.TYPE_PMID, pmid.value, row.id);

View file

@ -1,4 +1,4 @@
Components.utils.import("resource://zotero/pathparser.js", Zotero);
Components.utils.import("resource://zotero/pathparser.jsm", Zotero);
Zotero.Router = Zotero.PathParser;
delete Zotero.PathParser;

View file

@ -41,12 +41,12 @@ Zotero.Schema = new function(){
// If updating from this userdata version or later, don't show "Upgrading database…" and don't make
// DB backup first. This should be set to false when breaking compatibility or making major changes.
const minorUpdateFrom = 95;
const minorUpdateFrom = false;
var _dbVersions = [];
var _schemaVersions = [];
// Update when adding _updateCompatibility() line to schema update step
var _maxCompatibility = 5;
var _maxCompatibility = 6;
var _repositoryTimerID;
var _repositoryNotificationTimerID;
@ -88,7 +88,7 @@ Zotero.Schema = new function(){
/*
* Checks if the DB schema exists and is up-to-date, updating if necessary
*/
this.updateSchema = Zotero.Promise.coroutine(function* (options = {}) {
this.updateSchema = async function (options = {}) {
// TODO: Check database integrity first with Zotero.DB.integrityCheck()
// 'userdata' is the last upgrade step run in _migrateUserDataSchema() based on the
@ -96,7 +96,7 @@ Zotero.Schema = new function(){
//
// 'compatibility' is incremented manually by upgrade steps in order to break DB
// compatibility with older versions.
var versions = yield Zotero.Promise.all([
var versions = await Zotero.Promise.all([
this.getDBVersion('userdata'), this.getDBVersion('compatibility')
]);
var [userdata, compatibility] = versions;
@ -129,7 +129,7 @@ Zotero.Schema = new function(){
}
if (compatibility > _maxCompatibility) {
let dbClientVersion = yield Zotero.DB.valueQueryAsync(
let dbClientVersion = await Zotero.DB.valueQueryAsync(
"SELECT value FROM settings "
+ "WHERE setting='client' AND key='lastCompatibleVersion'"
);
@ -139,53 +139,81 @@ Zotero.Schema = new function(){
}
// Check if DB is coming from the DB Repair Tool and should be checked
var integrityCheck = yield Zotero.DB.valueQueryAsync(
var integrityCheck = await Zotero.DB.valueQueryAsync(
"SELECT value FROM settings WHERE setting='db' AND key='integrityCheck'"
);
var schemaVersion = yield _getSchemaSQLVersion('userdata');
// Check whether bundled global schema file is newer than DB
var bundledGlobalSchema = await _readGlobalSchemaFromFile();
var bundledGlobalSchemaVersionCompare = await _globalSchemaVersionCompare(
bundledGlobalSchema.version
);
// Check whether bundled userdata schema has been updated
var userdataVersion = await _getSchemaSQLVersion('userdata');
options.minor = minorUpdateFrom && userdata >= minorUpdateFrom;
// If non-minor userdata upgrade, make backup of database first
if (userdata < schemaVersion && !options.minor) {
yield Zotero.DB.backupDatabase(userdata, true);
if (userdata < userdataVersion && !options.minor) {
await Zotero.DB.backupDatabase(userdata, true);
}
else if (integrityCheck) {
yield Zotero.DB.backupDatabase(false, true);
// Automatic backup
else if (integrityCheck || bundledGlobalSchemaVersionCompare === 1) {
await Zotero.DB.backupDatabase(false, true);
}
yield Zotero.DB.queryAsync("PRAGMA foreign_keys = false");
await Zotero.DB.queryAsync("PRAGMA foreign_keys = false");
try {
var updated = yield Zotero.DB.executeTransaction(function* (conn) {
var updated = yield _updateSchema('system');
// If bundled global schema file is newer than DB, apply it
if (bundledGlobalSchemaVersionCompare === 1) {
await Zotero.DB.executeTransaction(async function () {
await _updateGlobalSchema(bundledGlobalSchema);
});
}
else {
let data;
// If bundled global schema is up to date, use it
if (bundledGlobalSchemaVersionCompare === 0) {
data = bundledGlobalSchema;
}
// If bundled global schema is older than the DB (because of a downgrade), use the
// DB version, which will match the mapping tables
else if (bundledGlobalSchemaVersionCompare === -1) {
data = await _readGlobalSchemaFromDB();
}
await _loadGlobalSchema(data, bundledGlobalSchema.version);
}
var updated = await Zotero.DB.executeTransaction(async function (conn) {
var updated = await _updateSchema('system');
// Update custom tables if they exist so that changes are in
// place before user data migration
if (Zotero.DB.tableExists('customItemTypes')) {
yield _updateCustomTables(updated);
await _updateCustomTables();
}
// Auto-repair databases coming from the DB Repair Tool
if (integrityCheck) {
yield this.integrityCheck(true);
yield Zotero.DB.queryAsync(
await this.integrityCheck(true);
await Zotero.DB.queryAsync(
"DELETE FROM settings WHERE setting='db' AND key='integrityCheck'"
);
}
updated = yield _migrateUserDataSchema(userdata, options);
yield _updateSchema('triggers');
updated = await _migrateUserDataSchema(userdata, options);
await _updateSchema('triggers');
// Populate combined tables for custom types and fields -- this is likely temporary
//
// We do this again in case custom fields were changed during user data migration
yield _updateCustomTables()
await _updateCustomTables();
return updated;
}.bind(this));
}
finally {
yield Zotero.DB.queryAsync("PRAGMA foreign_keys = true");
await Zotero.DB.queryAsync("PRAGMA foreign_keys = true");
}
if (updated) {
@ -220,7 +248,7 @@ Zotero.Schema = new function(){
}
// Reset sync queue tries if new version
yield _checkClientVersion();
await _checkClientVersion();
// In Standalone, don't load bundled files until after UI is ready. In Firefox, load them as
// soon initialization is done so that translation works before the Zotero pane is opened.
@ -273,7 +301,341 @@ Zotero.Schema = new function(){
});
return updated;
});
};
/**
* Get bundled schema from disk
*
* @return {Object}
*/
async function _readGlobalSchemaFromFile() {
return JSON.parse(
await Zotero.File.getResourceAsync('resource://zotero/schema/global/schema.json')
);
}
/**
* Get schema from database
*
* Doesn't include the .itemTypes property, which was already applied to the mapping tables
*/
async function _readGlobalSchemaFromDB() {
var pako = {};
Services.scriptloader.loadSubScript("resource://zotero/pako.js", pako);
var data = await Zotero.DB.valueQueryAsync(
"SELECT value FROM settings WHERE schema='globalSchema' AND key='data'"
);
if (data) {
try {
data = JSON.parse(pako.inflate(data, { to: 'string' }));
}
catch (e) {
Zotero.warn("Unable to extract global schema -- falling back to file: " + e);
}
}
else {
Zotero.warn("Global schema not found in DB -- falling back to file");
}
// If the data is missing or unreadable in the DB for some reason (e.g., DB corruption),
// fall back to the file, though it might be out of date
if (!data) {
data = await _readGlobalSchemaFromFile();
}
return data;
}
/**
* Compares a given version number to the version of the schema in the database
*
* @return {Number} - 1 if provided version is greater than DB (i.e., DB needs update), 0 if
* the same, -1 if DB version is newer
*/
async function _globalSchemaVersionCompare(version) {
if (!version) {
throw new Error("version not specified");
}
var dbVersion = await Zotero.Schema.getDBVersion('globalSchema') || null;
if (dbVersion > version) {
Zotero.debug(`Database has newer global schema (${dbVersion} > ${version}) -- skipping update`);
return -1;
}
else if (dbVersion == version) {
Zotero.debug(`Database is up to date with global schema version ${version} -- skipping update`);
return 0;
}
Zotero.debug(`Global schema needs update from ${dbVersion} to ${version}`);
return 1;
}
/**
* Update the item-type/field/creator mapping tables based on the passed schema
*/
async function _updateGlobalSchema(data) {
Zotero.debug("Updating global schema to version " + data.version);
Zotero.DB.requireTransaction();
await Zotero.ID.init();
var preItemTypeRows = await Zotero.DB.queryAsync(
"SELECT itemTypeID AS id, typeName AS name FROM itemTypes"
);
var preFieldRows = await Zotero.DB.queryAsync(
"SELECT fieldID AS id, fieldName AS name FROM fields"
);
var preCreatorTypeRows = await Zotero.DB.queryAsync(
"SELECT creatorTypeID AS id, creatorType AS name FROM creatorTypes"
);
var preFields = new Set(preFieldRows.map(x => x.name));
var preCreatorTypes = new Set(preCreatorTypeRows.map(x => x.name));
var preItemTypeIDsByName = new Map(preItemTypeRows.map(x => [x.name, x.id]));
var preFieldIDsByName = new Map(preFieldRows.map(x => [x.name, x.id]));
var preCreatorTypeIDsByName = new Map(preCreatorTypeRows.map(x => [x.name, x.id]));
var postFields = new Set();
var postCreatorTypes = new Set();
var postFieldIDsByName = new Map();
var postCreatorTypeIDsByName = new Map();
// Add new fields and creator types
for (let { fields, creatorTypes } of data.itemTypes) {
for (let { field, baseField } of fields) {
postFields.add(field);
if (baseField) {
postFields.add(baseField);
}
}
for (let { creatorType } of creatorTypes) {
postCreatorTypes.add(creatorType);
}
}
var fieldsValueSets = [];
var fieldsParams = [];
for (let field of postFields) {
if (preFields.has(field)) {
postFieldIDsByName.set(field, preFieldIDsByName.get(field));
}
else {
let id = Zotero.ID.get('fields');
fieldsValueSets.push("(?, ?, NULL)");
fieldsParams.push(id, field);
postFieldIDsByName.set(field, id);
}
}
if (fieldsValueSets.length) {
await Zotero.DB.queryAsync(
"INSERT INTO fields VALUES " + fieldsValueSets.join(", "),
fieldsParams
);
}
var creatorTypesValueSets = [];
var creatorTypesParams = [];
for (let type of postCreatorTypes) {
if (preCreatorTypes.has(type)) {
postCreatorTypeIDsByName.set(type, preCreatorTypeIDsByName.get(type));
}
else {
let id = Zotero.ID.get('creatorTypes');
creatorTypesValueSets.push("(?, ?)");
creatorTypesParams.push(id, type);
postCreatorTypeIDsByName.set(type, id);
}
}
if (creatorTypesValueSets.length) {
await Zotero.DB.queryAsync(
"INSERT INTO creatorTypes VALUES " + creatorTypesValueSets.join(", "),
creatorTypesParams
);
}
// Apply changes to DB
let itemTypeFieldsValueSets = [];
let baseFieldMappingsValueSets = [];
let itemTypeCreatorTypesValueSets = [];
for (let { itemType, fields, creatorTypes } of data.itemTypes) {
let itemTypeID = preItemTypeIDsByName.get(itemType);
// let preItemTypeCreatorTypeIDs = [];
if (itemTypeID) {
// Unused
/*preItemTypeCreatorTypeIDs = await Zotero.DB.columnQueryAsync(
"SELECT creatorTypeID FROM itemTypeCreatorTypes WHERE itemTypeID=?",
itemTypeID
);*/
}
// New item type
else {
itemTypeID = Zotero.ID.get('itemTypes');
await Zotero.DB.queryAsync(
"INSERT INTO itemTypes VALUES (?, ?, NULL, 1)",
[itemTypeID, itemType]
);
}
// Fields
let index = 0;
let postItemTypeFieldIDs = new Set();
for (let { field, baseField } of fields) {
let fieldID = postFieldIDsByName.get(field);
postItemTypeFieldIDs.add(fieldID);
itemTypeFieldsValueSets.push(`(${itemTypeID}, ${fieldID}, 0, ${index++})`);
if (baseField) {
let baseFieldID = postFieldIDsByName.get(baseField);
baseFieldMappingsValueSets.push(`(${itemTypeID}, ${baseFieldID}, ${fieldID})`);
}
}
// TODO: Check for fields removed from this item type
// throw new Error(`Field ${id} was removed from ${itemType}`);
// Creator types
for (let { creatorType, primary } of creatorTypes) {
let typeID = postCreatorTypeIDsByName.get(creatorType);
itemTypeCreatorTypesValueSets.push(`(${itemTypeID}, ${typeID}, ${primary ? 1 : 0})`);
}
// TODO: Check for creator types removed from this item type
// throw new Error(`Creator type ${id} was removed from ${itemType}`);
// TODO: Deal with existing types not in the schema, and their items
}
await Zotero.DB.queryAsync("DELETE FROM itemTypeFields");
await Zotero.DB.queryAsync("DELETE FROM baseFieldMappings");
await Zotero.DB.queryAsync("DELETE FROM itemTypeCreatorTypes");
await Zotero.DB.queryAsync("INSERT INTO itemTypeFields VALUES "
+ itemTypeFieldsValueSets.join(", "));
await Zotero.DB.queryAsync("INSERT INTO baseFieldMappings VALUES "
+ baseFieldMappingsValueSets.join(", "));
await Zotero.DB.queryAsync("INSERT INTO itemTypeCreatorTypes VALUES "
+ itemTypeCreatorTypesValueSets.join(", "));
// Store data in DB as compressed binary string. This lets us use a schema that matches the
// DB tables even if the user downgrades to a version with an earlier bundled schema file.
var pako = require('pako');
var dbData = { ...data };
// Don't include types and fields, which are already in the mapping tables
delete dbData.itemTypes;
await Zotero.DB.queryAsync(
"REPLACE INTO settings VALUES ('globalSchema', 'data', ?)",
pako.deflate(JSON.stringify(dbData), { to: 'string' }),
{
debugParams: false
}
);
await _updateDBVersion('globalSchema', data.version);
var bundledVersion = (await _readGlobalSchemaFromFile()).version;
await _loadGlobalSchema(data, bundledVersion);
await _reloadSchema();
// Mark that we need to migrate Extra values to any newly available fields in
// Zotero.Schema.migrateExtraFields()
await Zotero.DB.queryAsync(
"REPLACE INTO settings VALUES ('globalSchema', 'migrateExtra', 1)"
);
return true;
}
this._updateGlobalSchemaForTest = async function (schema) {
await Zotero.DB.executeTransaction(async function () {
await _updateGlobalSchema(schema);
}.bind(this));
};
/**
* Set properties on Zotero.Schema based on the passed data
*
* @param {Object} data - Global schema data ('version', 'itemTypes', 'locales', etc.)
* @param {Number} bundledVersion - Version of the bundled schema.json file
*/
async function _loadGlobalSchema(data, bundledVersion) {
if (!data) {
throw new Error("Data not provided");
}
var locale = data.locales[Zotero.locale];
if (!locale) {
Zotero.warn(`Locale ${Zotero.locale} not found in global schema locales`);
locale = data.locales['en-US'];
if (!locale) {
throw new Error("en-US locale not found in global schema locales");
}
}
Zotero.Schema.globalSchemaVersion = data.version;
Zotero.Schema.globalSchemaLocale = locale;
Zotero.Schema.globalSchemaMeta = data.meta;
Zotero.Schema.CSL_TYPE_MAPPINGS = {};
Zotero.Schema.CSL_TYPE_MAPPINGS_REVERSE = {};
for (let cslType in data.csl.types) {
for (let zoteroType of data.csl.types[cslType]) {
Zotero.Schema.CSL_TYPE_MAPPINGS[zoteroType] = cslType;
}
Zotero.Schema.CSL_TYPE_MAPPINGS_REVERSE[cslType] = data.csl.types[cslType][0];
}
Zotero.Schema.CSL_TEXT_MAPPINGS = data.csl.fields.text;
Zotero.Schema.CSL_DATE_MAPPINGS = data.csl.fields.date;
Zotero.Schema.CSL_NAME_MAPPINGS = data.csl.names;
}
/**
* Migrate values from item Extra fields that can be moved to regular item fields after a global
* schema update
*
* This needs the data object architecture to be initialized, so it's called from zotero.js
* rather than in _updateGlobalSchema().
*/
this.migrateExtraFields = async function () {
// Check for a flag set by _updateGlobalSchema()
var needsUpdate = await Zotero.DB.valueQueryAsync(
"SELECT COUNT(*) FROM settings WHERE setting='globalSchema' AND key='migrateExtra'"
);
if (!needsUpdate) {
return;
}
var fieldID = Zotero.ItemFields.getID('extra');
var sql = "SELECT itemID, value FROM itemData "
+ "JOIN itemDataValues USING (valueID) "
+ "WHERE fieldID=?";
var rows = await Zotero.DB.queryAsync(sql, fieldID);
var itemIDs = [];
for (let row of rows) {
let { itemType, fields, creators } = Zotero.Utilities.Internal.extractExtraFields(
row.value
);
if (itemType || fields.size || creators.length) {
itemIDs.push(row.itemID);
}
}
var items = await Zotero.Items.getAsync(itemIDs);
await Zotero.Items.loadDataTypes(items, ['itemData', 'creator']);
for (let item of items) {
let changed = item.migrateExtraFields();
if (!changed) continue;
await item.saveTx({
skipDateModifiedUpdate: true,
skipSelect: true
});
}
await Zotero.DB.queryAsync(
"DELETE FROM settings WHERE setting='globalSchema' AND key='migrateExtra'"
);
};
// https://www.zotero.org/support/nsf
@ -335,15 +697,15 @@ Zotero.Schema = new function(){
switch (fields[i][0]) {
case 'name':
var baseFieldID = 110; // title
var baseFieldID = Zotero.ItemFields.getID('title');
break;
case 'dateSent':
var baseFieldID = 14; // date
var baseFieldID = Zotero.ItemFields.getID('date');
break;
case 'homepage':
var baseFieldID = 1; // URL
var baseFieldID = Zotero.ItemFields.getID('url');
break;
default:
@ -415,6 +777,7 @@ Zotero.Schema = new function(){
yield _updateCustomTables();
yield Zotero.ItemTypes.init();
yield Zotero.ItemFields.init();
yield Zotero.CreatorTypes.init();
yield Zotero.SearchConditions.init();
// Update item type menus in every open window
@ -429,58 +792,40 @@ Zotero.Schema = new function(){
});
var _updateCustomTables = Zotero.Promise.coroutine(function* (skipDelete, skipSystem) {
var _updateCustomTables = async function () {
Zotero.debug("Updating custom tables");
Zotero.DB.requireTransaction();
if (!skipDelete) {
yield Zotero.DB.queryAsync("DELETE FROM itemTypesCombined");
yield Zotero.DB.queryAsync("DELETE FROM fieldsCombined WHERE fieldID NOT IN (SELECT fieldID FROM itemData)");
yield Zotero.DB.queryAsync("DELETE FROM itemTypeFieldsCombined");
yield Zotero.DB.queryAsync("DELETE FROM baseFieldMappingsCombined");
}
await Zotero.DB.queryAsync("DELETE FROM itemTypesCombined");
await Zotero.DB.queryAsync("DELETE FROM fieldsCombined WHERE fieldID NOT IN (SELECT fieldID FROM itemData)");
await Zotero.DB.queryAsync("DELETE FROM itemTypeFieldsCombined");
await Zotero.DB.queryAsync("DELETE FROM baseFieldMappingsCombined");
var offset = Zotero.ItemTypes.customIDOffset;
yield Zotero.DB.queryAsync(
await Zotero.DB.queryAsync(
"INSERT INTO itemTypesCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION "
)
+ "SELECT itemTypeID, typeName, display, 0 AS custom FROM itemTypes UNION "
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, typeName, display, 1 AS custom FROM customItemTypes"
);
yield Zotero.DB.queryAsync(
await Zotero.DB.queryAsync(
"INSERT OR IGNORE INTO fieldsCombined "
+ (
skipSystem
? ""
: "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION "
)
+ "SELECT fieldID, fieldName, NULL AS label, fieldFormatID, 0 AS custom FROM fields UNION "
+ "SELECT customFieldID + " + offset + " AS fieldID, fieldName, label, NULL, 1 AS custom FROM customFields"
);
yield Zotero.DB.queryAsync(
await Zotero.DB.queryAsync(
"INSERT INTO itemTypeFieldsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION "
)
+ "SELECT itemTypeID, fieldID, hide, orderIndex FROM itemTypeFields UNION "
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, "
+ "COALESCE(fieldID, customFieldID + " + offset + ") AS fieldID, hide, orderIndex FROM customItemTypeFields"
);
yield Zotero.DB.queryAsync(
await Zotero.DB.queryAsync(
"INSERT INTO baseFieldMappingsCombined "
+ (
skipSystem
? ""
: "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION "
)
+ "SELECT itemTypeID, baseFieldID, fieldID FROM baseFieldMappings UNION "
+ "SELECT customItemTypeID + " + offset + " AS itemTypeID, baseFieldID, "
+ "customFieldID + " + offset + " AS fieldID FROM customBaseFieldMappings"
);
});
};
/**
@ -1335,6 +1680,13 @@ Zotero.Schema = new function(){
}
}
var attachmentID = parseInt(yield Zotero.DB.valueQueryAsync(
"SELECT itemTypeID FROM itemTypes WHERE typeName='attachment'"
));
var noteID = parseInt(yield Zotero.DB.valueQueryAsync(
"SELECT itemTypeID FROM itemTypes WHERE typeName='note'"
));
// Non-foreign key checks
//
@ -1348,29 +1700,29 @@ Zotero.Schema = new function(){
"SELECT COUNT(*) > 0 FROM items WHERE itemTypeID IS NULL",
"DELETE FROM items WHERE itemTypeID IS NULL",
],
// Attachments row with itemTypeID != 14
// Non-attachment items in attachments table
[
"SELECT COUNT(*) > 0 FROM itemAttachments JOIN items USING (itemID) WHERE itemTypeID != 14",
"UPDATE items SET itemTypeID=14, clientDateModified=CURRENT_TIMESTAMP WHERE itemTypeID != 14 AND itemID IN (SELECT itemID FROM itemAttachments)",
`SELECT COUNT(*) > 0 FROM itemAttachments JOIN items USING (itemID) WHERE itemTypeID != ${attachmentID}`,
`UPDATE items SET itemTypeID=${attachmentID}, clientDateModified=CURRENT_TIMESTAMP WHERE itemTypeID != ${attachmentID} AND itemID IN (SELECT itemID FROM itemAttachments)`,
],
// Fields not in type
[
"SELECT COUNT(*) > 0 FROM itemData WHERE fieldID NOT IN (SELECT fieldID FROM itemTypeFieldsCombined WHERE itemTypeID=(SELECT itemTypeID FROM items WHERE itemID=itemData.itemID))",
"DELETE FROM itemData WHERE fieldID NOT IN (SELECT fieldID FROM itemTypeFieldsCombined WHERE itemTypeID=(SELECT itemTypeID FROM items WHERE itemID=itemData.itemID))",
],
// Missing itemAttachments row
// Missing itemAttachments rows
[
"SELECT COUNT(*) > 0 FROM items WHERE itemTypeID=14 AND itemID NOT IN (SELECT itemID FROM itemAttachments)",
"INSERT INTO itemAttachments (itemID, linkMode) SELECT itemID, 0 FROM items WHERE itemTypeID=14 AND itemID NOT IN (SELECT itemID FROM itemAttachments)",
`SELECT COUNT(*) > 0 FROM items WHERE itemTypeID=${attachmentID} AND itemID NOT IN (SELECT itemID FROM itemAttachments)`,
`INSERT INTO itemAttachments (itemID, linkMode) SELECT itemID, 0 FROM items WHERE itemTypeID=${attachmentID} AND itemID NOT IN (SELECT itemID FROM itemAttachments)`,
],
// Note/child parents
[
"SELECT COUNT(*) > 0 FROM itemAttachments WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))",
"UPDATE itemAttachments SET parentItemID=NULL WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))",
`SELECT COUNT(*) > 0 FROM itemAttachments WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (${noteID}, ${attachmentID}))`,
`UPDATE itemAttachments SET parentItemID=NULL WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (${noteID}, ${attachmentID}))`,
],
[
"SELECT COUNT(*) > 0 FROM itemNotes WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))",
"UPDATE itemNotes SET parentItemID=NULL WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (1,14))",
`SELECT COUNT(*) > 0 FROM itemNotes WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (${noteID}, ${attachmentID}))`,
`UPDATE itemNotes SET parentItemID=NULL WHERE parentItemID IN (SELECT itemID FROM items WHERE itemTypeID IN (${noteID}, ${attachmentID}))`,
],
// Delete empty creators
@ -1382,13 +1734,13 @@ Zotero.Schema = new function(){
// Non-attachment items in the full-text index
[
"SELECT COUNT(*) > 0 FROM fulltextItemWords WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=14)",
"DELETE FROM fulltextItemWords WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=14)"
`SELECT COUNT(*) > 0 FROM fulltextItemWords WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=${attachmentID})`,
`DELETE FROM fulltextItemWords WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=${attachmentID})`
],
// Full-text items must be attachments
[
"SELECT COUNT(*) > 0 FROM fulltextItems WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=14)",
"DELETE FROM fulltextItems WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=14)"
`SELECT COUNT(*) > 0 FROM fulltextItems WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=${attachmentID})`,
`DELETE FROM fulltextItems WHERE itemID NOT IN (SELECT itemID FROM items WHERE itemTypeID=${attachmentID})`
],
// Invalid link mode -- set to imported url
[
@ -1532,8 +1884,8 @@ Zotero.Schema = new function(){
/*
* Create new DB schema
*/
function _initializeSchema(){
return Zotero.DB.executeTransaction(function* (conn) {
async function _initializeSchema() {
await Zotero.DB.executeTransaction(function* (conn) {
var userLibraryID = 1;
// Enable auto-vacuuming
@ -1550,7 +1902,9 @@ Zotero.Schema = new function(){
yield _getSchemaSQL('triggers').then(function (sql) {
return Zotero.DB.executeSQLFile(sql);
});
yield _updateCustomTables(true);
var schema = yield _readGlobalSchemaFromFile();
yield _updateGlobalSchema(schema);
yield _getSchemaSQLVersion('system').then(function (version) {
return _updateDBVersion('system', version);
@ -2528,6 +2882,21 @@ Zotero.Schema = new function(){
yield Zotero.DB.queryAsync("ALTER TABLE retractedItems ADD COLUMN flag INT DEFAULT 0");
}
else if (i == 106) {
yield _updateCompatibility(6);
yield Zotero.DB.queryAsync("DROP TRIGGER insert_date_field");
yield Zotero.DB.queryAsync("DROP TRIGGER update_date_field");
yield Zotero.DB.queryAsync("DROP TRIGGER fki_itemAttachments");
yield Zotero.DB.queryAsync("DROP TRIGGER fku_itemAttachments");
yield Zotero.DB.queryAsync("DROP TRIGGER fki_itemNotes");
yield Zotero.DB.queryAsync("DROP TRIGGER fku_itemNotes");
yield Zotero.DB.queryAsync("DROP TABLE transactionSets");
yield Zotero.DB.queryAsync("DROP TABLE transactions");
yield Zotero.DB.queryAsync("DROP TABLE transactionLog");
}
// If breaking compatibility or doing anything dangerous, clear minorUpdateFrom
}
@ -2593,7 +2962,7 @@ Zotero.Schema = new function(){
}
else {
let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
.createInstance(Components.interfaces.nsIFile);
try {
file.persistentDescriptor = path;
}

View file

@ -140,7 +140,12 @@ Zotero.Server.SocketListener = new function() {
var dataListener = new Zotero.Server.DataListener(iStream, oStream);
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
.createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(iStream, -1, -1, 0, 0, false);
try {
pump.init(iStream, 0, 0, false);
}
catch (e) {
pump.init(iStream, -1, -1, 0, 0, false);
}
pump.asyncRead(dataListener, null);
}

View file

@ -38,10 +38,10 @@ Zotero.Sync.Storage.Result.prototype.updateFromResults = function (results) {
}
Zotero.Sync.Storage.Result.prototype.toString = function () {
/*Zotero.Sync.Storage.Result.prototype.toString = function () {
var obj = {};
for (let prop of this._props) {
obj[prop] = this[prop] || false;
}
return JSON.stringify(obj, null, " ");
}
}*/

View file

@ -271,6 +271,6 @@ Zotero.Sync.Storage.StreamListener.prototype = {
_safeSpec: function (uri) {
return uri.scheme + '://' + uri.username + ':********@'
+ uri.hostPort + uri.path
+ uri.hostPort + uri.pathQueryRef
},
};

View file

@ -188,32 +188,23 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
throw new this.VerificationError("NO_URL");
}
url = scheme + '://' + url;
var dir = "zotero";
var username = this.username;
var password = this.password;
if (!username) {
throw new this.VerificationError("NO_USERNAME");
}
if (!password) {
throw new this.VerificationError("NO_PASSWORD");
}
var ios = Components.classes["@mozilla.org/network/io-service;1"].
getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(url, null, null);
uri.username = encodeURIComponent(username);
uri.password = encodeURIComponent(password);
if (!uri.spec.match(/\/$/)) {
uri.spec += "/";
}
this._parentURI = uri;
url = scheme + '://'
+ encodeURIComponent(username) + ':' + encodeURIComponent(password) + '@'
+ url
+ (url.endsWith('/') ? '' : '/');
var uri = uri.clone();
uri.spec += "zotero/";
this._rootURI = uri;
var io = Services.io;
this._parentURI = io.newURI(url, null, null);
this._rootURI = io.newURI(url + "zotero/", null, null);
},
@ -656,8 +647,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
if (req.status == 207) {
// Test if missing files return 404s
let missingFileURI = uri.clone();
missingFileURI.spec += "nonexistent.prop";
let missingFileURI = uri.mutate().setSpec(uri.spec + "nonexistent.prop").finalize();
try {
req = yield Zotero.HTTP.request(
"GET",
@ -681,8 +671,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
}
// Test if Zotero directory is writable
let testFileURI = uri.clone();
testFileURI.spec += "zotero-test-file.prop";
let testFileURI = uri.mutate().setSpec(uri.spec + "zotero-test-file.prop").finalize();
req = yield Zotero.HTTP.request("PUT", testFileURI, {
body: " ",
successCodes: [200, 201, 204],
@ -765,7 +754,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
createInstance(Components.interfaces.nsIPromptService);
if (err.url) {
var spec = err.url.scheme + '://' + err.url.hostPort + err.url.path;
var spec = err.url.scheme + '://' + err.url.hostPort + err.url.pathQueryRef;
}
var errorTitle, errorMsg;
@ -784,7 +773,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
case 403:
errorTitle = Zotero.getString('general.permissionDenied');
errorMsg = Zotero.getString('sync.storage.error.webdav.permissionDenied', err.channel.URI.path)
errorMsg = Zotero.getString('sync.storage.error.webdav.permissionDenied', err.channel.URI.pathQueryRef)
+ "\n\n" + Zotero.getString('sync.storage.error.checkFileSyncSettings');
break;
@ -851,7 +840,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
if (e.status == 403) {
errorTitle = Zotero.getString('general.permissionDenied');
let rootURI = this.rootURI;
let rootSpec = rootURI.scheme + '://' + rootURI.hostPort + rootURI.path
let rootSpec = rootURI.scheme + '://' + rootURI.hostPort + rootURI.pathQueryRef
errorMsg = Zotero.getString('sync.storage.error.permissionDeniedAtAddress')
+ "\n\n" + rootSpec + "\n\n"
+ Zotero.getString('sync.storage.error.checkFileSyncSettings');
@ -986,7 +975,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
Zotero.debug("Purging orphaned storage files");
var uri = this.rootURI;
var path = uri.path;
var path = uri.pathQueryRef;
var contentTypeXML = { "Content-Type": "text/xml; charset=utf-8" };
var xmlstr = "<propfind xmlns='DAV:'><prop>"
@ -1037,7 +1026,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
if (href.match(/^https?:\/\//)) {
let ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
href = ios.newURI(href, null, null).path;
href = ios.newURI(href, null, null).pathQueryRef;
}
let decodedHref = decodeURIComponent(href).normalize();
@ -1336,9 +1325,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
* @return {nsIURI} URI of file on storage server
*/
_getItemURI: function (item) {
var uri = this.rootURI;
uri.spec = uri.spec + item.key + '.zip';
return uri;
return this.rootURI.mutate().setSpec(this.rootURI.spec + item.key + '.zip').finalize();
},
@ -1350,9 +1337,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
* @return {nsIURI} URI of property file on storage server
*/
_getItemPropertyURI: function (item) {
var uri = this.rootURI;
uri.spec = uri.spec + item.key + '.prop';
return uri;
return this.rootURI.mutate().setSpec(this.rootURI.spec + item.key + '.prop').finalize();
},
@ -1366,11 +1351,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
if (!uri.spec.match(/\.zip$/)) {
return false;
}
var propURI = uri.clone();
propURI.QueryInterface(Components.interfaces.nsIURL);
propURI.fileName = uri.fileName.replace(/\.zip$/, '.prop');
propURI.QueryInterface(Components.interfaces.nsIURI);
return propURI;
return uri.mutate().setFilePath(uri.filePath.replace(/\.zip$/, '.prop')).finalize();
},
@ -1408,10 +1389,7 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
for (let i = 0 ; i < files.length; i++) {
let fileName = files[i];
funcs.push(Zotero.Promise.coroutine(function* () {
var deleteURI = this.rootURI.clone();
deleteURI.QueryInterface(Components.interfaces.nsIURL);
deleteURI.fileName = fileName;
deleteURI.QueryInterface(Components.interfaces.nsIURI);
var deleteURI = this.rootURI.mutate().setSpec(this.rootURI.spec + fileName).finalize();
try {
var req = yield Zotero.HTTP.request(
"DELETE",
@ -1442,6 +1420,8 @@ Zotero.Sync.Storage.Mode.WebDAV.prototype = {
// If an item file URI, get the property URI
var deletePropURI = this._getPropertyURIFromItemURI(deleteURI);
// Only nsIURL has fileName
deletePropURI.QueryInterface(Ci.nsIURL);
// If we already deleted the prop file, skip it
if (!deletePropURI || results.deleted.has(deletePropURI.fileName)) {

View file

@ -1244,7 +1244,7 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
// Update local object with saved data if necessary, as long as it hasn't
// changed locally since the upload
if (!changed) {
obj.fromJSON(current.data);
obj.fromJSON(current.data, { strict: true });
toSave.push(obj);
}
else {

View file

@ -381,9 +381,7 @@ Zotero.Sync.Data.Local = {
*/
_getAPIKeyLoginInfo: function () {
try {
var loginManager = Components.classes["@mozilla.org/login-manager;1"]
.getService(Components.interfaces.nsILoginManager);
var logins = loginManager.findLogins(
var logins = Services.logins.findLogins(
{},
this._loginManagerHost,
null,
@ -723,11 +721,10 @@ Zotero.Sync.Data.Local = {
var ObjectType = Zotero.Utilities.capitalize(objectType);
var libraryName = Zotero.Libraries.get(libraryID).name;
var knownErrors = [
'ZoteroUnknownTypeError',
'ZoteroUnknownFieldError',
var knownErrors = new Set([
'ZoteroInvalidDataError',
'ZoteroMissingObjectError'
];
]);
Zotero.debug("Processing " + json.length + " downloaded "
+ (json.length == 1 ? objectType : objectTypePlural)
@ -1029,8 +1026,11 @@ Zotero.Sync.Data.Local = {
}
}
catch (e) {
// This allows errors handled by syncRunner to know the library in question
e.libraryID = libraryID;
// Display nicer debug line for known errors
if (knownErrors.indexOf(e.name) != -1) {
if (knownErrors.has(e.name)) {
let desc = e.name
.replace(/^Zotero/, "")
// Convert "MissingObjectError" to "missing object error"
@ -1409,7 +1409,7 @@ Zotero.Sync.Data.Local = {
json = this._checkCacheJSON(json);
if (!options.skipData) {
obj.fromJSON(json.data);
obj.fromJSON(json.data, { strict: true });
}
if (obj.objectType == 'item' && obj.isImportedAttachment()) {
yield this._checkAttachmentForDownload(obj, json.data.mtime, options.isNewObject);

View file

@ -1263,6 +1263,23 @@ Zotero.Sync.Runner_Module = function (options = {}) {
}
}
}
// Show warning for unknown data that couldn't be saved
else if (e.name && e.name == 'ZoteroInvalidDataError') {
e.message = Zotero.getString(
'sync.error.invalidDataError',
[
Zotero.Libraries.get(e.libraryID).name,
Zotero.clientName
]
)
+ "\n\n"
+ Zotero.getString('sync.error.invalidDataError.otherData');
e.errorType = 'warning';
e.dialogButtonText = Zotero.getString('general.checkForUpdates');
e.dialogButtonCallback = () => {
Zotero.openCheckForUpdatesWindow();
};
}
});

View file

@ -313,16 +313,6 @@ Zotero.Translate.Sandbox = {
* @inner
*/
var safeTranslator = {};
safeTranslator.__exposedProps__ = {
"setSearch":"r",
"setDocument":"r",
"setHandler":"r",
"setString":"r",
"setTranslator":"r",
"getTranslators":"r",
"translate":"r",
"getTranslatorObject":"r"
};
safeTranslator.setSearch = function(arg) {
if(!Zotero.isBookmarklet) arg = JSON.parse(JSON.stringify(arg));
return translation.setSearch(arg);

Some files were not shown because too many files have changed in this diff Show more